求打赏
写文章不易!如果你觉得文章有价值,动动手指【打赏】一个吧!
简介
最近在公司使用EasySwoole开发一个项目, 发现一些性能问题, 想安装个PHP性能调试工具监控调试一下,看看性能差在哪里。
找到了一个Facebook开源的PHP性能分析工具Xhprof。Xhprof可以报告函数级别的请求次数和各种指标,包括阻塞时间,CPU时间和内存使用情况。但是Facebook官方已经很久不更新,官方源已经显示This package is abandoned and no longer maintained(此包已废弃,不再维护),Xhprof已不支持PHP7.x以上版本。而且发现XHProf与Swoole也不兼容。
其实Swoole官方提供了一个工具:Swoole Tracker,但是这个不开源,是收费的。根据官方介绍,其实Swoole Tracker也集成了Facebook的Xhprof工具。这里要批一下国内的开源社区,发现大部分国内的开源项目都是部分开源,部分收费。更可恶的是明明集成了人家的开源项目,居然还要用于商用。
快要绝望之际,突然发现了另一个开源项目Tideways,兼容PHP7.X和Swoole,Tideways由商业公司在维护,虽然这个项目也有收费版和免费版,但是免费版基本够用。另外,我们还需结合免费的Xhprof的UI程序Xhgui。
PS: 由于Xhgui官方版已经很久不更新,只支持PHP5.X,也是不支持PHP7.X,而且很多符号和单位都不适合中国用户。这里使用国人维护的一个汉化的版本,并且坚持在更新。
搭建平台
安装tideways扩展
git clone https://github.com/tideways/php-xhprof-extension.git
cd php-profiler-extension
phpize
./configure --with-php-config=/usr/local/php7/bin/php-config
make && make install
编译完成后在php.ini中加入
extension=tideways_xhprof.so
查看是否安装成功
php --ri tideways_xhprof
由于Xhgui使用了mongodb作为数据库存储数据,所以需要安装Mongodb和PHP mongodb扩展
安装mongodb扩展
访问https://pecl.php.net/package/mongodb下载对应PHP版本的拓展,我这里是PHP7,所以下载最新版就可以了。
注意:由于xhgui要求MongoDB扩展要1.3.0以上,MongoDb要求2.2.0以上。
MongoDB Extension MongoDB PHP driver. XHGui requires verison 1.3.0 or later.
MongoDB MongoDB Itself. XHGui requires version 2.2.0 or later.
编译安装
wget https://pecl.php.net/get/mongodb-1.6.1.tgz
tar -zxvf mongodb-1.6.1.tgz
cd mongodb-1.6.1
phpize
./configure --with-php-config=/usr/local/php7/bin/php-config
make && make install
编译完成后在php.ini中加入
extension=mongodb.so
查看是否安装成功
安装Mongodb
wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel62-4.0.1.tgz
mv mongodb-linux-x86_64-rhel62-4.0.1 /usr/local/mongodb
创建数据库存放目录和日志存放文件
mkdir -p /data/mongo/data
touch /data/mongo/dblogs
启动MongoDB
/usr/local/mongodb/bin/mongod --dbpath=/data/mongo/data --logpath=/data/mongo/dblogs --logappend --fork
设置开机自启
echo /usr/local/mongodb/bin/mongod --dbpath=/data/mongo/data --logpath=/data/mongo/dblogs--logappend --fork >>/etc/rc.local
查看是否启动
重点来了
安装Xhgui
安装Xhgui需要首先安装Composer包管理工具,这里不详术,可以我的另一篇博文:Linux下安装Composer
git clone https://github.com/laynefyc/xhgui-branch.git
cd xhgui-branch
php install.php
等待安装完毕,下一步需要修改一下配置文件:主要修改extension,如果你的mongodb服务不是本机,那么也要修改一下。
'mode' => 'development',
/*
* support extension: uprofiler, tideways_xhprof, tideways, xhprof
* default: xhprof
*/
'extension' => 'tideways_xhprof', //主要修改这里
// Can be either mongodb or file.
/*
'save.handler' => 'file',
'save.handler.filename' => dirname(__DIR__) . '/cache/' . 'xhgui.data.' . microtime(true) . '_' . substr(md5($url), 0, 6),
*/
'save.handler' => 'mongodb',
// Needed for file save handler. Beware of file locking. You can adujst this file path
// to reduce locking problems (eg uniqid, time ...)
//'save.handler.filename' => __DIR__.'/../data/xhgui_'.date('Ymd').'.dat',
'db.host' => 'mongodb://127.0.0.1:27017',
'db.db' => 'xhprof',
测试MongoDB连接情况并优化索引;
当前机器安装过mongo客户端才能调用mongo命令,mongo客户端的安装方法第四步有详细说明。
$ /usr/local/mongodb/bin/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 } )
nginx配置访问xhgui站点
server {
listen 80;
listen 9920;
server_name test.xhgui.com;
root /data/wwwroot/xhgui-branch/webroot;
index index.html index.htm index.php;
#charset koi8-r;
access_log /dev/null;
location / {
if (!-e $request_filename) {
rewrite . /index.php last;
break;
}
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
location ~ \.php$ {
access_log /data/logs/web/test.xhgui.com.access.log main;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
重新加载配置
/usr/local/nginx/sbin/nginx -s reload -c /usr/local/nginx/conf/nginx.conf
这个时候已经可以访问xhgui界面了
重点的重点来了
整合tyideways到EasySwoole
网上找到很多教程都是说修改Nginx配置,如下面的内容:
注意:对于Swoole并不适用,因为Swoole是独立启动的,可不经过Nginx。
server {
listen 80;
server_name site.localhost;
root /data/wwwroot/myweb/;
fastcgi_param PHP_VALUE "auto_prepend_file=/data/wwwroot/xhgui-branch/external/header.php";
}
这只适合普通的使用Nginx作为服务器的PHP项目。
真正解决方案:
修改EasySwooleEvent.php的:onRequest
public static function onRequest(Request $request, Response $response): bool
{
//'===========开启分析生成文件==========='
if (extension_loaded('uprofiler')) {
uprofiler_enable(UPROFILER_FLAGS_CPU | UPROFILER_FLAGS_MEMORY);
} else if (extension_loaded('tideways_xhprof')) {
tideways_xhprof_enable(TIDEWAYS_XHPROF_FLAGS_MEMORY | TIDEWAYS_XHPROF_FLAGS_MEMORY_MU | TIDEWAYS_XHPROF_FLAGS_MEMORY_PMU | TIDEWAYS_XHPROF_FLAGS_CPU);
} else if (extension_loaded('tideways')) {
tideways_enable(TIDEWAYS_FLAGS_CPU | TIDEWAYS_FLAGS_MEMORY);
tideways_span_create('sql');
} else if(function_exists('xhprof_enable')){
if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 4) {
xhprof_enable(XHPROF_FLAGS_CPU | XHPROF_FLAGS_MEMORY | XHPROF_FLAGS_NO_BUILTINS);
} else {
xhprof_enable(XHPROF_FLAGS_CPU | XHPROF_FLAGS_MEMORY);
}
}else{
throw new Exception("Please check the extension name in config/config.default.php \r\n,you can use the 'php -m' command.", 1);
}
return true;
}
修改EasySwooleEvent.php的:afterRequest
public static function afterRequest(Request $request, Response $response): void
{
//'===========性能分析生成文件==========='
$server = ServerManager::getInstance()->getSwooleServer();
$_SERVER = $request->getServerParams();
$_SERVER = array_change_key_case($_SERVER, CASE_UPPER);
$_GET = $request->getQueryParams();
$_ENV = null;
if (extension_loaded('uprofiler')) {
$data['profile'] = uprofiler_disable();
} else if (extension_loaded('tideways_xhprof')) {
$data['profile'] = tideways_xhprof_disable();
} else if (extension_loaded('tideways')) {
$data['profile'] = tideways_disable();
$sqlData = tideways_get_spans();
$data['sql'] = array();
if(isset($sqlData[1])){
foreach($sqlData as $val){
if(isset($val['n'])&&$val['n'] === 'sql'&&isset($val['a'])&&isset($val['a']['sql'])){
$_time_tmp = (isset($val['b'][0])&&isset($val['e'][0]))?($val['e'][0]-$val['b'][0]):0;
if(!empty($val['a']['sql'])){
$data['sql'][] = [
'time' => $_time_tmp,
'sql' => $val['a']['sql']
];
}
}
}
}
} else {
$data['profile'] = xhprof_disable();
}
$uri = array_key_exists('REQUEST_URI', $_SERVER)
? $_SERVER['REQUEST_URI']
: null;
if (empty($uri) && isset($_SERVER['argv'])) {
$cmd = basename($_SERVER['argv'][0]);
$uri = $cmd . ' ' . implode(' ', array_slice($_SERVER['argv'], 1));
}
$time = array_key_exists('REQUEST_TIME', $_SERVER)
? $_SERVER['REQUEST_TIME']
: time();
$requestTimeFloat = explode('.', $_SERVER['REQUEST_TIME_FLOAT']);
if (!isset($requestTimeFloat[1])) {
$requestTimeFloat[1] = 0;
}
$requestTs = array('sec' => $time, 'usec' => 0);
$requestTsMicro = array('sec' => $requestTimeFloat[0], 'usec' => $requestTimeFloat[1]);
$uri .= strrpos($uri,'?')===false?'?'.http_build_query($_GET):http_build_query($_GET);
$data['meta'] = array(
'url' => $uri,
'SERVER' => $_SERVER,
'get' => $_GET,
'env' => $_ENV,
'simple_url' => preg_replace('/\=\d+/', '', $uri),
'request_ts' => $requestTs,
'request_ts_micro' => $requestTsMicro,
'request_date' => date('Y-m-d', $time),
);
//print_r($data);
$dirname = './Log/xhprof';
if (!is_dir($dirname)){
mkdir($dirname,0755,true);
}
$filename = $dirname.'/'.date('Ymd').'.xhprof';
file_put_contents($filename, json_encode($data)."\n", FILE_APPEND);
}
其实我只是把 Xhgui的 external/header.php文件内容拷贝出来,然后修改一下兼容Swoole而已。
配置完毕,重启EasySwoole
php easyswoole start
这个时候访问接口,会在EasySwoole的/Log/xhprof目录生成一些日志文件。
导入日志
使用xhgui的导入工具,导入生成的日志到xhgui。
php import.php -f=/data/wwwroot/myweb/Log/xhprof/20200111.xhprof
导入成功后,再次访问Xhgui平台,可以看到有数据了:
点击任意一条记录的方法POST或GET:
点击查看火焰图:
哇哇哇。。。给自己鼓掌!!欢呼吧,折腾了两天终于成功搭建好了。特意记录下来,供朋友们参考!
写文章不易,谢谢你的支持!
转载:https://blog.csdn.net/uisoul/article/details/103936873