Docker中使用Xhprof 对代码进行性能分析
介绍
XHProf 是一个分层 PHP 性能分析工具。它报告函数级别的请求次数和各种指标,包括阻塞时间,CPU时间和内存使用情况。一个函数的开销,可细分成调用者和被调用者的开销,XHProf 数据收集阶段,它记录调用次数的追踪和包容性的指标弧在动态 callgraph 的一个程序。它独有的数据计算的报告/后处理阶段。在数据收集时,XHProfd通过检测循环来处理递归的函数调用,并通过给递归调用中每个深度的调用一个有用的命名来避开死循环。XHProf分析报告有助于理解被执行的代码的结构,它有一个简单的 HTML的 用户界面( PHP写成的)。基于浏览器的性能分析用户界面能更容易查看,或是与同行们分享成果。也能绘制调用关系图。
简单说就是查看 PHP 性能和耗时情况
Docker安装
本文演示使用仓库 dnmp
- 启动 PHP 容器并进入容器
docker exec -it php71 sh
- 使用 docker-php-extension 安装 Xhprof 扩展
install-php-extensions xhprof
php -m
查看 xhprof 扩展
使用
安装成功后,可以在 PHP 代码中使用,使用 tp,在 public/index.php 文件中添加代码
//开始分析
\xhprof_enable();
require __DIR__ . '/../vendor/autoload.php';
// 要进行分析的代码,执行HTTP应用并响应
$http = (new App())->http;
//分析结束,并获取分析结果
$xhprof_data = \xhprof_disable();
var_dump($xhprof_data);
exit;
$xhprof_data 变量保存程序运行过程中所有的函数调用时间及 CPU 内存消耗
具体记录哪些指标可以通过 xhprof_enable 的参数控制,目前支持的参数有:
- HPROF_FLAGS_NO_BUILTINS 跳过所有内置(内部)函数。
- XHPROF_FLAGS_CPU 输出的性能数据中添加 CPU 数据。
- XHPROF_FLAGS_MEMORY 输出的性能数据中添加内存数据。
访问 tp 框架,打印分析结果
可以看到,虽然成功分析出来结果,但是数据对我们不是太友好
对分析结果,可以通过 xhprof 自带的 lib 库生成到一个文件里面,然后通过 xhprof 自带的 html 来显示
xhprof_html 来显示分析结果
配置 xhprof_html 的访问域名,以便我们进行访问
在 php 容器的根目录下 通过 find -name xhprof_html
搜索扩展生成的 html 的位置
在 /usr/local/lib/php
文件夹下,可看到两个文件夹 xhprof_html
,xhprof_lib
xhprof_html: 进行分析结果网页显示的html
xhprof_lib: 对分析结果进行其它处理的类库
配置访问域名
复制 xhprof_html,xhprof_lib 文件到 nginx
的 对外访问目录,我的 PHP 容器的 www 映射的就是 nginx 的访问目录,不在一个容器,可以先复制到宿主机,然后在复制到 nginx 容器内
# 创建 nginx 的目录
/www # mkdir /www/xhprof
/www # mkdir /www/xhprof/xhprof_html
/www # mkdir /www/xhprof/xhprof_lib
#复制 xhprof_html,xhprof_lib 到新创建的目录
/www/xhprof # cp -R /usr/local/lib/php/xhprof_html/* /www/xhprof/xhprof_html/
/www/xhprof # cp -R /usr/local/lib/php/xhprof_lib/* /www/xhprof/xhprof_lib
nginx 配置域名,并重启 nginx 服务
server {
listen 80;
server_name xhprof.li;
root /www/xhprof/xhprof_html;
index index.php index.html index.htm;
#charset koi8-r;
access_log /dev/null;
#access_log /var/log/nginx/nginx.localhost.access.log main;
error_log /var/log/nginx/nginx.xhprof-li.error.log warn;
location = /50x.html {
root /usr/share/nginx/html;
}
location / {
if (!-e $request_filename) {
rewrite ^(.*)$ /index.php?s=$1 last;
break;
}
}
location ~ \.php$ {
fastcgi_pass php71:9000;
fastcgi_split_path_info ^((?U).+\.php)(/?.+)$;
include fastcgi-php.conf;
include fastcgi_params;
}
}
分析结果显示文件
配置生成的文件输出目录,
PHP.ini
,手动创建下,注意权限
[xhprof] # 注意目录是否有权限生成文件 xhprof.output_dir = /www/xhprof
重启 PHP 服务
docker-compose restart php71
更改代码,ThinkPHP6 index.php
<?php // [ 应用入口文件 ] namespace think; //xhprof-开始分析 \xhprof_enable(); require __DIR__ . '/../vendor/autoload.php'; //xhprof-要进行分析的代码,执行HTTP应用并响应 //----------------------------------------- $http = (new App())->http; //----------------------------------------- //xhprof-分析结束,并获取分析结果 $xhprof_data = \xhprof_disable(); //xhprof-引入类库,进行分析结果的保存 include_once "/www/xhprof/xhprof_lib/utils/xhprof_lib.php"; include_once "/www/xhprof/xhprof_lib/utils/xhprof_runs.php"; // save raw data for this profiler run using default // implementation of iXHProfRuns. $xhprof_runs = new \XHProfRuns_Default(); // xhprof - 保存数据到 xhprof-test 文件 $run_id = $xhprof_runs->save_run($xhprof_data, "xhprof_test"); dd($run_id); $response = $http->run(); $response->send(); $http->end($response);
运行 tp 框架,生成文件
打开刚刚配置 xhprof 域名
显示结果
字段名 | 含义 |
---|---|
Calls | 调用次数 |
Incl. Wall Time | 调用的包括子函数所有花费时间,以微秒算 |
Excl. Wall Time | 函数执行本身花费的时间,不包括子树执行时间,以微秒算 |
Incl. CPU | 调用的包括子函数所有花费的cpu时间 |
Excl. CPU | 函数执行本身花费的cpu时间,不包括子树执行时间,以微秒算 |
Incl.MemUse | 包括子函数执行使用的内存, 以字节算 |
Excl.MemUse | 函数执行本身内存,以字节算 |
Incl.PeakMemUse | Incl.MemUse的峰值 |
可以点击 [View Full Callgraph] 链接,查看可视化分析图
如果报错 failed to execute cmd: " dot -Tsvg". stderr: 'sh: dot: not found '
, 大概率是缺少依赖 graphviz
安装 graphviz
graphviz 是一个绘制图形的工具,可以更为直观的让你查看性能的瓶颈。
docker 容器内使用 apk
进行安装
- 进入 PHP71 容器
docker exec -it php71 sh
- 安装 graphviz
apk add graphviz --repository=http://dl-cdn.alpinelinux.org/alpine/edge/main
- 再次点击 [View Full Callgraph]
接入项目
在上面演示中,是直接通过修改框架的入口文件 index.php 来进行分析的,但不可能每个项目我们都需要修改源码
其实 PHP 本身就提供了更好的注入方式,将上述逻辑保存到 xhprof_inject.php 文件中,然后修改 PHP 配置文件中的 auto_prepend_file 配置,这样所有的 PHP 请求文件都会自动注入 xhprof_inject.php 这个文件,这样侵入性更小,并且可以实现基于站点的注入。
/www/xhprof/xhprof_inject.php
<?php //开启xhprof xhprof_enable(XHPROF_FLAGS_MEMORY | XHPROF_FLAGS_CPU); //在程序结束后收集数据 register_shutdown_function(function() { $xhprof_data = xhprof_disable(); //让数据收集程序在后台运行 if (function_exists('fastcgi_finish_request')) { fastcgi_finish_request(); } //xhprof-引入类库,进行分析结果的保存 include_once "/www/xhprof/xhprof_lib/utils/xhprof_lib.php"; include_once "/www/xhprof/xhprof_lib/utils/xhprof_runs.php"; // save raw data for this profiler run using default // implementation of iXHProfRuns. $xhprof_runs = new \XHProfRuns_Default(); // xhprof - 保存数据到 xhprof-test 文件 $xhprof_runs->save_run($xhprof_data, "xhprof_test"); });
- 更改
PHP.ini
或添加nginx参数
更改完重启 PHP 容器; Automatically add files before PHP document. ; http://php.net/auto-prepend-file auto_prepend_file =/www/xhprof/xhprof_inject.php
如果不想修改 PHP 配置文件,也可修改 Nginx 的 fastcgi_param
修改完后重启 Nginx 服务fastcgi_param PHP_VALUE "auto_prepend_file=/www/xhprof/xhprof_inject.php";
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: