1. 程式人生 > >Tomcat7.0.26的連線數控制bug的問題排查

Tomcat7.0.26的連線數控制bug的問題排查

感謝同事[空濛]的投稿。

首先感謝@烈元一起排查此問題。今天發現線上一臺機器,監控一直在告警,一看是健康檢查不通過,就上去查看了下,首先自己curl了下應用的url,果然是超時沒有響應,那就開始按順序排查了:

1、 load非常低,2、gc也正常,3、執行緒上也沒死鎖,4、日誌一切正常。那是什麼情況呢,不能忘記網路啊。果然,netstat命令一把,結果如下:

TIME_WAIT 68
CLOSE_WAIT 194
ESTABLISHED 3941
SYN_RECV 100

問題出來了,SYN_RECV竟然達到100個,正常情況下,半連線的請求應該是很小的。而且我們機器是內部的,不是lvs,不太會有半連線攻擊,怎麼可能達到這麼大呢?

再grep SYN_RECV的連線,看到全部都是nginx在連線這臺mtop機器,那接下來就dump tcp包看看了

image001
一看一堆堆的wjas向mtop發起SYN連線請求,可是mtop機器是絕大部分沒回應,只有極少的mtop機器syn+ack包。
注:wjas一天向mtop發起了近20億的健康檢查請求,夠多的,所以沒有外部流量時,還是有一大堆的http請求到應用上。

看系統資訊,核心是2.6.32-220.23.2.ali1113.el5.x86_64的,半連線佇列的長度是128不同的核心,半連線佇列長度演算法稍有不同,可以參考文章:linux詭異的半連線(SYN_RECV)佇列長度

可見是mtop半連線佇列滿了,不再接受新的tcp連線,導致請求沒有響應了,但應用其實很空閒。

問題表現很清楚了,接下來就是各種懷疑了,因為這機器為解決之前tcnative的crash bug,剛切換成了nio模式、又昨天我手賤,在上面搞過btrace,aliperf。自己也成嫌疑,但這是半連線佇列滿,要麼受到半連線攻擊(排除,內網不太可能有半連線攻擊),要麼是Accept執行緒沒有及時處理,應用沒有接收連線的請求,導致三次握手後的佇列連線滿再引起半連線佇列滿了。(總結起來好像很有邏輯,排查的時候沒那麼明確的,還在想各種可能)

我們知道,tomcat有一個Acceptor執行緒,監聽在埠上,在收到連線請求後,會立刻把請求交個後面執行緒池處理,bio是直接拿執行緒等待資料,nio與apr會在poller執行緒上註冊監聽,也就是select模式,底層再基於epoll事件觸發(和nginx的處理模式有點區別)。那就是這個Acceptor執行緒難道停止了。

檢視堆疊資訊,果然

image002當前的這個acceptor已經被禁用了,需要喚醒,多次dump執行緒,發現此執行緒一直是這個狀態,這就解釋了為什麼了。

馬上檢視tomcat原始碼,發現此程式碼是在tomcat的連線數(nio)達到1w的時候,會park當前執行緒,再請求處理後,會再喚醒,繼續接受新的連線,Btrace了一把,果然這個連線數值是1w,但什麼情況下,會導致這個值那麼大,一直把執行緒暫停呢?按說如果要達到這麼大的連線,我們的T4機器早就雞飛狗跳了。

Google一把,原來是tomcat7.0.27之前的bug,我們使用的剛好是7.0.26.中槍了,不管是nio,bio,apr,都存在這個問題。Tomcat的程式碼如下:

image003-3當接受連線,出現異常時候,舊版本沒有把這個陣列減少,這時候就拼人品了,如果異常的請求累積,達到連線的最大值,就發生機器很閒,但tcp的連線佇列與半連線佇列滿的情況了

1、如有遇到此類似情況,可看看是否這原因。
2、當最新版的jar或者容器穩定後,早點升級吧,特別是bug修復。
3、提供一次問題排查的參考。


sunqi

花名空濛,阿里-無線事業部-資深開發工程師。目前負責MTOP的設計和開發工作。