Laravel 令人头疼的性能问题
昨天上午,同事在测试一个知识 PK 的小程序,在 2~3 同时在线的情况下,服务器 CPU 居然达到了 60%,触发了阿里云钉钉报警。。。
一个非常简单的 API 接口,虽然写的逻辑确实比较啰嗦,但是一秒钟不超过 5 个请求的情况下,Laravel 硬是把 CPU 彪到了 60%。
这个性能确实让人无法接受,简直是玩具,根本不满足上线的基本要求。
确认是接口逻辑问题还是 Laravel 的问题
为了确认是接口的逻辑问题,还是 Laravel 的问题。我们做了一些简单的对比测试。
测试的对象是两个不同的 Laravel 项目下的相同功能的 API,例如,这里测试的是一个用来返回服务端版本号的接口。逻辑完全相同。
sudo apt install apache2-utils
time ab -c 10 -n 100 ‘https://something1.com/api/get_version’
time ab -c 10 -n 100 ‘https://something2.com/api/get_version’
结果令人意外,两个不同项目的同一个接口,性能差异巨大。
- A 项目为一个简单的 Laravel 项目,CPU 一直在 50% 左右。
- B 项目为出问题的 PK Laravel 项目,CPU 持续 100%。
所以,问题应该不是接口的逻辑问题,而是 Laravel 的性能问题。
鸡肋的 route cache 及 config cache
php artisan route:cache
php artisan config:cache
按照网上的推荐,使用了 laravel 的 cache 机制,CPU 的问题并没有得到解决。可以说毫无效果。
一些尝试
- 尝试1,将 route 数量精简,从 300 多压缩到 200 多,CPU 问题依旧。
- 尝试2,将与后台 Admin 管理无关的接口,拆分出来,放到独立的 Controller 中。已规避 php class 加载时,加载多余的内容。
然后,效果也都不明显。
Laravel 作者推荐的 opcache
Laravel 作者似乎对网上抨击 Laravel 的渣性能问题有非常大的不满。所以,他亲自做了一个测评,并做了如下优化配置:
- enable opcache
- config:cache
- 禁掉 app/Http/Kernel.php 的 session middleware
原文在这里 https://medium.com/@taylorotwell/benchmarking-laravel-symfony-zend-2c01c2b270f8
结果居然是 Laravel 在与 Zend, Symfony 的对决中胜出。。。
当然,对比的结果我不太关心,我关心是,laravel 居然能跑出 600 req/s 的成绩。。。
于是,我决定尝试一下 opcache
启用 opcache
vim /etc/php/7.0/fpm/php.ini
opcache.enable=1
opcache.memory_consumption=512
opcache.interned_strings_buffer=64
opcache.max_accelerated_files=20000
opcache.validate_timestamps=0
opcache.save_comments=1
opcache.fast_shutdown=1
修改之后,重启 fpm 服务
service php7.0-fpm reload
重新测试,发现并发量提升了 10 倍后,CPU 稳稳的控制在了 50%。。。
性能提升惊人。
opcache 的原理
首先需要了解的是 Laravel 为何会导致 CPU 飙高。
PHP 代码的执行过程是:
- Nginx 等 web server 收到 request 请求具体的 PHP 文件
- Zend引擎读取.php文件
- Lexicon sacn:扫描其词典和表达式
- Parse:解析文件
- 创建要执行的计算机代码(称为Opcode)
- 返回
所以,问题就很明显了吧,从第三步到第五步,都是耗费 CPU 的操作。而且每个请求都要重复一遍。而这些操作对于静态语言来说本来应该是在编译时该执行一次的操作。但是对于 PHP 就需要重复这个流程。
而 opcache 所做的事情就是将 3 ~ 5 步的结果进行缓存,如果 php 文件不发生更改,那么就不重复这3步操作,直接读取内存中的缓存。
那么,显然 CPU 使用率就必然会降下来。
opcache 需要注意的地方
每次更新了代码,需要重启 fpm 服务,以更新 cache。
持续的优化策略
即使开了 opcache,在业务的并发请求突增的情况下,Laravel 自然还是抗不住。所以,一种可行的优化策略是
- 将后台管理 admin 继续使用 laravel 维护,因为请求量不高,而且可控
- 将大并发量的接口使用 java,golang 重写,因为很多高并发的接口都是简单的 sql 查询,用什么语言实现都很快,但是性能上还是差距巨大。毕竟 Laravel 这种框架在一个请求中加载了几十个无用的类,性能损耗巨大。
参考
- Laravel 作者的配置 https://medium.com/@taylorotwell/benchmarking-laravel-symfony-zend-2c01c2b270f8
- (强烈推荐阅读)Opcode是啥以及如何使用好 Opcache https://f/phper/note/1016714
- 详细的每步优化的对比数据 https://www.jianshu.com/p/b7c12a824044
微信关注我哦 👍
我是来自山东烟台的一名开发者,有感兴趣的话题,或者软件开发需求,欢迎加微信 zhongwei 聊聊, 查看更多联系方式
谈笑风生
测试 (来自: 中国 重庆 重庆 电信) 2年前