Docker中使用Xhprof 對程式碼進行效能分析

善良的死神發表於2021-09-18

介紹

XHProf 是一個分層 PHP 效能分析工具。它報告函式級別的請求次數和各種指標,包括阻塞時間,CPU時間和記憶體使用情況。一個函式的開銷,可細分成呼叫者和被呼叫者的開銷,XHProf 資料收集階段,它記錄呼叫次數的追蹤和包容性的指標弧在動態 callgraph 的一個程式。它獨有的資料計算的報告/後處理階段。在資料收集時,XHProfd通過檢測迴圈來處理遞迴的函式呼叫,並通過給遞迴呼叫中每個深度的呼叫一個有用的命名來避開死迴圈。XHProf分析報告有助於理解被執行的程式碼的結構,它有一個簡單的 HTML的 使用者介面( PHP寫成的)。基於瀏覽器的效能分析使用者介面能更容易檢視,或是與同行們分享成果。也能繪製呼叫關係圖。

簡單說就是檢視 PHP 效能和耗時情況

Docker安裝

本文演示使用倉庫 dnmp

  1. 啟動 PHP 容器並進入容器
     docker exec -it php71 sh
  2. 使用 docker-php-extension 安裝 Xhprof 擴充套件
      install-php-extensions xhprof
    Docker中使用Xhprof 對程式碼進行效能分析
  3. php -m 檢視 xhprof 擴充套件
    Docker中使用Xhprof 對程式碼進行效能分析

使用

安裝成功後,可以在 PHP 程式碼中使用,使用 tp,在 public/index.php 檔案中新增程式碼

//開始分析
\xhprof_enable();

require __DIR__ . '/../vendor/autoload.php';

// 要進行分析的程式碼,執行HTTP應用並響應
$http = (new App())->http;
//分析結束,並獲取分析結果
$xhprof_data = \xhprof_disable();
var_dump($xhprof_data);
exit;

$xhprof_data 變數儲存程式執行過程中所有的函式呼叫時間及 CPU 記憶體消耗

具體記錄哪些指標可以通過 xhprof_enable 的引數控制,目前支援的引數有:

  1. HPROF_FLAGS_NO_BUILTINS 跳過所有內建(內部)函式。
  2. XHPROF_FLAGS_CPU 輸出的效能資料中新增 CPU 資料。
  3. XHPROF_FLAGS_MEMORY 輸出的效能資料中新增記憶體資料。

訪問 tp 框架,列印分析結果

Docker中使用Xhprof 對程式碼進行效能分析

可以看到,雖然成功分析出來結果,但是資料對我們不是太友好

對分析結果,可以通過 xhprof 自帶的 lib 庫生成到一個檔案裡面,然後通過 xhprof 自帶的 html 來顯示

xhprof_html 來顯示分析結果

配置 xhprof_html 的訪問域名,以便我們進行訪問
在 php 容器的根目錄下 通過 find -name xhprof_html 搜尋擴充套件生成的 html 的位置
Docker中使用Xhprof 對程式碼進行效能分析
Docker中使用Xhprof 對程式碼進行效能分析
/usr/local/lib/php 資料夾下,可看到兩個資料夾 xhprof_htmlxhprof_lib
xhprof_html: 進行分析結果網頁顯示的html
xhprof_lib: 對分析結果進行其它處理的類庫

配置訪問域名

複製 xhprof_html,xhprof_lib 檔案到 nginx 的 對外訪問目錄,我的 PHP 容器的 www 對映的就是 nginx 的訪問目錄,不在一個容器,可以先複製到宿主機,然後在複製到 nginx 容器內

# 建立 nginx 的目錄
/www # mkdir /www/xhprof
/www # mkdir /www/xhprof/xhprof_html
/www # mkdir /www/xhprof/xhprof_lib

#複製 xhprof_html,xhprof_lib 到新建立的目錄
/www/xhprof # cp -R /usr/local/lib/php/xhprof_html/* /www/xhprof/xhprof_html/
/www/xhprof # cp -R /usr/local/lib/php/xhprof_lib/* /www/xhprof/xhprof_lib

nginx 配置域名,並重啟 nginx 服務

server {
    listen       80;
    server_name  xhprof.li;
    root   /www/xhprof/xhprof_html;
    index  index.php index.html index.htm;
    #charset koi8-r;

    access_log /dev/null;
    #access_log  /var/log/nginx/nginx.localhost.access.log  main;
    error_log  /var/log/nginx/nginx.xhprof-li.error.log  warn;

    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    location / {
        if (!-e $request_filename) {
            rewrite  ^(.*)$  /index.php?s=$1  last;
            break;
        }
    }

    location ~ \.php$ {
        fastcgi_pass   php71:9000;
        fastcgi_split_path_info  ^((?U).+\.php)(/?.+)$;
        include        fastcgi-php.conf;
        include        fastcgi_params;
    }


}

