讓程式設計師不再苦逼的神器(上)

發表於2019-05-11
乘風破浪,啟航未來!
做程式猿「媛」是一個苦逼的活,大週六地早起在技術群裡招呼,看到沒有啥人響應,說了一句,「估計都沒有醒」,然後一位哥們拋過來,「在加班」 !

做 Web 開發更是一個苦逼的活,不像是做 iOS,搞定客戶端,基本上就萬事大吉了。做 Web 開發不僅僅是要做後端,前端也需要了解和熟悉!

做前後端通吃的 DevOps 全棧工程師絕對是最苦逼的活,不但需要做開發,而且還要了解運維、優化,不會運維的工程師絕對不是一個好架構師!

但所幸的事,一個優秀的工程師儘管很忙,儘管要熟悉前後端、系統、運營、優化,但是在他的工具箱裡,放著一大堆工具,讓這種生活變得不那麼苦逼。

這裡就介紹四個讓我們 DevOps 生活變得美好的神器。分別是 Xdebug、XHProf 、OneAPM 和 SocketLog。

Xdebug

作為開發人員,睡得最踏實的事是對自己寫的程式碼瞭如指掌,無論是從功能層面,還是效能層面。而做 Web PHP 開發,比較棘手的一件事情就是程式碼的除錯。作為指令碼語言,在遠端伺服器端執行,客戶端生成的全是 HTML 程式碼,一般認為我們無法準確地除錯執行的情況,更不要說單步除錯、變數監控等事情了。其實這是有誤解的,有一個 PHP 擴充套件 Xdebug 能讓方便地讓我們除錯遠端伺服器上執行的程式碼。

方法很簡單,從Xdebug下載最新版的原始碼。wget 解壓:
?
1tar –zxvf xdebug-2.3.3.tgz 
2cd xdebug-2.3.3 
3phpize 
4./configure –with-php-config=/usr/bin/php-config
5make 
6make install

再增加相應的配置到 php.ini ,需要注意有兩點:
1. xdebug 是 zend_extension
2. 埠預設是 9000,和 php-fpm 的預設埠衝突,所以這裡換用 9100
?
1[xdebug]
2zend_extension=/usr/lib/php/extensions/no-debug-non-zts-20121212/xdebug.so 
3xdebug.remote_enable=on 
4xdebug.remote_handler=dbgp 
5xdebug.remote_host=localhost 
6xdebug.remote_port=9100

這樣就裝好了,就是如此簡單,下面我們可以開始使用了。以 ThinkPHP 應用開發做例子吧。ThinkPHP 典型的單入口應用,呼叫層次相對複雜。所以想要了解呼叫堆疊還是比較麻煩的。要想得到某一行的呼叫堆疊,可以使用 PHP 自帶的 debug_backtrace 函式可以獲得,但是不怎麼形象,返回來一個陣列而已。而 Xdebug 就能讓這一切視覺化起來。

配合 Xdebug 使用的是 Netbeans 自帶的除錯工具。首先通過首選項修改了一下 PHP 除錯的埠,從9000改成9100,其他不用動。注意在第一行停止是選上的,這將在 PHP 程式除錯時,執行到第一行 PHP 程式碼時停止,在 ThinkPHP 中,即入口檔案 index.php 的第一行停止。

[img=PHP,程式猿,程式設計]https://jf-bucket-public.oss-cn-qingdao.aliyuncs.com/jfperiodical/attached/image/20150702/1411806666.png[/img]

在本例中,筆者做了一個租車管理系統的示例,專案名為 zuches。將程式碼部署在 http://localhost/zuches 能訪問的地方,索引檔案是 index.php。

[img=PHP,程式猿,程式設計]https://jf-bucket-public.oss-cn-qingdao.aliyuncs.com/jfperiodical/attached/image/20150702/129181632.png[/img]


由於 index.php 是入口檔案,所以在 index.php 上點選右鍵,選擇除錯。

[img=PHP,程式猿,程式設計]https://jf-bucket-public.oss-cn-qingdao.aliyuncs.com/jfperiodical/attached/image/20150702/-55690232.png[/img]


然後 Netbeans 自動開啟了如下地址,進入除錯狀態 :http://localhost/zuches/index.php?XDEBUG_SESSION_START=netbeans-xdebug 同時執行位置指示停留到了 index.php 的第一行。如下:

[img=PHP,程式猿,程式設計]https://jf-bucket-public.oss-cn-qingdao.aliyuncs.com/jfperiodical/attached/image/20150702/2042826307.png[/img]


點選繼續執行,由於在 IndexController.class.php 的 index 方法中加上了斷點,所以位置指示又停留在了相應的斷點處。

[img=PHP,程式猿,程式設計]https://jf-bucket-public.oss-cn-qingdao.aliyuncs.com/jfperiodical/attached/image/20150702/468314068.png[/img]


這個時候,我們可以檢視中斷時候的呼叫堆疊和變數了。通過堆疊可以方便地定位到各定位的類和相應方法。

[img=PHP,程式猿,程式設計]https://jf-bucket-public.oss-cn-qingdao.aliyuncs.com/jfperiodical/attached/image/20150702/268650822.png[/img]


各種變數,無論是區域性變數和例項屬性,無論是 cookie,還是提交資料,都能在變數表中看到,一目瞭然。

[img=PHP,程式猿,程式設計]https://jf-bucket-public.oss-cn-qingdao.aliyuncs.com/jfperiodical/attached/image/20150702/1344229064.png[/img]


