使用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 re
start
之後可以通過phpinfo()
檢查擴充套件是否已經載入。
具體如何使用呢,xhprof專案
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
修改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
-
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.gitcd 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();
之前一直使用基於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 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的邏輯
-
//開啟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
修改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
-
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.gitcd 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();