Linux系统下使用XHProf和XHGui分析PHP运行性能


Posted in PHP onDecember 08, 2015

什么是性能分析?
性能分析是衡量应用程序在代码级别的相对性能。性能分析将捕捉的事件包括:CPU的使用,内存的使用,函数的调用时长和次数,以及调用图。性能分析的行为也会影响应用性能。
什么时候应该进行性能分析?
在考虑是否进行性能分析时,你首先要想:应用是否存在性能问题?如果有,你要进一步考虑:这个问题有多大?

如果你不这样做,将会陷入一个陷阱——过早优化,这可能会浪费你的时间。

为了评断应用是否存在性能问题,你应该确定性能目标。例如,100 个并发用户的响应时间小于 1s 。然后,你需要进行基准测试,看是否达到这个目标。一个常见的错误是,在开发环境进行基准测试。事实上,你必须在生产环境进行基准测试。(实际生产环境或模拟的生产环境,后者很容易在 SaaS 实现。
用于基准测试的产品很多,包括 ab,siege 和 JMeter。我个人比较喜欢 JMeter 的功能集,但 ab 和 siege 更加易用。

一旦你确定应用存在性能问题,就需要分析其性能,实施改进,然后再一次进行基准测试,查看问题是否解决。每一次变更之后,你都该进行基准测试查看效果。如果你做了很多变更,却发现应用性能有所下降,你就无法确定具体是哪一次变更导致了这个问题。

下图是我定义的性能生命周期:

Linux系统下使用XHProf和XHGui分析PHP运行性能

性能下降的一般原因
导致性能下降的一般原因中,有些相当出人意料。即便是像 PHP 这样的高级语言,代码的好坏也很少是问题的根源。在当今的硬件配置条件下,CPU 很少是性能限制的原因。常见的原因反而是:

数据存储

  • PostgreSQL
  • MySQL
  • Oracle
  • MSSQL
  • MongoDB
  • Riak
  • Cassandra
  • Memcache
  • CouchDB
  • Redis

外部资源

  • APIs
  • 文件系统
  • 网络接口
  • 外部流程
  • 糟糕的代码

选择哪一种性能分析器?
在 PHP 世界里,有两个截然不同的的性能分析器——主动和被动。

主动 VS 被动性能分析
主动分析器在开发过程中使用,由开发人员启用。主动分析器收集的信息比被动分析器多,对性能的影响更大。通常,主动分析器不能用在生产环境中。XDebug 就是一种主动分析器。

因为无法在生产环境中使用主动分析器,Facebook 推出了一个被动分析器——XHProf。XHProf 是为了在生产环境中使用而打造的。它对性能的影响最小,同时收集足够的信息用于诊断性能问题。XHProf 和 OneAPM 都是被动分析器。

通常,XDebug 收集的额外信息对于一般的性能问题分析并不必要。这意味着,被动分析器是用于不间断性能分析的更佳选择,即使是在开发环境中。

XHProf + XHGui
XHProf 由 Facebook 开发的,包含一个基本的用户界面用于查看性能数据。此外,Paul Reinheimer 开发了 XHGui 和一个增强的用户界面(UI)用于查看、比较和分析性能数据。

安装
安装 XHProf
XHProf 可通过 PECL 安装,步骤如下:

$ pecl install xhprof-beta

该 pecl 命令将尝试自动更新你的 php.ini 设置。pecl 尝试更新的文件可以使用以下命令找到:

$ pecl config-get php_ini

它会在指定的文件(如果有的话)顶部增加新的配置行。你可能想把他们移到一个更合适的位置。

一旦你编译了该扩展程序,您必须启用它。为此,您需要在 PHP INI 文件添加以下代码:

[xhprof]
extension=xhprof.so

之后,结合 XHGui 就能轻松地执行性能分析与检查。

安装 XHGui
安装 XHGui,必须直接从 git 获取。该项目可以在 github 上找到,地址为:https://github.com/perftools/xhgui

XHGui 要求:

  • PHP 5.3+
  • ext/mongo
  • composer
  • MongoDB (若只需要收集数据,则可选可不选;若需要数据分析,则为必选)

首先,克隆项目到任意位置。在基于 Debian 的 Linux 系统(例如 Ubuntu 等等),可能是 /var/www。在 Mac OS X 系统,可能是 /Library/WebServer/Documents。

$ cd /var/www
$ git clone https://github.com/perftools/xhgui.git
$ cd xhgui
$ php install.php

最后一个命令是运行 composer 以安装依赖并检查 XHGui 缓存目录的权限。如果失败,你可以手动运行 composer install。

下一步,你可能需要创建配置文件。这一步很容易实现,可以使用在 /path/to/XHGui/config/config.default.php 下的默认配置文件。

如果你在本地运行 MongoDB,没有身份验证,则可能不需要这样做。因为它将回退为默认值。而在多服务器环境中,你会需要一个所有服务器都能进行存储的远程 MongoDB 服务器,并进行恰当的配置。

为提高 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 } )

其他配置
如果你不想在生产环境中安装 mongo ,或无法让 Web 服务器访问 mongo 服务器,您可以将性能分析数据保存在磁盘中,再导入到本地 MongoDB 供以后分析。

为此,请在 config.php 中进行以下修改:

<?php 
'save.handler' = 'file', 
'save.handler.filename' => '/path/to/xhgui/xhprof-' .uniqid("", true). '.dat', 
?>

改变文件中的 save.handler,然后取消批注 save.handler.filename ,为其赋一个恰当的值。

注意:默认每天只保存一个分析文件。

一旦分析数据的准备就绪,你就可以使用 XHGui 附带的脚本导入之:

$php /path/to/xhgui/external/import.php /path/to/file.dat

在此之后的步骤都相同。

运行 XHGui
XHGui 是以 PHP 为基础的 Web 应用程序,你可以以 /path/to/xhgui/webroot 为根文件,设置一个标准的虚拟主机。

或者,你可以简单地使用 PHP 5.4+ cli-server 例如:

$ cd /path/to/xhgui
$ php -S 0:8080 -t webroot/
这将使 XHGui 在所有网络接口都可通过 8080 端口进行通信。

运行性能分析器
运行分析器时,你需要在待分析的所有页面包含 external/header.php 脚本。为此,你可以在 PHP ini 文件设置 auto_prepend_file 。你既可以直接在公共 INI 文件进行设置,也可以限制到单一的虚拟主机。

对于 Apache 服务器,添加以下代码:

php_admin_value auto_prepend_file "/path/to/xhgui/external/header.php"

对于 Nginx 服务器,在服务器配置中添加以下代码:

fastcgi_param PHP_VALUE "auto_prepend_file=/path/to/xhgui/external/header.php";

如果您使用 PHP 5.4+ cli-server(PHP -S),则必须通过命令行标记进行设置:

$ php -S 0:8080 -dauto_prepend_file=/path/to/xhgui/external/header.php

默认情况下,分析器运行时只分析(大约) 1% 的请求。这是由以下 external/header.php 代码控制的:

<?php 
if (rand(0, 100) !== 42) { 
  return;
}
?>

如果你想分析每一个请求(例如,在开发阶段),你可以将这段代码注释掉。如果你想让分析 10% 的请求,你可以做如下改动:

<?php
if (rand(0, 10) !== 4) {
  return;
}
?>

这允许你对一小部分用户请求进行分析,而不过多影响单个用户或太多用户。

如果你想在性能分析时进行手动控制,你可以这样做:

<?php 
if (!isset($_REQUEST['A9v3XUsnKX3aEiNsUDZzV']) && !isset($_COOKIE['A9v3XUsnKX3aEiNsUDZzV'])) { 
 return;
} else {
 // Remove trace of the special variable from REQUEST_URI
 $_SERVER['REQUEST_URI'] = str_replace(array('?A9v3XUsnKX3aEiNsUDZzV', '&A9v3XUsnKX3aEiNsUDZzV'), '', $_SERVER['REQUEST_URI']);
 setcookie('A9v3XUsnKX3aEiNsUDZzV', 1);
}
if (isset($_REQUEST['no-A9v3XUsnKX3aEiNsUDZzV'])) { 
 setcookie('A9v3XUsnKX3aEiNsUDZzV', 0, time() - 86400);
 return;
}
?>

这段代码会检查一个随机命名的 GET/POST/COOKIE 变量(在此例中为:A9v3XUsnKX3aEiNsUDZzV),同时创建一个同名的 Cookie ,用于分析该请求的整个过程,例如:表单提交后的重定向,Ajax 请求等等。

此外,它允许一个名为 no-A9v3XUsnKX3aEiNsUDZzV 的 GET/POST 变量来删除 Cookie ,停止分析。

当然,我们欢迎大家尝试使用 OneAPM 来为您的 PHP 和 Java 应用做免费的性能分析。OneAPM 独有的探针能够深入到所有 PHP 和 Java 应用内部完成应用性能管理和监控,包括代码级别性能问题的可见性、性能瓶颈的快速识别与追溯、真实用户体验监控、服务器监控和端到端的应用性能管理。 OneAPM 可以追溯到性能表现差的 SQL 语句 Traces 记录、性能表现差的第三方 API、Web 服务、Cache 等等。

