使用 xhprof 進行線上 PHP 性能追蹤及分析

使用 xhprof 進行線上 PHP 性能追蹤及分析

 

之前一直使用基於 Xdebug 進行 PHP 的性能分析,對於本地開發環境來說是夠用了,但如果是線上環境的話,xdebug 消耗較大,配置也不夠靈活,因此線上環境建議使用xhprof 進行 PHP 性能追蹤及分析

# xhprof 的安裝與簡易用法

xhprof是 Facebook 開源的輕量級 PHP 性能分析工具,Linux 環境下可以通過 pecl 直接安裝,比如在 Ubuntu 下僅需 3 行指令

pecl install xhprof-beta
echo "extension=xhprof.so" > /etc/php5/fpm/conf.d/xhprof.ini
service php5-fpm restart

之後可以通過phpinfo()檢查擴展是否已經加載。

具體如何使用呢,xhprof 項目中已經提供了示例以及簡易的 UI,下載 xhprof 項目到 web 服務器,假設可以通過http://localhost/xhprof/訪問,那麼訪問http://localhost/xhprof/examples/sample.php可以看到一些輸出,並且提示通過訪問http://<xhprof-ui-address>/index.php?run=XXX&source=xhprof_foo查看結果。接下來訪問http://localhost/xhprof/xhprof_html/就可以看到已經保存的結果,列出了所有函數的調用以及所消耗的時間。

分析一下示例代碼sample.php,關鍵部分只有 2 行:

//開啓xhprof並開始記錄
xhprof_enable();
//運行一些函數
foo();
//停止記錄並取到結果
$xhprof_data = xhprof_disable();

$xhprof_data中記錄了程序單步運行過程中所有的函數調用時間及 CPU 內存消耗等,具體記錄哪些指標可以通過xhprof_enable的入口參數控制,之後的處理已經與 xhprof 擴展無關,大致是編寫了一個存儲類XHProfRuns_Default,將$xhprof_data序列化並保存到某個目錄,可以通過XHProfRuns_Default(__DIR__)將結果輸出到當前目錄,如果不指定則會讀取 php.ini 配置文件中的xhprof.output_dir,仍然沒有指定則會輸出到/tmp

xhprof_html/index.php將記錄的結果整理並可視化,默認的 UI 裏列出了:

  • funciton name : 函數名
  • calls: 調用次數
  • Incl. Wall Time (microsec): 函數運行時間(包括子函數)
  • IWall%:函數運行時間(包括子函數)佔比
  • Excl. Wall Time(microsec):函數運行時間(不包括子函數)
  • EWall%:函數運行時間(不包括子函數)

每一項應該不難理解,以項目自帶的sample.php爲例,示例中編寫了一個main()函數,main()函數中調用foo()bar()等一些子函數進行了一點字符處理。整個程序運行過程中,main()函數只運行了一次,並且由於main()函數中包括了所有的邏輯,所以main()函數的 IWall%佔比爲 100%,但是由於main()函數的功能都是由子函數實現的,因此main()函數的 EWall%只有 0.3%,而foo()函數完成了主要的工作,EWall%有 98.1%。因此在分析更大型的程序時,往往需要根據這幾項指標分別排序,從不同的角度審視性能消耗。

xhprof_html/index.php中還可以看到[View Full Callgraph]鏈接,點擊後可以繪製出一張可視化的性能分析圖,如果點擊後報錯的話,可能是缺少依賴graphviz,ubuntu 可以通過 apt 安裝

apt-get install graphviz

# 更好的注入方式

瞭解了上面這些,其實就已經可以將 xhprof 整合到任何我們已有的項目中去了。目前大部分 MVC 框架都有唯一的入口文件,只需要在入口文件的開始處注入 xhprof 的邏輯

//開啓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數據
    ...
});

但是這樣免不了要修改項目的源代碼,其實 php 本身就提供了更好的注入方式,比如將上述邏輯保存爲/opt/inject.php,然後修改 php fpm 配置文件

vi /etc/php5/fpm/php.ini
vi /etc/php5/fpm/php.ini
```plain

修改`auto_prepend_file`配置

```plain
auto_prepend_file = /opt/inject.php

這樣所有的 php-fpm 請求的 php 文件前都會自動注入/opt/inject.php文件

