問題描述
公司的支付系統(tǒng)架曹,支付核心調(diào)用網(wǎng)關(guān)系統(tǒng)時(shí)候裙顽,Tomcat接口服務(wù)偶爾會(huì)出現(xiàn)連接超時(shí)蛙婴,以下為問題現(xiàn)象:
- 出現(xiàn)連接超時(shí)的時(shí)間點(diǎn),該機(jī)器仍有正常的請(qǐng)求到網(wǎng)關(guān)系統(tǒng)舔糖,只是部分請(qǐng)求異常
- 偶發(fā)性質(zhì)娱两,一天或2天一次,且出現(xiàn)連接超時(shí)的時(shí)間段比較短暫
初步排查分析
- JVM GC情況:正常金吗,分析GC日志十兢,在發(fā)生連接超時(shí)時(shí)間段,系統(tǒng)只發(fā)生過幾次YGC摇庙,每次STW時(shí)間在0.02 sec 左右
- 系統(tǒng)資源:CPU旱物、I/O、磁盤使用率和平常一樣卫袒,無異常
- Tomcat 線程數(shù):在出現(xiàn)連接超時(shí)的時(shí)間段宵呛,活動(dòng)線程數(shù)增長(zhǎng)了100多個(gè),當(dāng)仍遠(yuǎn)低于1500最大工作線程數(shù)夕凝。
-
檢查網(wǎng)絡(luò)宝穗,發(fā)下在出現(xiàn)連接超時(shí)的時(shí)間段,TCP全連接隊(duì)列發(fā)生了溢出
image.png
PS:只是臨時(shí)找了個(gè)類似的圖迹冤,當(dāng)時(shí)時(shí)間的溢出圖沒找著
基于以上分析讽营,初步懷疑是TCP全連接隊(duì)列溢出導(dǎo)致出現(xiàn)了客戶端連接超時(shí)的情況
接著查看溢出后,OS怎么處理:
# cat /proc/sys/net/ipv4/tcp_abort_on_overflow
0
tcp_abort_on_overflow 為0表示如果三次握手第三步的時(shí)候全連接隊(duì)列滿了那么server扔掉client 發(fā)過來的ack(在server端認(rèn)為連接還沒建立起來)
接著檢查服務(wù)端的全連接隊(duì)列長(zhǎng)度泡徙,發(fā)現(xiàn)僅為100。 而出現(xiàn)連接超時(shí)情況時(shí)膜蠢,全連接隊(duì)列已經(jīng)堆積了超過100個(gè)請(qǐng)求
采用ss -lnt | grep {server_port} 查看
[root@localhost ~]# ss -lnt | grep 10007
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 100 :::10007 :::*
解釋一下上訴Recv-Q Send-Q的含義:
- LISTEN 狀態(tài): Recv-Q 表示的當(dāng)前等待服務(wù)端調(diào)用 accept 完成三次握手的 listen backlog 數(shù)值堪藐,也就是說,當(dāng)客戶端通過 connect() 去連接正在 listen() 的服務(wù)端時(shí)挑围,這些連接會(huì)一直處于這個(gè) queue 里面直到被服務(wù)端 accept()礁竞;Send-Q 表示的則是最大的 listen backlog 數(shù)值
- 非LISTEN狀態(tài):
recv-Q 表示網(wǎng)絡(luò)接收隊(duì)列,表示收到的數(shù)據(jù)已經(jīng)在本地接收緩沖杉辙,但是還有多少?zèng)]有被進(jìn)程取走
send-Q 表示網(wǎng)路發(fā)送隊(duì)列
對(duì)方?jīng)]有收到的數(shù)據(jù)或者說沒有Ack的,還是本地緩沖區(qū).
如果發(fā)送隊(duì)列Send-Q不能很快的清零模捂,可能是有應(yīng)用向外發(fā)送數(shù)據(jù)包過快,或者是對(duì)方接收數(shù)據(jù)包不夠快蜘矢。
檢查tomcat的配置的backlog狂男,確實(shí)默認(rèn)的是100,關(guān)鍵代碼如下:
NioEndpoint.java
serverSock.socket().bind(addr,getAcceptCount());
/**
* Allows the server developer to specify the acceptCount (backlog) that
* should be used for server sockets. By default, this value
* is 100.
*/
private int acceptCount = 100;
public void setAcceptCount(int acceptCount) { if (acceptCount > 0) this.acceptCount = acceptCount; }
public int getAcceptCount() { return acceptCount; }
測(cè)試模擬
經(jīng)過上訴分析品腹,已經(jīng)初步懷疑是由于tomcat backlog配置過小導(dǎo)致的客戶端出現(xiàn)連接超時(shí)岖食,在測(cè)試環(huán)境進(jìn)行模擬(系統(tǒng)TCP相關(guān)參數(shù)與生產(chǎn)環(huán)境一致)
- 將tomcat的backlog配置調(diào)小至1:(max-connections默認(rèn)10000,max-threads默認(rèn)1500)
-Dserver.tomcat.accept-count=1
- 服務(wù)端接口:
@ResponseBody
@RequestMapping("/tomcatTest/{sleepSeconds}")
public String testSMS(@PathVariable("sleepSeconds") long sleepMS) {
try {
TimeUnit.SECONDS.sleep(sleepMS);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "OK";
}
并發(fā)10個(gè)http請(qǐng)求(連接超時(shí)時(shí)間配置為3s舞吭,服務(wù)端處理時(shí)長(zhǎng)為10s)泡垃,出現(xiàn)3個(gè)連接超時(shí)的情況
第3次異常:I/O error on GET request for "http://xxxx:19966/tomcatTest/10": connect timed out; nested exception is java.net.SocketTimeoutException: connect timed out exec: 3001
第5次異常:I/O error on GET request for "http://xxxx:19966/tomcatTest/10": connect timed out; nested exception is java.net.SocketTimeoutException: connect timed out exec: 3001
第4次異常:I/O error on GET request for "http://xxxx:19966/tomcatTest/10": connect timed out; nested exception is java.net.SocketTimeoutException: connect timed out exec: 3003
第0次正常 exec: 10020
第1次正常 exec: 10019
第7次正常 exec: 10018
第2次正常 exec: 10023
第9次正常 exec: 10020
第8次正常 exec: 10022
第6次正常 exec: 10327
在執(zhí)行期間觀察全連接隊(duì)列情況析珊,確實(shí)有出現(xiàn)溢出
netstat -s | grep -i "listen"
366 times the listen queue of a socket overflowed
366 SYNs to LISTEN sockets ignored
netstat -s | grep -i "listen"
368 times the listen queue of a socket overflowed
368 SYNs to LISTEN sockets ignored
將tomcat backlog調(diào)大后,在繼續(xù)壓測(cè)蔑穴,沒有在出現(xiàn)連接超時(shí)的情況忠寻。
依此測(cè)試結(jié)果,將線上的tomcat backlog增大至500后存和,觀察一周均無在出現(xiàn)connect timeout的情況奕剃。
未完待續(xù)
- 疑惑1:按道理全連接隊(duì)列滿了,但是客戶端的連接請(qǐng)求是已經(jīng)接收到SYN+ACK了哑姚,所以對(duì)于客戶端來說該連接已經(jīng)建立了祭饭,為啥會(huì)報(bào)connect timeout ? 應(yīng)該是read timeout或者connect reset 。
- 疑惑2:全連接隊(duì)列滿了叙量,但客戶端的請(qǐng)求認(rèn)為連接ESTABLISH狀態(tài)倡蝙,可以繼續(xù)發(fā)送數(shù)據(jù)請(qǐng)求。這時(shí)候服務(wù)端如何處理绞佩?
帶著這2個(gè)問題寺鸥,后續(xù)將會(huì)模擬測(cè)試全連接隊(duì)列滿的情況,通過TCPDUMP抓包觀察下