最近觀察到日志上偶現(xiàn)一個(gè)錯(cuò)誤:
feign.RetryableException: connect timed out executing POST http://service_name/a/b
java.net.SocketTimeoutException: connect timed out
該問(wèn)題很清晰,就是發(fā)起http請(qǐng)求時(shí),構(gòu)建TCP連接時(shí)發(fā)生了超時(shí)問(wèn)題。
由于是偶現(xiàn)的,頻率不高所以一開(kāi)始沒(méi)有足夠重視,直到上了k8s之后頻率頗高。一開(kāi)始沒(méi)有把這個(gè)問(wèn)題聚焦到配置ribbon.ConnectionTimeout
參數(shù)上,因?yàn)槟J(rèn)使用的統(tǒng)一配置,沒(méi)成想居然這里有個(gè)小坑。
為了排查該問(wèn)題,我找到運(yùn)維同學(xué)在生產(chǎn)環(huán)境低峰期通過(guò)tcpdump
命令進(jìn)行抓包,參考命令如下:tcpdump -i eth0 dst host \( ....(IP地址).. \) and port 5000 -w /root/connect_timeout.cap &
,由于是偶現(xiàn)的問(wèn)題,所以抓包過(guò)程時(shí)間較長(zhǎng),所幸在低峰期出現(xiàn)了一次。在幾百兆的里找哪些有問(wèn)題的包,確實(shí)不好找。
好在我們異常調(diào)用棧了打印了案發(fā)現(xiàn)場(chǎng)的時(shí)間:2021-07-05 09:20:35.954 ERROR 1 --- [nio-5000-exec-3]
使用wireshake
前,我調(diào)整了兩個(gè)配置項(xiàng):
1、修改View -> Time Diaplay Formate
,選擇Date and Time of Day
2、修改Preferences -> Protocols -> TCP
,去掉Relative sequence number
通過(guò)時(shí)間找到了兩個(gè)非常可疑的包,如下:
為何說(shuō)這兩個(gè)包可疑:
1、它的時(shí)間跟異常調(diào)用棧的時(shí)間很接近,一秒左右的時(shí)間。
2、它只有SYN
包,其他包沒(méi)有,一個(gè)正常的http請(qǐng)求必須是完整的三次握手在前。
3、它向服務(wù)方發(fā)送了RST
包。
胡思亂想:
1、在34秒的時(shí)候發(fā)出SYN
包,之后等待服務(wù)端響應(yīng)ACK
包
2、在1秒后由于沒(méi)有收到ACK
包,所以響應(yīng)了ConnectinTimeout
超時(shí)?要滿足這里,應(yīng)用程序需要將ConnectinTimeout
設(shè)置為1秒
3、在超時(shí)異常后,客戶端終止了本次TCP
連接的建立,但是此時(shí)收到了服務(wù)端的ACK
包。(由于運(yùn)維同學(xué)的命令寫的有問(wèn)題,導(dǎo)致沒(méi)有把服務(wù)端的ACK
包抓下來(lái),去掉dst即可)
4、客戶端收到這個(gè)包之后,由TCP協(xié)議棧回復(fù)了服務(wù)端一個(gè)RST
包終止連接的建立。
那么ConnectinTimeout
導(dǎo)致配置了多少?程序上找了半天也沒(méi)有找到相關(guān)的配置,只找到一個(gè)配置項(xiàng)如下ribbon.ReadTimeout: 2000
,那么默認(rèn)的ConnectinTimeout
是多少?沒(méi)成想居然真的是1s,如下圖:
猜測(cè)
將ConnectinTimeout
上調(diào)5s將會(huì)出現(xiàn)什么情況? 理論上在建立TCP連接時(shí)發(fā)送SYN
將會(huì)由于超時(shí)而重復(fù)發(fā)送,重試次數(shù)配置參數(shù)為net.ipv4.tcp_syn_retries
,我們的機(jī)器默認(rèn)是6,而重試的是具備時(shí)間間隔的,大致為【1,3,7,15,31,63】
,間隔為s。
- 所以如果
SYN
持續(xù)丟包,最多將會(huì)見(jiàn)到6個(gè)TCP Retransmission
- 綜合當(dāng)前的情況來(lái)看,可能會(huì)在1s, 之后收到來(lái)自服務(wù)端的
ACK
包,此時(shí)即可完成連接的建立, - 如果未收到則大概會(huì)在第二次發(fā)出重復(fù)
SYN
包后超時(shí),之后再收到服務(wù)端ACK
將會(huì)回復(fù)RST
包
最終我將
ConnectiTimeout
調(diào)整為3s
,這兩天抓包觀察,偶爾可以看到一個(gè)重發(fā)的syn
包,與猜測(cè)基本一致。