分析結果顯示檔案

  1. 配置生成的檔案輸出目錄,PHP.ini,手動建立下,注意許可權

    [xhprof]
    # 注意目錄是否有許可權生成檔案
    xhprof.output_dir = /www/xhprof
  2. 重啟 PHP 服務

     docker-compose restart php71
  3. 更改程式碼,ThinkPHP6 index.php

    <?php
    // [ 應用入口檔案 ]
    namespace think;
    //xhprof-開始分析
    \xhprof_enable();
    require __DIR__ . '/../vendor/autoload.php';
    //xhprof-要進行分析的程式碼,執行HTTP應用並響應
    //-----------------------------------------
    $http = (new App())->http;
    //-----------------------------------------
    //xhprof-分析結束,並獲取分析結果
    $xhprof_data = \xhprof_disable();
    //xhprof-引入類庫,進行分析結果的儲存
    include_once "/www/xhprof/xhprof_lib/utils/xhprof_lib.php";
    include_once "/www/xhprof/xhprof_lib/utils/xhprof_runs.php";
    // save raw data for this profiler run using default
    // implementation of iXHProfRuns.
    $xhprof_runs = new \XHProfRuns_Default();
    // xhprof - 儲存資料到 xhprof-test 檔案
    $run_id = $xhprof_runs->save_run($xhprof_data, "xhprof_test");
    dd($run_id);
    $response = $http->run();
    $response->send();
    $http->end($response);
  4. 執行 tp 框架,生成檔案
    Docker中使用Xhprof 對程式碼進行效能分析

  5. 開啟剛剛配置 xhprof 域名
    Docker中使用Xhprof 對程式碼進行效能分析

  6. 顯示結果
    Docker中使用Xhprof 對程式碼進行效能分析

欄位名 含義
Calls 呼叫次數
Incl. Wall Time 呼叫的包括子函式所有花費時間,以微秒算
Excl. Wall Time 函式執行本身花費的時間,不包括子樹執行時間,以微秒算
Incl. CPU 呼叫的包括子函式所有花費的cpu時間
Excl. CPU 函式執行本身花費的cpu時間,不包括子樹執行時間,以微秒算
Incl.MemUse 包括子函式執行使用的記憶體, 以位元組算
Excl.MemUse 函式執行本身記憶體,以位元組算
Incl.PeakMemUse Incl.MemUse的峰值

可以點選 [View Full Callgraph] 連結,檢視視覺化分析圖
如果報錯 failed to execute cmd: " dot -Tsvg". stderr: 'sh: dot: not found ', 大概率是缺少依賴 graphviz

安裝 graphviz

graphviz 是一個繪製圖形的工具,可以更為直觀的讓你檢視效能的瓶頸。
docker 容器內使用 apk 進行安裝

  1. 進入 PHP71 容器
     docker exec -it php71 sh
  2. 安裝 graphviz
     apk add graphviz --repository=http://dl-cdn.alpinelinux.org/alpine/edge/main
    Docker中使用Xhprof 對程式碼進行效能分析
  3. 再次點選 [View Full Callgraph]
    Docker中使用Xhprof 對程式碼進行效能分析

接入專案

在上面演示中,是直接通過修改框架的入口檔案 index.php 來進行分析的,但不可能每個專案我們都需要修改原始碼
其實 PHP 本身就提供了更好的注入方式,將上述邏輯儲存到 xhprof_inject.php 檔案中,然後修改 PHP 配置檔案中的 auto_prepend_file 配置,這樣所有的 PHP 請求檔案都會自動注入 xhprof_inject.php 這個檔案,這樣侵入性更小,並且可以實現基於站點的注入。

  1. /www/xhprof/xhprof_inject.php
    <?php
    //開啟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-引入類庫,進行分析結果的儲存
     include_once "/www/xhprof/xhprof_lib/utils/xhprof_lib.php";
     include_once "/www/xhprof/xhprof_lib/utils/xhprof_runs.php";
     // save raw data for this profiler run using default
     // implementation of iXHProfRuns.
     $xhprof_runs = new \XHProfRuns_Default();
     // xhprof - 儲存資料到 xhprof-test 檔案
     $xhprof_runs->save_run($xhprof_data, "xhprof_test");
    });
  2. 更改 PHP.ini 或新增 nginx引數
    ; Automatically add files before PHP document.
    ; http://php.net/auto-prepend-file
    auto_prepend_file =/www/xhprof/xhprof_inject.php
    更改完重啟 PHP 容器
    如果不想修改 PHP 配置檔案,也可修改 Nginx 的 fastcgi_param
    fastcgi_param PHP_VALUE "auto_prepend_file=/www/xhprof/xhprof_inject.php";
    修改完後重啟 Nginx 服務
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章