php-fpm CPU佔用率過高的排查方法【轉】

轉自 http://www.xiaoxiaoliang.com/?p=235

一,開啓日誌記錄,爲以後排查做準備
1.1 開啓php-fpm.conf的錯誤日誌和慢執行日誌和常規日誌, 採樣一個小時,就可以根據這些日誌的內容進行分析問題
error_log = /tmp/error.log //錯誤日誌
access.log = /tmp/access.$pool.log //常規日誌,記錄每次訪問時間,記錄不同參數,以防止惡意攻擊,後面會詳細解析
access.format = “%R – %u %t \”%m %r%Q%q\” %s %f %{mili}d %{kilo}M %{system}C%%”
slowlog = /tmp/slow.$pool.log //滿查詢日誌記錄
request_slowlog_timeout = 3s //慢執行超時時間,可以設置爲1秒
1.2 慢查詢日誌會以堆棧的形式打印每個腳本執行過程中耗時較長的地方,格式如下(可以詳細查看我以前發表的PHP慢日誌的分析)
[日期時間] [pool www] pid 進程號
script_filename = 腳本文件
[0x00007f2d286c2790] replace() /xxx/Plugin.php:72
[0x00007fff78ab00f0] replace() unknown:0
[0x00007f2d286c2420] call_user_func_array() /xxx/Plugin.php:489
[0x00007fff78ab0430] __call() unknown:0
[0x00007f2d286c1f78] contentEx() /xxx/Abstract/Contents.php:141
[0x00007f2d286c1b78] ___content() /xxx/Widget.php:385
[0x00007f2d286c10f8] +++ dump failed
1.3 使用ll /proc/進程號/fd/ 查看,目前PHP進程在操作什麼文件,
使用strace -o /tmp/output.txt -T -tt -F -e trace=all -p <進程號> 來跟蹤進程(可查看我以前發表的strace命令的使用)
[root@localhost www]# cat /tmp/output.txt
61905 18:00:40.169437 epoll_wait(8, {}, 1, 409) = 0 <0.410078>
61905 18:00:40.579997 getsockopt(7, SOL_TCP, TCP_INFO, “\n\0\0\0\0\0\0\0@B\17\0\0\0\0\0\30\2\0\0\0\0\0\0\0\0\0\0\200\0\0\0″…, [104]) = 0 <0.000327>
61905 18:00:40.580786 epoll_wait(8, {}, 1, 1000) = 0 <1.001902>
61905 18:00:41.583271 getsockopt(7, SOL_TCP, TCP_INFO, “\n\0\0\0\0\0\0\0@B\17\0\0\0\0\0\30\2\0\0\0\0\0\0\0\0\0\0\200\0\0\0″…, [104]) = 0 <0.000361>
61905 18:00:41.585020 epoll_wait(8, {}, 1, 999) = 0 <1.001523>
61905 18:00:42.587037 getsockopt(7, SOL_TCP, TCP_INFO, “\n\0\0\0\0\0\0\0@B\17\0\0\0\0\0\30\2\0\0\0\0\0\0\0\0\0\0\200\0\0\0″…, [104]) = 0 <0.000408>
二,結合上述的日誌,分析可能出現的問題
2.1 php錯誤日誌中,存在大量的常規錯誤,例如mysql使用了事務導致死鎖
2.2 php慢查詢日誌可以知道那些函數比較耗時,可以考慮使用擴展來實現解決耗時問題, 也有可能是代碼中大量使用了file_get_contents等默認不會超時的函數
*file_get_contents一步就做完了打開,讀取,關閉的三個動作,過程相當自動化,並且可以讀取遠程內容,非常方便,在網絡狀況差的情況下,可能會導致程序執行陷入停滯或者過慢,
因爲不停的重試和等待PHP進程本身的超時纔會退出。詳情可以查看我以前發表過的關於file_get_contents(擴展函數)的內部實現分析的內容
*這個問題還可以根據跟蹤PHP-FPM進程知道,如果存在大量的epoll或select超時
2.3 查詢TCP連接是否出現大量TIME_WAIT,查詢具體原因(可以參考我以前發表的關於TCP的內容和HTTP Keep-Alive在異常情況下推TIME_WAIT的影響)HTTP協議1.1版規定default行爲是Keep-Alive,
就是會重用TCP連接傳輸多個請求/響應, 還有就是從nginx到php-fpm的原地址都是一樣,TCP連接就會受限制,因爲頻繁的TCP連接建立和關閉,會在服務器上留下TIME_WAIT狀態,
端口一旦進入服務器的TIME_WAIT黑名單,就會產生相當大的超時於等待,甚至阻塞等待
*詳細可以查詢TIME_WAIT對http的影響一文
三,排查攻擊問題
*php是人類的一個偉大的東西,但不代表他永遠不會有bugs, 想了解可以去bugs.php.net查看
3.1 根據常規日誌,分析是否存在惡意請求, 例如超大表達的哈希攻擊,我們查看PHP的數組的實現原理可以知道,它是一個hash表, 包括$_POST,$_GET, 哈希表其實就是一個數組,每個元素是一個鏈表,
當存在一個惡意的post,post大量的a[]=1&a[]=1&a[]=1&a[]=1&…這種字段,會導致哈希碰撞產生一個超大鏈表,從而導致效率嚴重下降
*詳情可以參考我以前發表的關於PHP哈希表碰撞攻擊的內容
3.2 php-5的PHP解析multipart/form-datahttp請求的body part請求頭時,重複拷貝字符串導致DOS。遠程攻擊者通過發送惡意構造的multipart/form-data請求,導致服務器CPU資源被耗盡,從而遠程DOS服務器。
*此bugs比較新,是今年年中的bugs,估計還存在大量的服務器並沒有安裝補丁(可以參考我之前發表的PHP5的嚴重bug, 其實就是一個沒有換行符導致的慘劇)
3.3 如果有關注PHP發展的,會知道很多很多這類型的, 人無完人,要根據具體的日誌和問題,有可能還能發現並提出新的bug給PHP呢
四,系統優化方面
4.1 不是進程開得越多越好; 拿着16核的CPU, 開着100多個PHP-FPM進程+100多個nginx進程, 純屬裝逼; cpu在切換進程的時候不用力嗎?
4.2 如果代碼和系統的優化都達到最佳狀態;可以考慮使用xcache或者apc等進行opcode優化,從而不需要每次都去解析PHP腳本,直接緩存中間代碼,可以提升數十倍的性能

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