最近发现接口项目的服务器上,fpm进程的CPU使用率忽高忽低。根据以前的项目经验,类似情况最后都发现接口里存在严重性能问题的代码,所以我们立马动手分析一下吧。
症状
服务器上top,不管是高峰期还是低峰期,CPU使用率忽高忽低。

排查
首先查看了php的错误日志和慢日志,发现没有任何记录。
回到top查看系统状态,按数字键1,每个核的负载情况如下图。

发现用户空间占用CPU百分比比较高(us),安装ltrace开始跟踪FPM进程的库函数调用。如果是内核空间占用CPU百分比比较高(sy),则使用strace。
ltrace -c -p $(pgrep -n php-fpm)
如下图结果,memcpy调用有点夸张,这时还只是认为因部分接口里列表数据比较大,每个数据的字段又循环赋值很多次导致的。

还有没有其他方面的原因?!使用ltrace查看php的一个进程的系统调用,并输出到/tmp/output.txt。
ltrace -o tmp/output.txt -T -p $(pgrep -n php-fpm)
下载output.txt文件到本地分析也没有发现其他异常的库函数调用。
继续分析,接下来准备查看一下项目里的PHP代码性能。到一台线上服务器安装xhprof,修改入口文件。
index.php添加以下代码。
if (function_exists('xhprof_enable')) {
// 如果使用xhprof_enable(XHPROF_FLAGS_MEMORY | XHPROF_FLAGS_CPU);报502错误,使用下面的设置 xhprof_enable(XHPROF_FLAGS_NO_BUILTINS | XHPROF_FLAGS_CPU | XHPROF_FLAGS_MEMORY); }
register_shutdown_function里添加以下代码。
if (function_exists('xhprof_enable')) {
$data = xhprof_disable();
include_once "/data/src/xhprof-0.9.4/xhprof_lib/utils/xhprof_lib.php";
include_once "/data/src/xhprof-0.9.4/xhprof_lib/utils/xhprof_runs.php";
$objXhprofRun = new XHProfRuns_Default();
$objXhprofRun->save_run($data, "xhprof"); }
两分钟后关闭xhprof扩展,下载所有收集的数据,在本地使用xhprof的图表模式查看收集的数据。

完整调用图如下。可以看到,大部分的接口里(但并不是所有),ApiData::encrypt比较耗时。

结论
到此,终于。。。大概。。。找到了问题。在我们的接口系统里,数据加密方法会对接口返回的数据每个字节进行加密操作。这也解释了为什么有时ltrace记录里memcpy操作次数会那么夸张,而且并不是所有接口都是encrypt最耗时。因为只有接口返回的数据比较大的时候才会出现。
继续验证,写了个PHP脚本测试加密算法,加密20KB的数据,耗时6ms,而使用C语言,0.1ms不到。。。
那么,在不能改变算法的情况下,解决办法是用C语言写个PHP扩展?




