PHP服务器降低10%-15%CPU资源占用

业务请求稳步上升, PHP接口服务器资源又吃紧了, 在巡检时发现了一个有趣的现象引起了我的注意. 其中一台的CPU资源占用相对其他相同配置服务器小10-15%左右, 预计有比较可观的深挖价值.

(标题先卖个关子, 通过这个关子引出下面的3种解决问题的思路), 下面请看3种排查思路.

 

首先先看"错误"示范:

 

既然负载表现有差别, 第一思路就是该"大家来找茬"了, 在这个思路下分别排查了:

1.负载均衡分配规则.

2.负载均衡中配置权重相同, 业务请求量日志也大致相同.

3.服务器内核版本和参数.

4.PHP的编译参数, gcc版本.

5.PHP报错日志.

查看系统的message日志,其中中会有一些redis插件相关的段错误. 由于以前处理过redis插件的内存泄露问题,所以重新编译最新版本的redis.so库,检查没有了段错误.但是对性能并没有任何提升

6.服务器防火墙配置.

性能好的服务器防火墙取消了TCP请求跟踪,  其他服务器上也取消TCP请求跟踪后后性能差异也没有明显提高.

7.外部请求速度差异.

在链路监控日志中对比, 2台服务器请求数据库/缓存等接口速度无差异. 网络消耗也无差异.

8.系统内核调用差异.类调用差异.

通过strace追踪系统内核调用差异,和ltrace跟踪so文件时间调用差异. 内核调用差异.这种差距可以对比出,但是分析这种差异太过复杂,不直观.

走到这里, 这条思路基本上是走到死胡同了.虽然发现了一些可以优化的地方, 但是并没有找到差异真正原因. 所以需要转变思路, 通过判断推理而不是假设验证来定位.

 

下面请看"能解决问题"的示范:

 

1.进程分组汇总后,观察某类进程占用资源的对比.

watch -d -n 1 "ps aux | awk '{S[\$11]+=\$3} END { for (i in S) print  S[i], i}' | sort -nr"

发现确实根本原因还是在php-fpm的差异上,与其他的应用无关.

2.内核态CPU差距不大,用户态CPU有15%左右的差距.load值会有1-2倍的差异.这个比例就比较蹊跷了.通过vmstat -n 1发现,CPU等待队列数基本保持在3-5附近,偶尔会突然增高到20-30附近,并迅速下降,这种情况会导致CPU的用户态差距不大,而系统load差距非常大.平时正常,但是波峰过大.

3.这种情况需要查找CPU任务堆积的分配, 通过观察CPU时钟来推断产生问题的原因.

通过perf top对比发现.性能差的服务器中会有大量的mutex_spin_on_owner和_spin_unlock_irqrestore时间占用.

关于这个的解释:

It's bad reporting by perf, not cycles consumed by _spin_unlock_irqrestore.

When IRQs are disabled, perf's interrupts are not processed. Instead, they're processed when interrupts are re-enabled. When perf's interrupt handler looks at the instruction pointer, to see what code was running, it finds the function that enabled interrupts - quite often it's _spin_unlock_irqrestore.

So all you know is that the cycles were consumed by code that had interrupts disabled, and enabled them using _spin_unlock_irqrestore.

If you can get perf to use NMI (non maskable interrupt), it could solve this problem.