PHP 相关文章推荐
第十一节 重载 [11]
Oct 09 PHP
php入门教程 精简版
Dec 13 PHP
php中DOMDocument简单用法示例代码(XML创建、添加、删除、修改)
Dec 19 PHP
php下获取Discuz论坛登录用户名、用户组、用户ID等信息的实现代码
Dec 29 PHP
php中将指针移动到数据集初始位置的实现代码[mysql_data_seek]
Nov 01 PHP
不使用php api函数实现数组的交换排序示例
Apr 13 PHP
CI使用Tank Auth转移数据库导致密码用户错误的解决办法
Jun 12 PHP
PHP7之Mongodb API使用详解
Dec 26 PHP
PHP数学运算与数据处理实例分析
Apr 01 PHP
php mysql PDO 查询操作的实例详解
Sep 23 PHP
php中输出json对象的值(实现方法)
Mar 07 PHP
tp5框架使用composer实现日志记录功能示例
Jan 10 PHP
thinkPHP中create方法与令牌验证实例浅析
Dec 08 #PHP
详解PHP的Yii框架中日志的相关配置及使用
Dec 08 #PHP
UPUPW 更新 64 位 Apache 系列 PHP 7.0 正式版
Dec 08 #PHP
学习php设计模式 php实现模板方法模式
Dec 08 #PHP
PHP7正式版测试,性能惊艳!
Dec 08 #PHP
学习php设计模式 php实现命令模式(command)
Dec 08 #PHP
学习php设计模式 php实现合成模式(composite)
Dec 08 #PHP
You might like
php 无极分类(递归)实现代码
2010/01/05 PHP
利用PHP生成静态HTML文档的原理
2012/10/29 PHP
PHP基础知识介绍
2013/09/17 PHP
详谈PHP编码转换问题
2015/07/28 PHP
PHP的Yii框架中行为的定义与绑定方法讲解
2016/03/18 PHP
php写一个函数,实现扫描并打印出自定目录下(含子目录)所有jpg文件名
2017/05/26 PHP
Yii2框架中使用PHPExcel导出Excel文件的示例
2017/08/09 PHP
PHP大文件分割上传 PHP分片上传
2017/08/28 PHP
在IE模态窗口中自由查看HTML源码的方法
2007/03/08 Javascript
JS 动态获取节点代码innerHTML分析 [IE,FF]
2009/11/30 Javascript
js 绑定键盘鼠标事件示例代码
2014/02/12 Javascript
Firefox中使用outerHTML的2种解决方法
2014/06/07 Javascript
使用bootstrap3开发响应式网站
2016/05/12 Javascript
easy ui datagrid 从编辑框中获取值的方法
2017/02/22 Javascript
浅谈原生JS实现jQuery的animate()动画示例
2017/03/08 Javascript
jQuery 利用ztree实现树形表格的实例代码
2017/09/27 jQuery
微信小程序实现红包功能(后端PHP实现逻辑)
2018/07/11 Javascript
NestJs 静态目录配置详解
2019/03/12 Javascript
vue+echarts实现可拖动节点的折线图(支持拖动方向和上下限的设置)
2019/04/12 Javascript
[51:17]Mineski vs Secret 2019国际邀请赛淘汰赛 败者组 BO3 第一场 8.22
2019/09/05 DOTA
Python中的列表知识点汇总
2015/04/14 Python
Flask框架Flask-Login用法分析
2018/07/23 Python
widows下安装pycurl并利用pycurl请求https地址的方法
2018/10/15 Python
Pandas:Series和DataFrame删除指定轴上数据的方法
2018/11/10 Python
python登录WeChat 实现自动回复实例详解
2019/05/28 Python
Python高级特性 切片 迭代解析
2019/08/23 Python
Python使用tkinter实现摇骰子小游戏功能的代码
2020/07/02 Python
阿联酋彩妆品牌:OUD MILANO
2019/10/06 全球购物
竞争性谈判邀请书
2014/02/06 职场文书
安全生产活动月方案
2014/03/09 职场文书
目标管理责任书
2014/04/15 职场文书
教师作风整顿个人剖析材料
2014/10/10 职场文书
2015年医德考评自我评价
2015/03/03 职场文书
2016年“12.3”国际残疾人日活动总结
2016/04/01 职场文书
MySQL表的增删改查(基础)
2021/04/05 MySQL
Vue elementUI表单嵌套表格并对每行进行校验详解
2022/02/18 Vue.js