總之,Xdebug 讓我們即使做的是伺服器端頁面和 API 開發,也可以像桌面應用開發一樣,除錯每一行程式碼了。


Xdebug 除了使用來進行單步除錯之外,還能收集請求中的執行日誌,記錄每一個函式的執行過程。這些日誌可以用 wincachegrind 等工具進行分析,看到函式的呼叫棧和所花的時間。這裡不再贅述,我們也不是很推薦,因為 XHProf 在這塊要輕量級和強大的多,不用如此費勁地下載日誌,分析日誌。


XHProf


如前文所述,對於 PHP 中函式呼叫棧和效能分析,XHProf 將 Xdebug 開的先河繼承併發揚光大。XHProf 也是 PHP 擴充套件,不過不建議從http://pecl.php.net/package/xhprof 上下載,版本已有近2年沒有更新了,最好從https://github.com/phacility/xhprof 上下載。下載編譯配置和使用過程都很簡單。


配置只需要加上如下兩行:

?
1[xhprof]
2extension=/usr/lib/php/extensions/no-debug-non-zts-20121212/xhprof.so

然後將要相應的 XHProf 程式碼配置到需要監控的頁面中,即可以獲得整個頁面的執行中,各函式的呼叫報表。XHProf 提供了示例,配置成功執行如下:

[img=PHP,程式猿,程式設計]https://jf-bucket-public.oss-cn-qingdao.aliyuncs.com/jfperiodical/attached/image/20150702/-1072166469.png[/img]


如何看到上面圖中的效果呢?將 examples、xhprof_html、xhprof_lib 三個目錄放到瀏覽器能訪問到的地方,比如 localhost 的主目錄內。修改 examples/sample.php 中的換成 如:localhost/xhprof_html/。即可。


然後訪問 http://localhost/examples/sample.php 即可以得到輸出結果如下:

[img=PHP,程式猿,程式設計]https://jf-bucket-public.oss-cn-qingdao.aliyuncs.com/jfperiodical/attached/image/20150702/-390899433.png[/img]


從 examples/sample.php 中可以看到,對於頁面的監測分析,在頁面頂部使用 xhprof_enable 開始,而效能的分析的結束,則是在頁面底部使用 xhprof_disable 結束 。這樣每次監控,都需要增加一段程式碼,所以為了規範起見。對 XHProf 相關的操作進行了封裝。繼續採用上例 ThinkPHP 版租車系統的例子,看看如何整合 XHProf 分析。


首先將對 XHProf 的操作封裝成了一個類。

?
1
2
3
4
5
6
7
8
9
10
11
{privatestatic$strNameSpace="";publicstaticfunctioninit($strNameSpace="myhome") {if(!function_exists("xhprof_enable")) {return; } self::$strNameSpace=strval($strNameSpace);$param= XHPROF_FLAGS_CPU + XHPROF_FLAGS_MEMORY;$path=getcwd();require_once$path.'/../xhprof_lib/utils/xhprof_lib.php';require_once$path.'/../xhprof_lib/utils/xhprof_runs.php'; xhprof_enable($param); register_shutdown_function("XHProf::genResult"); }publicstaticfunctiongenResult() {$profiler_namespace= self::$strNameSpace;$xhprof_data= xhprof_disable();$xhprof_runs=newXHProfRuns_Default();$run_id=$xhprof_runs--->save_run($xhprof_data,$profiler_namespace); $profiler_url= sprintf('http://'.$_SERVER['HTTP_HOST'] .'/xhprof_html/index.php?run=%s&source=%s',$run_id,$profiler_namespace); echo' <p style="background: orange;"> ' .'<a href="'.$profiler_url. '" target="_blank">Profiler output</a> </p>'; } }


在這個封裝的類中,首先檢測了 xhprof_enable 函式是否存在,這可以用於判斷是否成功安裝配置了 xhprof 擴充套件。其次設定了引數,特別值得一提的是使用函式 register_shutdown_function 來註冊一個操作,這個操作在頁面結束之前,會自動執行。


這樣,在分析頁面效能之時,只需要在 ThinkPHP 應用的 index.php 上加上兩行,即可以了。

比如:

?
1require_once"xhprof.php"; 
2XHProf::init("zuches");

執行後,得到如下效果:

[img=PHP,程式猿,程式設計]https://jf-bucket-public.oss-cn-qingdao.aliyuncs.com/jfperiodical/attached/image/20150702/-1440803838.png[/img]




並且每一個頁面,都有這樣的輸出。點選「Profiler output」即得到了報表。


[img=PHP,程式猿,程式設計]https://jf-bucket-public.oss-cn-qingdao.aliyuncs.com/jfperiodical/attached/image/20150702/-1572266668.png[/img]


在本例中,可以看到最後的整合方法,執行時間超過了2 S,是值得關注並優化了。從報表中,可以首先看到各個方法或者函式的呼叫次數,執行時間,以及可以層層點進去看到父子層級的呼叫關係 。


需要注意的是,線上上,如上使用 XHProf 要慎重,即使開啟,也要有限制條件地開啟,比如當前使用者是某些除錯開發者使用者時才開啟。否則,普通使用者看到這樣的輸出,則不明所以,給使用者帶來困惑,當然,我們也可以不輸出,而將資料直接儲存,在後臺系統中檢視,這樣使用者就感受不到了。有一個工具,可以完全消除這些糾結。那就是 OneAPM,在第四部分我們再介紹。


未完,待續... ...

回覆

相關文章