Tideways与Xhgui实现PHP性能分析实践

PolyQY 2018年11月10日

当我们发现生产环境的某个接口执行时间特别长时应该怎么做?直接登录线上机器单步调试?打大量的log然后分析?本文部署的非侵入式php性能框架可以帮助你迅速定位问题。

profiling1.png

对于生产环境下php响应缓慢的问题,一般我们可以把分析流程拆分为如下几步操作:

1.分析开发环境下执行是否会慢;

如果是代码问题,在开发环境下就能检测出来;

2.分析测试机器执行是否会慢;

如果是数据库或者第三方扩展问题,在测试阶段就能检查出来。

3.从生产环境下线一台机器,分析代码执行慢的原因;

如果是机器的问题,在这一步才能检查出来。

1,2,3步骤都需要去分析代码,看哪部分执行时间长。如果迫不得已在生产环境下调试代码,不但要耗费大量时间还可能导致用户流失。

大多时候我们会使用第三方的分析工具xhprof来快速发现问题。
xhprof是由facebook开发的一款非侵入式php性能分析框架。但是官方已经不在维护,也没有跟进php7,只能在php5上运行,tideways则是一直由商业公司在xhprof停止维护后fork出的版本,开源在Github上(托管服务需付费,自行搭建免费),支持PHP7。
显而易见,tideways是我们的选择。

tideways扩展能把每条请求生成详细的执行日志,通过对日志做简单的分析就能看到程序哪部分耗时最长,这里可以使用xhprof的UI程序(xhprof生成的日志和tideways生成的日志格式通用),交互虽然不大友好但是够用了。
当然,也可按照本文介绍使用xhgui来进行可视化的分析。

先决条件

本文部署所用环境为Ubuntu 18.04 LTS,其它环境请自行研究类似的配置。

安装Tideways

由于部署环境php采用编译安装,故Tideways也采用编译安装。
如果采用apt安装,也可以直接使用apt安装php-tideways。
还可以使用pecl安装,具体方法不详细讲了。

git clone https://github.com/tideways/php-xhprof-extension 
cd php-xhprof-extension
phpize
./configure
make
make install //sudo make install if not root

配置php.ini,加入

extension=tideways_xhprof.so
tideways.auto_prepend_library=0 //optional

注意最好加上后面那行,因为如果不加,tideways会调试所有经过php-fpm的请求,包括xhgui的请求,而这些请求不是我们想看的。

安装xhgui

其实xhgui就是一个网站程序,建过站的同学都知道大概怎么回事,只不过数据库换用了mongodb而已。

安装mongodb

因为xhgui需要储存由tideways传入的信息,所以需要安装mongodb。
另有人维护了一个mysql版本的xhgui,如果不喜欢mongodb可以换用。
部署环境使用了oneinstack安装lnmp栈,所以也使用oneinstack安装mongodb

wget http://mirrors.linuxeye.com/oneinstack.tar.gz
tar zxf oneinstack.tar.gz
cd oneinstack
./install.sh // sudo install.sh if not root

按照提示交互式安装mongodb就行了。
如果你不想这么麻烦或者非生产环境部署,也可以直接使用apt安装

apt install mongodb

如果没有在php中加入mongodb扩展,则需继续安装mongodb扩展。

git clone https://github.com/mongodb/mongo-php-driver.git
cd mongo-php-driver
git submodule update --init
phpize
./configure
make
make install // add sudo if not root

同样地,在php.ini中添加

extension=mongodb.so

官方基于性能建议我们为mongodb添加索引

$ mongo
> use xhprof
> db.results.ensureIndex( { 'meta.SERVER.REQUEST_TIME' : -1 } )
> db.results.ensureIndex( { 'profile.main().wt' : -1 } )
> db.results.ensureIndex( { 'profile.main().mu' : -1 } )
> db.results.ensureIndex( { 'profile.main().cpu' : -1 } )
> db.results.ensureIndex( { 'meta.url' : 1 } )
> db.results.ensureIndex( { 'meta.simple_url' : 1 } )

至此mongodb算是安装完毕。
注意生产环境安装还需要为mongodb配置访问权限,此处不详细讨论了。

下载xhgui

git clone https://github.com/perftools/xhgui

代码下载完以后上传到网站目录,具体操作因环境而异,若不熟悉请先熟悉lnmp架构。
上传完后执行

php <wwwroot>/install.php

配置Nginx

假设我们需要调试的网站位于dev.fqdn
而xhgui位于xhgui.fqdn

则需要在dev.fqdn配置文件的location段加入

fastcgi_param PHP_VALUE "auto_prepend_file=/<wwwroot of xhfui.fqdn>/xhgui/external/header.php";

此配置的作用是在dev.fqdn下的每个php经过fastcgi的时候自动在开头加入这个xhgui/external/header.php
而这个文件的作用是运行你的代码前调用tideways开启调试
你的代码运行完毕后结束调试并把结果传入mongodb等待查看。

xhgui.fqdn配置则是正常php网站的配置,不赘述了。
注意需有这一行

location / {
    try_files $uri $uri/ /index.php?$args;
}

配置tideways

tideways的配置文件位于/config/config.php
安装时这个文件不存在,故需要把默认配置复制到这个文件

cp <wwwroot>/config/config.default.php <wwwroot>/config/config.php

除了mongodb链接配置(一般无需调整)
主要关注这一个回调函数

// In config/config.php
return array(
    // Other config
    'profiler.enable' => function() {
        return rand(1, 100) === 42;
    }
);

默认配置的此函数仅仅做了节流,截取1%的流量进行分析。这是基于生产环境的性能考虑,如果我们仅仅是在开发环境中使用,就不需要管这么多了。
例如

// In config/config.php
return array(
    // Other config
    'profiler.enable' => function() {
        return true;
    }
);

这个配置就会把所有流量全部截取分析。
令人哭笑不得的是,这所有流量包括了xhgui.fqdn的流量,这显然不是我们想要分析的。
还有的时候我们也不想看到dev.fqdn中管理界面的流量,这就需要一些进阶配置了。
例如

'profiler.enable' => function() {
        $url = $_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI'];
        if (strpos($url, 'dev.fqdn/admin') === 0) {
            return false;
        } elseif (strpos($url, 'dev.kucloud.win') === 0) {
            return true;
        }
        return false;
    },

这样一来就只会看到我们希望分析的请求了。

参考:
phperzh
Xhgui Readme

expand_less