今天早上,收到一個報警,有個服務器的http往返時延飆升,同時曝出大量404,很是折騰了一番,特記錄下思考和排查經過。
1.這是單純的時延增大,還是有什么其他情況還未掌握?
因為不知道是只有時延變大而已,還是同時有別的情況,第一反應是先看日志有沒有異常。
看了一下,一片風平浪靜,既是好消息也是壞消息。好消息是核心業務還在,不然一定會打日志,壞消息是日志提供不了任何信息。當然這也說明了我們的日志肯定有不到位的地方。
2.換個思路,日志風平浪靜,是否只是服務器啟動了什么任務,占用了大量cpu/IO等?GC呢?
於是去看監控,服務器CPU利用率、磁盤利用率、內存利用率、Swap交換次數都很正常。
那么會不會是一次長時間的FGC導致請求大量堆積了呢?又去看gc,結果發現也很正常,這段時間連fgc都沒有觸發過,minorGC的時間也在合理范圍內。此路不通
3.再換個思路,往返時延增大,又沒有全部404,看起來像是服務器處理不過來了,那么既然服務器資源充足,為什么處理不過來了呢?是不是tcp的問題
於是去查tcp連接和端口,果然發現了一點端倪,服務器上有大量的close_wait。熟悉tcp的人應該知道,close_wait是tcp連接時,被動關閉的一方會產生的狀態。所以往返時延增大就有了一個合理的解釋:大量處於close_wait的未關閉socket無法被釋放,導致tomcat的可用連接非常少,從而請求堆積,往返時延增大,甚至超時。
4.繼續思考,為何有大量的close_wait?
通常情況下,可能是程序員沒有關閉socket,我們的項目里不存在這種情況。那么,目前最大的可能是:請求阻塞在什么地方了,客戶端已經超時發送fin,所以服務端就變成了close_wait,在等待請求執行完之后才能切換狀態。TCP的狀態切換是排錯基本功,同學們一定要掌握啊!
5.被阻塞的請求阻塞在了什么地方?
這個其實比較好處理,因為通常情況下,阻塞發生在IO處。再順一下業務邏輯,最大的嫌疑是數據庫。查一下sql執行時間,發現一條簡單的select 1 from dual,執行時間都非常長。那就好解釋了,sql執行太慢,連接池連接耗盡,后續請求只能阻塞。打電話給運維,運維:啊?我剛剛做表遷移來着,忘了告訴你們......我:*%&&@*@@&&¥&……()*
6.小插曲,sql超時是會報異常的,為何日志里沒有報警呢?
答案是,服務器的主業務壓根不走數據庫,丫只是因為可用連接太少了所以才時延上升。走數據庫的那個鏈接應該是報了異常的,只是有位大仙把測試時的日志輸出到console的設置覆蓋了線上輸出到文件的設置...