I know that it can be done with oprofile (perf's predecessor) by changing the makefile, but don't know about perf.

大意是:这个问题是在处理多核CPU均衡相关的内容,但这个问题只是表现症状,并不是根本原因.这个内容和CPU抢占,自旋锁等锁,和多核CPU均衡相关的内容.进一步分析发现这些内容是由php-fpm发起的.

对比性能较好的服务器:

 

性能差的API服务器中libgomp.so.1.0.0 gomp_barrier_wait_end的调用占据了大量的CPU时钟.

调查发现openmp是和多核CPU并行相关的.软件编译的过程中,可以利用这个动态库,达到充分利用多核CPU的目的. 关闭openmp的多核功能后, 性能得到提升, 问题得到解决.

以上是调查过程中的思路和方法, 问题虽然得到解决, 但是还是有一些推理盲点存在, 仍然有些运气成分, 不能让人完全信服.

 

下面请看"正确"的示范:

思路是通过查找出这个动态库的上游调用,来找到有问题的点.通过perf工具查出,这个库的最上游调用时php-fpm.(果然,最终还回到了php-fpm).具体是php-fpm的哪个步骤出了问题需要进一步推断.

1: 查找任意一个php-fpm的文件调用.

lsof -p `ps aux | grep "[p]hp-fpm: pool"| head -n 1 | awk '{print $2}'`

2: 查看php-fpm的动态库调用树.比较取巧的方式是从文件调用中查到so文件,只需要查下一级的ldd调用即可.

lsof -p `ps aux | grep "[p]hp-fpm: pool"| head -n 1 | awk '{print $2}'`

| awk '{print $NF}'| sort |uniq | grep so | xargs ldd

搜索这些调用树:

/usr/lib64/libMagickCore.so.5.0.0

/usr/lib64/libMagickWand.so.5.0.0

/usr/local/php7/lib/php/extensions/debug-non-zts-20151012/imagick.so

只有这几个so用到了那个libgomp.so.1

rpm -qf /usr/lib64/libMagickWand.so.5.0.0

ImageMagick-6.7.2.7-4.el6_7.x86_64

到此为止,真正的问题找到了.就是ImageMagick这个文件产生了CPU多核调用,从而导致的出现频繁的波峰.

然后调查这个软件和多核CPU相关的部分:

identify -list Configure

返回的结果中:

FEATURES      OpenMP

发现默认是启用了OpenMP.

性能好的服务器当时为了修复ImageMagick的一个软件安全隐患, 除了有yum安装的多核版本, 还有一个关闭掉多核功能的版本, 编译参数中有一项CONFIGURE      ./configure  '--disable-openmp'.关闭多核功能. imagic.so编译的时候使用了无多核版本的库文件.

所以,问题就非常明了了. 在有和图片处理相关的请求中,理论上这个请求能占用所有的CPU资源.这会导致后续的PHP请求堆积,有个比较大的高峰.

开启多核功能在有些场景下使用,但作为PHP的库时,并不一定是最佳实践.

 

解决方法:

1.重新编译其余服务器的ImageMagick软件,关闭多核心功能.并重新编译so解决.

2.在PHP调用的时候手动制定最多使用1颗CPU.但是这种方式需要修改的PHP文件调用可能比较多,所以并不采用.

最终结果:重新编译后,perf top观察没有多核CPU相关/锁相关CPU时钟占用.系统性能也区域稳定,理论上会多支持20%-30%左右的请求量..

关于这次调查中总结的非技术问题.下篇再接着总结吧.

--------------------

参考文章:

https://www.google.com/#q=linux++disable+OpenMP+

https://www.google.com/#q=gomp_barrier_wait_end

https://www.google.com/#q=gomp_barrier_wait_end+php

http://stackoverflow.com/questions/14703328/spin-unlock-irqrestore-has-very-high-sampling-rate-in-my-kvm-why

https://www.google.com/#q=spin_unlock_irqrestore+high

https://www.google.com/#q=php+7++imagemagick+libgomp

ImageMagick这个插件

http://stackoverflow.com/questions/14703328/spin-unlock-irqrestore-has-very-high-sampling-rate-in-my-kvm-why

http://stackoverflow.com/questions/7847900/disable-openmp-in-nice-way

http://stackoverflow.com/questions/1357604/turn-off-openmp

http://stackoverflow.com/questions/5697824/openmp-gcc-gomp-wasteful-barrier

http://stackoverflow.com/questions/39116329/enable-disable-openmp-locally-at-runtime/39119009

http://stackoverflow.com/questions/1357604/turn-off-openmp

http://boomshadow.net/tech/installs/installing-imagemagick-php-imagick/

http://www.daniloaz.com/en/high-cpu-load-when-converting-images-with-imagemagick/

发表回复

您的电子邮箱地址不会被公开。