
哈Ha! 我叫Timur Shagiakhmetov,我是
Badoo的PHP开发人员。
应用程序性能是程序员工作质量的最重要标准之一。 在优化PHP应用程序方面,助手是探查器。
最近,我们
讨论了用于剖析的工具。 让我提醒您:当不清楚哪些代码部分影响了响应生成时间的增加时,性能分析工具之一就是
XHProf 。 这是PHP的扩展,允许您在战斗服务器上分析代码并随后对其进行改进。
但我也想拥有性能变化的历史记录,以便您可以跟踪性能下降的时间和影响,对吗? 为此,大约一年前,我们开发了
Liveprof ,该工具可通过界面自动分析所有请求,以分析应用程序性能的变化。
我们的工具使您能够分析代码任何部分的性能变化,找到最容易受到影响的地方。 同时,无需专门打开它并等待统计信息累积-它始终处于活动状态并收集所有请求中特定部分的数据。
在本文中,我将讨论使用此工具的实现细节和功能。
关于XHProf的一些知识
首先,简单介绍一下XHProf本身的功能。 这是用C编写的PHP扩展程序的探查器。 它是在Facebook上开发的,并已在公共领域发布。 它具有多个fork(
uprofiler ,
Tideways ),在输出数据格式级别上完全兼容。
XHProf设置所有函数/方法调用的计时器。 它的使用涉及一些开销。 但是它们并不是很大,可以在生产中使用它。
XHProf的结果是以下格式的元素数组:
$data = [ 'parentMethodName==>childMethodName' => [ 'ct' => 1 'wt' => 8 'cpu' => 11 'mu' => 528 'pmu' => 0 ] ];
在哪里
parentMethodName
和
childMethodName
分别是父方法和子方法;
ct
请求上下文中的调用次数;
wt
请求执行时间(包括处理器花费的时间和等待I / O或其他服务的响应的时间);
cpu
处理器在处理请求上花费的时间;
mu
方法调用后的内存消耗变化;
pmu
方法调用后峰值内存消耗的变化。
其他一些选项也是可能的。
XHProf还包含用于可视化由此获得的结果的工具。 对于每个概要分析操作,我们将获得一个表,其中包含每种方法的一组参数。
举个例子
气泡排序结果 <?php class ArrayGenerator { public function getRandomArray(int $count): array { $array = []; for ($i = 0; $i < $count; $i++) { $array[] = rand(0, 1000); } return $array; } } class BubbleSorter { public function sort(&$array): void { $len = count($array); for ($i = 0; $i < $len ; $i++) { for ($j = 0; $j < $len - $i - 1; $j++) { if ($array[$j] > $array[$j + 1]) { $this->swap($array[$j], $array[$j + 1]); } } } } private function swap(&$a, &$b): void { $tmp = $a; $a = $b; $b = $tmp; } public function isSorted(array $array): bool { $len = count($array); for ($i = 0; $i < $len - 1; $i++) { if ($array[$i] > $array[$i + 1]) { return false; } } return true; } } class ArrayPrinter { public function print(array $array, string $delimiter = ' '): void { echo implode($delimiter, $array) . PHP_EOL; } } xhprof_enable(); $n = 10; $arrayGenerator = new \ArrayGenerator(); $array = $arrayGenerator->getRandomArray($n); $sorter = new BubbleSorter(); if (!$sorter->isSorted($array)) { $sorter->sort($array); } $printer = new \ArrayPrinter(); $printer->print($array); $xhprof_data = xhprof_disable();

您可以深入每种方法中,找出哪些方法消耗了多少资源。
您还可以通过突出显示最消耗资源的方法来查看调用图:

XHProf对于手动分析每个请求的性能很有用。 但是,对我们来说,了解全局也很重要。 您需要了解性能如何随时间变化。 为此,开发了一种工具,该工具可以自动模式分析查询并允许您在Web界面中分析查询。
Liveprof:汇总结果并保留历史记录
如何获取分析历史记录?
首先,您需要配置探查器的自动启动并保存结果。 性能不是恒定不变的,并且在发射之间会有所波动。 为了避免这种波动的影响,我们使用了几个查询的平均数据。 结果,我们获得了每个查询的汇总结果,例如,最小值,最大值,平均值和第95个百分位数。 这有助于查找可能并非为每个请求都调用的困难事物。
我们的工具既有优势,也有一些局限性。
聚合器可以做什么:
- 每N个请求自动进行性能分析。
- 每日汇总收集的配置文件。
- 能够查看由探查器测量的每个参数的变化图。 例如,上述wt,cpu,mu,pmu。
- 在一定时间间隔内查看任何方法的性能变化。
- 基于最新汇总数据的火焰图 。
- 查找调用特定方法的查询
局限性:
- 由于我们的工具是一个汇总的工具,因此您无法找出一个查询(例如,最慢的查询)的性能-我们将获得最后一天的平均值。 但这足以评估整体性能动态。 如果有任何请求降低了执行速度,则平均值,第95个百分位数和最大执行时间将改变。
- 您无法明确地恢复完整的调用堆栈,因为XHProf仅返回唯一的父子对以及已消耗资源的值之和。
- 请求与XHProf开销有关的运行时错误。 差别不是很大,但是在测量查询执行时间时必须考虑到这一点。
如何使用分析器
- 首先,分析器需要连接到站点或脚本。 使用该工具最方便的方法是自动启动事件探查器 :
php composer.phar require badoo/liveprof # Run a script to configure database LIVE_PROFILER_CONNECTION_URL=mysql://db_user:db_password@db_mysql:3306/Profiler?charset=utf8 php vendor/badoo/liveprof/bin/install.php
它支持从5.4开始的PHP版本,并且使用时开销很小,这使您可以在战斗环境中使用它。 该工具会自动检测使用的探查器扩展名: XHProf , uprofiler或Tideways 。 在启动时,您需要指定用于连接数据库和配置文件设置的参数。
具有默认设置的代码中的示例用法:
<?php include 'vendor/autoload.php'; \Badoo\LiveProfiler\LiveProfiler::getInstance()->start();
分析结果将保存到数据库中。 每天进行一次汇总过程。 为此,每天选择一个特定请求的所有记录,然后为每个参数计算聚合函数。 聚合功能可以扩展或重新定义。
现在可以使用以下内容:
- 聚合器Web客户端用于配置聚合和查看结果。 将其安装在Docker容器中的最简单方法是:
git clone https:
- 在第一次启动之前,您需要在src / config / services.yaml配置文件中配置数据库连接参数,字段列表和使用的聚合函数。 然后执行安装脚本:
docker-compose exec web bash install.sh
- 必须注册自动运行的汇总和清除表冠中的旧数据的脚本:
- 要填充测试数据,可以运行以下脚本:
docker-compose exec web php /app/bin/cli.php example:a-week-degradation
接口说明
Web界面位于:127.0.0.1:8000。
默认情况下,将打开一个包含汇总查询列表的页面。 它使查找感兴趣的查询,按任何参数对所有查询进行排序以及重新汇总特定查询以查看最新结果变得容易:

使用该工具时,最常用的页面是列出方法和性能变化图表的页面。 它使您可以遍历调用堆栈,观察每个参数的使用情况以及特定时间间隔内的性能变化图:
包含完整的被调用方法列表的页面使您可以快速找到感兴趣的方法并通过转到图表页面来查看图表:
上一个汇总查询的带有火焰图的页面使您可以直观地识别最重的部分。使用XHProf对结果的准确性施加了一些限制。 这是由于以下事实:分析器没有返回完整的调用树,而是仅返回了父子对。 而且,如果从应用程序的不同位置调用了一对方法,那么结果就是我们得到了花费的时间。 对于火焰图,您需要具有完整的调用树。 还原此类树时,参数值会在考虑父级花费的时间的情况下进行标准化。
包含在选定间隔内变慢的方法列表的页面。此外,对于每种方法,您都可以查看哪个子调用对性能的影响最大。 例如,在下面的屏幕截图中,您可以看到
ServiceApi::getAvailableServices()
方法开始运行慢了116毫秒。 这样做的原因是增加了对
ServiceApi::getGifts()
的调用(更改了56毫秒),并且对
ServiceApi::getConfigForList()
方法的调用次数从1增加到了5(另外50ms):

如果事先不知道哪个查询的性能发生了最明显的变化,那么一个页面,其中列出了一些方法,这些方法在没有引用特定查询的情况下变得越来越慢,这将有所帮助:
搜索调用特定方法的查询的页面。它允许您比较不同请求中的执行时间。 查找未使用的代码也很有用:

定制功能
该工具具有充足的自定义机会:
结论
我希望我们的工具对其他开发人员有用。 无需使用其他计时器,就可以检查代码任何部分的性能更改。 由于现在您可以了解是什么因素影响了应用程序性能随时间下降的原因,这也将有助于优化过程。
它在GitHub上可用:
github.com/badoo/liveprof,Web界面是
github.com/badoo/liveprof-ui 。
该工具正在积极开发中,可能包含一些错误。 希望在社区的参与下,情况会更好。 该计划包括添加对XHProf之外的其他探查器的支持,以及扩展受支持数据库的列表。
向我们发送有关在
电报中使用,错误和提取请求的反馈和问题-直接发送到
GitHub 。 我们欢迎提出意见和建议!
特别感谢
Gregory的想法和首次实施。