1. 程式人生 > >使用xhprof進行線上PHP效能追蹤及分析

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

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

xhprof的安裝與簡易用法

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


    
  1. pecl install xhprof-beta
  2. echo "extension=xhprof.so" > /etc/php5/fpm/conf.d/xhprof.ini
  3. service php5-fpm re start

之後可以通過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的邏輯


    
  1. //開啟xhprof xhprof_enable(XHPROF_FLAGS_MEMORY | XHPROF_FLAGS_CPU); //在程式結束後收集資料 register_shutdown_function( function () {
  2.    $xhprof_data        = xhprof_disable();     //讓資料收集程式在後臺執行
  3.     if (function_exists( 'fastcgi_finish_request')) {
  4.        fastcgi_finish_request();
  5.    }     //儲存xhprof資料
  6.    ...
  7. });

但是這樣免不了要修改專案的原始碼,其實php本身就提供了更好的注入方式,比如將上述邏輯儲存為/opt/inject.php,然後修改php fpm配置檔案

vi /etc/php5/fpm/php.ini
    

修改auto_prepend_file配置

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


    
  1. cd /opt/htdocs
  2. git clone https: //github.com/EvaEngine/xhprof.io.git cd xhprof.io/
  3. composer install
  4. cp xhprof/includes/config.inc.sample.php xhprof/includes/config.inc.php
  5. 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


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

1/100概率隨機開啟xhprof


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

網頁攜帶引數debug=1時開啟xhprof


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

網頁URL為特定路徑時開啟


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

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

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

附錄:xhpgui的安裝方法


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

編輯Nginx配置檔案加入

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

收集資料過多時可以清空mongodb


    
  1. mongo use xhprof;
  2. db .dropDatabase();

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

xhprof的安裝與簡易用法

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


  
  1. pecl install xhprof-beta
  2. echo "extension=xhprof.so" > /etc/php5/fpm/conf.d/xhprof.ini
  3. service php5-fpm re start

之後可以通過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的邏輯


  
  1. //開啟xhprof xhprof_enable(XHPROF_FLAGS_MEMORY | XHPROF_FLAGS_CPU); //在程式結束後收集資料 register_shutdown_function( function () {
  2.    $xhprof_data        = xhprof_disable();     //讓資料收集程式在後臺執行
  3.     if (function_exists( 'fastcgi_finish_request')) {
  4.        fastcgi_finish_request();
  5.    }     //儲存xhprof資料
  6.    ...
  7. });

但是這樣免不了要修改專案的原始碼,其實php本身就提供了更好的注入方式,比如將上述邏輯儲存為/opt/inject.php,然後修改php fpm配置檔案

vi /etc/php5/fpm/php.ini
  

修改auto_prepend_file配置

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


  
  1. cd /opt/htdocs
  2. git clone https: //github.com/EvaEngine/xhprof.io.git cd xhprof.io/
  3. composer install
  4. cp xhprof/includes/config.inc.sample.php xhprof/includes/config.inc.php
  5. 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


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

1/100概率隨機開啟xhprof


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

網頁攜帶引數debug=1時開啟xhprof


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

網頁URL為特定路徑時開啟


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

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

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

附錄:xhpgui的安裝方法


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

編輯Nginx配置檔案加入

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

收集資料過多時可以清空mongodb


  
  1. mongo use xhprof;
  2. db .dropDatabase();