nginx與php的WEB常見問題排查

nginx與php的WEB常見問題排查
 

nginx與php的WEB常見問題一般的排查方法有:檢查error_log,檢查access_log,使用strace查看系統調用,tcpdump分析網絡狀況。而程序本身的問...

nginx與php的WEB常見問題一般的排查方法有:
檢查error_log,檢查access_log,使用strace查看系統調用,tcpdump分析網絡狀況。而程序本身的問題,可能需要用到gdb調試。

 

error_log提供了有關異常的豐富信息,大多數情況下可以發現問題的蛛絲馬跡。比如:connect,write,read timeout等網絡超時錯誤,比如Permission denied, File not found等系統調用錯誤,比如400,499,500等對應的HTTP狀態碼錯誤。

舉例來講,更新nginx後,http請求不能返回完整數據,返回部分數據後請求就結束了,每次都能復現。這時候查看error_log,看到有Permission denied錯誤,即查實爲nginx的臨時寫目錄沒有權限所造成。


access_log則提供了所有客戶端訪問流水的日誌,而且可以靈活定義日誌的格式與內容,包括日誌翻轉的設定。比如查看到後端響應的時間,後端返回狀態碼。

對access_log進行統計分析,可以很好地展示與監控web服務的狀態。包括200請求次數的變化,流量大小,後端響應時間等等。一般地講:
可以定義access_log每5分鐘翻轉1次;
統計HTTP狀態碼的比例,可以知道nginx服務健康狀況;
統計響應時間,判斷超時請求。(根據經驗,如果響應時間分佈集中在某個數字,並且標準差很小,則可能是達到超時)
統計QPS,對比負載是否均衡。


舉例:在一次nginx應用更新的灰度過程中,我們選取一臺nginx上線,觀察接下來的半小時內,nginx佔用內存不斷增大,直到耗盡系統資源。排查error_log中並沒有錯誤信息;監控發現新上線的nginx流量大增;分析access_log發現新上線的nginx的QPS是原機器的6~8倍。證實是Keepalived導致流量負載不均衡。LVS作爲負載均衡時,後端的nignx配置的keepalive_timeout時間有差異導致。


LVS的負載均衡是TCP層的,只能做連接的均衡,如果Keepalive時間設置的超長,會導致路由到這個連接的請求越多。


access_log排查舉例2:nginx做反向代理,有時發現某些接口較慢,通常都在3s左右。
配置nginx的access log記錄upstream_response_time,分析發現響應大約3s多。而分析後端的access_log,處理時間都在毫秒級別。這說明問題是由於nginx跟後端connect較慢導致。

使用strace命令工具可跟蹤系統調用並打印出豐富的信息,如系統調用發生的時間,調用耗時,傳送的參數,調用返回結果等等。
常用參數有:
-tt 打印系統調用發生的時間
-T 打印系統調用耗時
-e <name> 跟蹤特定系統調用,比如-e open,read,write,close來追蹤文件相關調用
-s <size> 指定打印的最大長度

strace舉例:某個PHP進程hang住,其pid爲26879,那麼可以使用strace -p 26879來觀察。
示例顯示,futex表示很可能發生死鎖,strace打印出其參數值。


某次排查PHP問題時,發現有時PHP處理請求過慢,就可用strace來追查驗證系統調用耗時。結果strace -T就發現是flock調用過慢。


而另外一次,朋友線上出了故障,系統負載特別高,load到200多,CPU使用率也到90%的,特別高,關鍵是使用strace追查並沒有發現什麼問題。這裏要說明一下,strace只能追蹤系統調用,普通函數是無法追蹤的。CPU很高,說明程序一直在跑,但strace沒有異常,說明是用戶狀代碼在消耗CPU。問題必定出現在自己程序邏輯上。
最終證實是系統上線更換配置文件,導致程序邏輯出錯。


tcpdump是linux本身得供的一款強大的網絡抓包工具。對於兩臺設備之間傳輸的數據是否正常,爲何響應慢等網絡問題,都可以使用tcpdump來抓包排查。
windows下面有一款具有同樣功能的工具軟件--wireshark,也很好用。


還記得剛纔示例中提到的慢連接吧,Nginx的access_log顯示請求響應時間爲3s多,而後端是毫秒計,這種慢就可以使用tcpdump來抓包查看了。


可以看到後端響應ack花了3s.這種慢連接,我們線上會經常碰到。通常nginx做反向代量連接後端時、php程序在訪問後端資源時、以及php用curl請求其他接口時,經常出現慢連接的情況。這些慢連接產生的根本原因在於:
--服務端listen時,設置的backlog太小,導致連接隊列很小;
--連接隊列滿時,對於新的連接請求,服務端會直接丟棄SYN包;
--SYN包初始重傳時間爲3s;
 


再舉一下綜合案例:PHP升級後,開始運行正常,但幾天後,系統負載突然上升,達到200-400左右,CPU使用不高,內存使用不高,netstat發現大量的PHP進程處於CLOSE_WAIT狀態。
排查:error_log與access_log都沒有問題;
nginx與PHP不在同一臺機器,暫時無法查看其error_log;
CPU、內存都不高,爲何負載這麼高?
CLOSE_WAIT是怎麼造成的?


我們先從CLOSE_WAIT入手,TCP關閉連接過程中,被動關閉的一方,在接收到對方的FIN後,發送自己的FIN前,這個狀態就是CLOSE_WAIT。
系統調用close,關閉連接,發送FIN。


從CLOSE_WAIT的狀態看,PHP應該是沒有調用close函數,程序可能因爲某種原因堵塞,而無法調用close。
啓用strace追蹤PHP進程到底堵塞在哪裏。


從strace追蹤結果來看,PHP進程沒有堵塞,但write函數調用失敗,Broken pipe說明連接已經關閉,調用close了。


繼續strace分析,連接爲什麼關閉,是後端PHP處理太慢導致nginx超時麼?從strace看到,PHP從accept到close,總共耗時1ms,nginx不可能超時。


我們啓動tcpdump抓包(windows下使用wireshark查看)


發現3個異常:
--3次握手nginx發送的SYN,PHP響應是ACK,而不是SYN+ACK
--Nginx發送FIN關閉連接後,PHP沒有發送FIN
--第二個連接的SYN,PHP同樣只返回ACK,但ACK序列號卻是確認上一個連接的。


結合strace與tcpdump來看,accept調用比接收到SYN晚了2分鐘。


這裏可以看到:SYN三次握手完成後,socket放到了連接隊列裏,accept從連接隊列獲取socket,如果隊列過大,等到accept消費到這個socket,可能已經超時關閉連接。隊列中關閉的連接處於CLOSE_WAIT狀態。若PHP一個關閉的連接,就會出現Broken Pipe報錯現象。
這其實就是backlog設置過大引起。

 


總結一下,web問題排障很複雜,找到正確的方向很重要。學會看log與系統狀態,學會使用統計數據(常用的一些awk,grep命令),熟悉一些常用的工具。
問題一定是有原因的,要找到root cause(真正的根本原因)


最後,常常總結分享,共同進步。

發佈了1 篇原創文章 · 獲贊 2 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章