背景
- 流量突增后,cpu负载飙升100%
- 采用 nginx 负载均衡,最小值5台web机(8核16G)
- 架构:经典 lnmp+laravel
- 当前fpm采用static模式,进程数设置为200(200*30=6000 约等于6G)
初步摸排情况(由于缺乏数据支持,所以只能从侧面摸排)
- 建议开启 opcache 支持
- 建议开启
laravel artisan cache
- 移除composer dev依赖:
composer install --prefer-dist --no-dev -o
- 注释
app/Http/Kernel.php
中未使用的中间件 - db操作如果只需要一条数据,建议添加
->limit(1)
- 建议将热点数据放到
cache
- 如果内存没有其他用,建议适当增加
fpm
进程数(按当个进程30m算,分10G出来,大约350个);为防止内存泄露,建议设置重启请求数(每个fpm处理多少请求后重启) - 生产环境
app.debug
应始终为false
,防止敏感数据泄露 - 建议使用
config(filename.key.val)
来获取配置数据 - Redis 推荐使用laravel 中集成
Illuminate\Support\Facades\Redis::class
,驱动选用phpredis
扩展,而不是predis
opcache配置优化
开启opcache后,每次发布版本建议 reload fpm
否则会在cache刷新间隙出现500错误(部分文件cache未能更新)
//推荐配置
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=4000
opcache.revalidate_freq=60
opcache.fast_shutdown=1
opcache.enable_cli=0 //cli模式不建议开启
opcache.save_comments=0 //注意,如果使用了注释路由 该项必须开启!!!
//详解
; opcache的开关,关闭时代码不再优化.
opcache.enable=1
; cli模式
opcache.enable_cli=1
; OPcache的共享内存大小,以兆字节为单位。总共能够存储多少预编译的PHP代码(单位:MB)
; 推荐128
opcache.memory_consumption=64
; 用来存储临时字符串的内存大小,以兆字节为单位.
; 推荐8
opcache.interned_strings_buffer=4
; 最大缓存的文件数目200到100000之间.
; 推荐4000
opcache.max_accelerated_files=2000
; 内存"浪费"达到此值对应的百分比,就会发起一个重启调度.
opcache.max_wasted_percentage=5
; 开启这条指令, Zend Optimizer + 会自动将当前工作目录的名字追加到脚本键上,以此消除同名文件间的键值命名冲突.关闭这条指令会提升性能,但是会对已存在的应用造成破坏.
opcache.use_cwd=0
; 开启文件时间戳验证
opcache.validate_timestamps=1
; 检查脚本时间戳是否有更新的周期,以秒为单位。设置为0会导致针对每个请求,OPcache都会检查脚本更新.
; 推荐60
opcache.revalidate_freq=2
; 允许或禁止在include_path中进行文件搜索的优化.
opcache.revalidate_path=0
; 如果禁用,脚本文件中的注释内容将不会被包含到操作码缓存文件,这样可以有效减小优化后的文件体积,禁用此配置指令可能会导致一些依赖注释或注解的应用或框架无法正常工作,比如:Doctrine,Zend Framework2等.
; 推荐0
opcache.save_comments=1
; 如果禁用,则即使文件中包含注释,也不会加载这些注释内容。本选项可以和opcache.save_comments一起使用,以实现按需加载注释内容.
opcache.load_comments=1
; 打开快速关闭,打开这个在PHP Request Shutdown的时候会收内存的速度会提高.
; 推荐1
opcache.fast_shutdown=1
; 允许覆盖文件存在(file_exists等)的优化特性.
opcache.enable_file_override=0
; 定义启动多少个优化过程.
opcache.optimization_level=0xffffffff
; 启用此Hack可以暂时性的解决"can’t redeclare class"错误.
opcache.inherited_hack=1
; 启用此Hack可以暂时性的解决"can’t redeclare class"错误.
;opcache.dups_fix=0
; 通过文件大小屏除大文件的缓存,默认情况下所有的文件都会被缓存.
;opcache.max_file_size=0
; 每N次请求检查一次缓存校验.默认值0表示检查被禁用了,由于计算校验值有损性能,这个指令应当紧紧在开发调试的时候开启.
;opcache.consistency_checks=0
; 从缓存不被访问后,等待多久后(单位为秒)调度重启.
;opcache.force_restart_timeout=180
; 日志记录level,默认只有fatal error和error.
;opcache.error_log=
; 将错误信息写入到服务器(Apache等)日志
;opcache.log_verbosity_level=1
; 内存共享的首选后台.留空则是让系统选择.
;opcache.preferred_memory_model=
; 运行php脚本时保护共享内存防止意外的写入,只对debug时有用.
;opcache.protect_memory=0
初步测试数据
- 开启opcache对比(生产机器 web1启用了opcache)
- 启用laravel cache,未启用opcache 对比 (本地环境 pm=static 20child);使用
wrk
压测工具,20个线程200个连接压测20秒
- 启用opcache情况
xhprof 分析(针对cache情况,请求/api/get_vivo_token<注:屏蔽vivo请求>)
测试分支:dev-xhprof
未开启cache
开启cache
指标含义:
- Calls:方法被调用的次数。
- Calls%:方法调用次数在同级方法总数调用次数中所占的百分比。
- Incl.Wall Time (microsec):方法执行花费的时间,包括子方法的执行时间。(单位:微秒)
- IWall%:方法执行花费的时间百分比。
- Excl. Wall Time (microsec):方法本身执行花费的时间,不包括子方法的执行时间。(单位:微秒)
- EWall%:方法本身执行花费的时间百分比。
- Incl. CPU (microsecs):方法执行花费的 CPU 时间,包括子方法的执行时间。(单位:微秒)
- ICpu%:方法执行花费的 CPU 时间百分比。
- Excl. CPU (microsec):方法本身执行花费的 CPU 时间,不包括子方法的执行时间。(单位:微秒)
- ECPU%:方法本身执行花费的 CPU 时间百分比。
- Incl.MemUse (bytes):方法执行占用的内存,包括子方法执行占用的内存。(单位:字节)
- IMemUse%:方法执行占用的内存百分比。
- Excl.MemUse (bytes):方法本身执行占用的内存,不包括子方法执行占用的内存。(单位:字节)
- EMemUse%:方法本身执行占用的内存百分比。
- Incl.PeakMemUse (bytes):Incl.MemUse 峰值。(单位:字节)
- IPeakMemUse%:Incl.MemUse 峰值百分比。
- Excl.PeakMemUse (bytes):Excl.MemUse 峰值。单位:(字节)
- EPeakMemUse%:Excl.MemUse 峰值百分比。
问题排查结果
启用config配置情况下,程序加载时不会写入env,所以后续使用env读取的配置,一律为null;
jwt鉴权中间件使用了env获取配置,故链接Redis失败。