如果使用 Nginx 的話,還可以通過 Nginx 的配置文件設置,這樣侵入性更小,並且可以實現基於站點的注入。

fastcgi_param PHP_VALUE "auto_prepend_file=/opt/inject.php";

# 更好的分析工具:xhprof.io 還是 xhpgui

注入代碼後我們還需要實現保存 xhprof 數據以及展示數據的 UI,聽起來似乎又是一大堆工作,有現成的輪子可以用嗎?

經過搜索和比較,貌似比較好的選擇有xhprof.io以及xhpgui

兩個項目做得事情差不多,都提供了 xhprof 數據保存功能以及一套索引展示數據的 UI,下面是一些比較

xhprof.io

  • ✗ 年久失修
  • ✗ 保存 xhprof 數據到 MySQL
  • ✓ 支持域名、URI 等多個維度的數據索引
  • ✓ 函數調用記錄完整,內核級別函數都能顯示
  • ✗ 無法針對個別 URI 開啓
  • ✗ 注入被分割成兩個文件,如果程序被強制中斷時 xhprof 數據將無法收集

xhgui

  • ✓ 保存 xhprof 數據到 MongoDB
  • ✗ 不支持域名索引
  • ✗ 函數調用記錄不完整,部分內核級別函數(如擴展內)無法顯示
  • ✓ 有配置文件可以控制開啓條件
  • ✓ 注入只有一個文件
  • ✓ 狂拽酷炫的基於 D3.js 的調用關係動態圖

可以看到其實兩個項目都不夠完善,相對而言 xhgui 不支持域名索引對於線上調試來說是無法忍受的,因此我最後的選擇是使用 xhprof.io,但是自己進行了微量的調整,修改後的xhprof.io 修正版支持:

  • ✓ 增加開啓開關配置,可以針對個別 URI 開啓
  • ✓ 注入文件合併爲一個

# xhprof.io 修正版安裝與使用

安裝及配置方法如下,假設 web 服務器根目錄爲/opt/htdocs

cd /opt/htdocs
git clone https://github.com/EvaEngine/xhprof.io.git
cd xhprof.io/
composer install
cp xhprof/includes/config.inc.sample.php xhprof/includes/config.inc.php
vi xhprof/includes/config.inc.php

在 MySQL 中建立 xhprof.io 數據庫,假設數據庫名爲xhprof,然後導入 xhprof/setup/database.sql

配置文件config.inc.php中需要調整

  • 'url_base' => 'http://localhost/xhprof.io/', 這是 xhprof.io 界面所在路徑
  • 'pdo' => new PDO('mysql:dbname=xhprof;host=localhost;charset=utf8', 'root', 'password'), 根據 MySQL 實際情況調整配置
  • enable 這是一個匿名函數,當匿名函數返回 true 時啓用 xhprof 數據收集

通過配置enable項,就可以實現線上調試的需求,比如

始終開啓 xhprof

'enable' => function() {
    return true;
}

1/100 概率隨機開啓 xhprof

'enable' => function() {
    return rand(0, 100) === 1;
}

網頁攜帶參數 debug=1 時開啓 xhprof

'enable' => function() {
    return !empty($_GET['debug']);
}

網頁 URL 爲特定路徑時開啓

'enable' => function() {
    return strpos($_SERVER['REQUEST_URI'], '/testurl') === 0;
}

最後按上文所述,在要配置的項目中包含xhprof.io/inc/inject.php即可。

線上環境操作時務必要膽大心細,如果沒有結果尤其注意需要檢查xhprof 擴展是否安裝

# 附錄:xhpgui 的安裝方法

apt-get install mongodb php5-mongo php5-mcrypt
cp /etc/php5/mods-available/mcrypt.ini /etc/php5/fpm/conf.d/
cp /etc/php5/mods-available/mcrypt.ini /etc/php5/cli/conf.d/
cd /opt/htdocs
git clone https://github.com/perftools/xhgui.git
cd xhgui
composer install
cp config/config.default.php config/config.php
chown www-data.www-data -R cache

編輯 Nginx 配置文件加入

fastcgi_param PHP_VALUE "auto_prepend_file=/opt/htdocs/xhgui/external/header.php";

收集數據過多時可以清空 mongodb

mongo
use xhprof;
db.dropDatabase();

Tags :phpxhprof

Donate:Buy me a coffee|文章有幫助,可以請我喝杯咖啡

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章