结论
先说优化要点:
- Nginx
work_processes
设置为CPU核心数 - Nginx 设置
worker_cpu_affinity
CPU亲和性绑定 Nginx 进程 - Nginx
worker_rlimit_nofile
设置为较大的数,可应对较大的并发量,这里设置为 102400 - Nginx
worker_connections
设置为较大的数,以应对高并发,这里设置为 102400 - Nginx 使用
epoll
提高性能 - Nginx 连接后端
php-fpm
使用unixsocket
方式,比使用tcp
方式性能好太多(tcp
方式在高并发下稳定较unixsocket
方式好,但在高并发下性能太差) - Nginx 使用
upstream
连接到后端的多个php-fpm
实例中,每个php-fpm
实例监听一个unixsocket
,可提高稳定性,建议php-fpm
实例数与CPU核心数相同(使用 Apache ab 进行压测,在高并发下错误为0) - php-fpm 监听
unixsocket
文件放置在/dev/shm/
下,使用内存文件系统,提高 io 性能 - php-fpm
pm.max_childred
不宜设置的过大,CPU核心数的 1~2 倍即可,pm
设置为pm = dynamic
,pm.start_servers
设置为pm.max_childred
的一般,pm.min_spare_servers
设置为pm.max_childred
的 1/4,pm.max_spare_servers
设置与pm.max_childred
相同 - php-fpm
pm.max_requests
设置为一个较大的值,这里设置为 200000,可根据Apache ab
测试中查看内存的占用率查看是否有内存泄漏情况,如果存在内存泄漏可调小该值 - php-fpm 打开
slowlog
,将request_slowlog_timeout
设置为 1,即在响应时间超过 1s 时就记入slowlog
,一个响应迅速的网站应当在毫秒级别响应用户请求 - php-fpm 设置
rlimit_files
为一个较大值,这里设置为 102400 - php 打开
opcache
可显著提高性能,PHP 文件包含会导致性能降低,打开opcache
能够显著提升性能 - php 使用
redis
作为 session 存储,PHP 默认使用文件作为 session 存储,性能较差(此处可在打开了 php-fpmslowlog
时进行高并发压测,可以看到session_start
函数调用影响了性能) - redis 使用多实例,redis 为单线程,不能利用多CPU核心,使用多个实例能够提高性能(此处实例数为 10 个)
配置
配置文件(服务器配置为 16核32G内存):
nginx.conf:
|
|
vhosts/default.conf
|
|
php-fpm.conf
配置多个 php-fpm 实例,配置文件中的 pid,error_log,listen,slowlog 都要相应修改
|
|
php-fpm 关键配置是启动多个实例,监听多个 unixsocket
文件。
php.ini(关键配置)
|
|
相应的,redis-server 应启动多个实例
调优经过
场景是一个推广的H5小游戏,游戏结束后可根据名次获得奖品。业务逻辑较为简单,但因需要在各大社交平台上推广,对性能要求较高,要求
- 可承受并发在 1500 左右
- 每秒请求数 1500 以上
- 错误数极少(个位数)
- 90% 请求在 200ms 内完成
- 平均响应时间 < 200 ms。
以上也是压力测试需要关注的几个点,压测时要同时关注,缺一不可。
首先根据网上搜索到的一些文章对 nginx 进行了配置:工作进程数、CPU 亲和性、连接数、打开文件限制等。对 php-fpm 使用了 /dev/shm 下的 unixsocket 进行监听,打开了两个 php-fpm 实例以防止高并发下的不稳定。此时使用 Apache ab 进行压测,发现并发能够完成测试,但错误请求 Failed requests
过多,达到 40% 左右。
想到 php-fpm 在高并发下不稳定,将 php-fpm 切换到监听本机 127.0.0.1 下的 9000 和 9001 端口进行测试,发现错误为 0,但并发数达不到要求。以上两种方式测试同时监控服务器 CPU、内存使用情况,发现在使用 unixsocket 进行通信时 CPU 占用在 30%,而使用 tcp 进行通信 CPU 会卡在 100% 一段时间。
最终开启 16 个 php-fpm 实例,能够在错误数为 0 的情况下完成测试,此时每秒处理请求数达不到要求。突然想到 php 在使用文件作为 session 存储时会在请求开始时锁定文件,请求结束后再释放锁。于是将 php 改为 session handler 改为 redis,并开启了 php-fpm 的 slowlog 设置为 1s。
再次压测发现性能出现在 session_start() 函数调用,于是将 redis-server 也开启了多个实例,压测发现 slowlog 不再出现 session_start() 调用。
最后将 php 的 opcache 打开,又提升了不少性能。
结果
PHP 程序逻辑为:
打开 session,包含文件,连接数据库,输出内容。
|
|
Apache ab 测试 1500 并发 15000 个请求,90% 的请求在 200ms 内完成,平均响应时间 190ms,每秒钟处理请求数在 7500,错误请求为0.
|
|