Linux協(xié)議棧accept和syn隊(duì)列問(wèn)題
環(huán)境:
本文引用地址:http://m.butianyuan.cn/article/201610/305659.htmClient 通過(guò)tcp 連接server,server端只是listen,但是不調(diào)用accept。通過(guò)netstat –ant查看兩端的連接情況。
server端listen,不調(diào)用accept。
client一直去connect server。
問(wèn)題:
運(yùn)行一段時(shí)間后,為什么server端的ESTABLISHED連接的個(gè)數(shù)基本是固定的129個(gè),但是client端的ESTABLISHED連接的個(gè)數(shù)卻在不斷增加?
分析
Linux內(nèi)核協(xié)議棧為一個(gè)tcp連接管理使用兩個(gè)隊(duì)列,一個(gè)是半鏈接隊(duì)列(用來(lái)保存處于SYN_SENT和SYN_RECV狀態(tài)的請(qǐng)求),一個(gè)是accpetd隊(duì)列(用來(lái)保存處于established狀態(tài),但是應(yīng)用層沒(méi)有調(diào)用accept取走的請(qǐng)求)。
第一個(gè)隊(duì)列的長(zhǎng)度是/proc/sys/net/ipv4/tcp_max_syn_backlog,默認(rèn)是1024。如果開(kāi)啟了syncookies,那么基本上沒(méi)有限制。
第二個(gè)隊(duì)列的長(zhǎng)度是/proc/sys/net/core/somaxconn,默認(rèn)是128,表示最多有129個(gè)established鏈接等待accept。(為什么是129?詳見(jiàn)下面的附錄1)。
現(xiàn)在假設(shè)acceptd隊(duì)列已經(jīng)達(dá)到129的情況:
client發(fā)送syn到server。client(SYN_SENT),server(SYN_RECV)
server端處理流程:tcp_v4_do_rcv--->tcp_rcv_state_process--->tcp_v4_conn_request
if(sk_acceptq_is_full(sk) inet_csk_reqsk_queue_yong(sk)>1)
goto drop;
inet_csk_reqsk_queue_yong(sk)的含義是請(qǐng)求隊(duì)列中有多少個(gè)握手過(guò)程中沒(méi)有重傳過(guò)的段。
在第一次的時(shí)候,之前的握手過(guò)程都沒(méi)有重傳過(guò),所以這個(gè)syn包server端會(huì)直接drop掉,之后client會(huì)重傳syn,當(dāng)inet_csk_reqsk_queue_yong(sk) 1,那么這個(gè)syn被server端接受。server會(huì)回復(fù)synack給client。這樣一來(lái)兩邊的狀態(tài)就變?yōu)閏lient(ESTABLISHED), server(SYN_SENT)
Client收到synack后回復(fù)ack給server。
server端處理流程: tcp_check_req--->syn_recv_sock-->tcp_v4_syn_recv_sock
if(sk_acceptq_is_full(sk)
goto exit_overflow;
如果server端設(shè)置了sysctl_tcp_abort_on_overflow,那么server會(huì)發(fā)送rst給client,并刪除掉這個(gè)鏈接;否則server端只是記錄一下LINUX_MIB_LISTENOVERFLOWS(詳見(jiàn)附錄2),然后返回。默認(rèn)情況下是不會(huì)設(shè)置的,server端只是標(biāo)記連接請(qǐng)求塊的acked標(biāo)志,之后連接建立定時(shí)器,會(huì)遍歷半連接表,重新發(fā)送synack,重復(fù)上面的過(guò)程(具體的函數(shù)是inet_csk_reqsk_queue_prune),如果重傳次數(shù)超過(guò)synack重傳的閥值(/proc/sys/net/ipv4/tcp_synack_retries),會(huì)把該連接從半連接鏈表中刪除。
一次異常問(wèn)題分析
Nginx通過(guò)FASTCGI協(xié)議連接cgi程序,出現(xiàn)cgi程序read讀取socket內(nèi)容的時(shí)候永遠(yuǎn)block。通過(guò)netstat查看,cgi程序所在的服務(wù)器上顯示連接存在,但是nginx所在的服務(wù)器上顯示不存在該連接。
下面是原始數(shù)據(jù)圖:
我們從上面的數(shù)據(jù)流來(lái)分析一下:
出現(xiàn)問(wèn)題的時(shí)候,cgi程序(tcp server端)處理非常慢,導(dǎo)致大量的連接請(qǐng)求放到accept隊(duì)列,把a(bǔ)ccept隊(duì)列阻塞。
148021 nginx(tcp client端) 連接cgi程序,發(fā)送syn
此時(shí)server端accpet隊(duì)列已滿,并且inet_csk_reqsk_queue_yong(sk) > 1,server端直接丟棄該數(shù)據(jù)包
148840 client端等待3秒后,重傳SYN
此時(shí)server端狀態(tài)與之前送變化,仍然丟棄該數(shù)據(jù)包
150163 client端又等待6秒后,重傳SYN
此時(shí)server端accept隊(duì)列仍然是滿的,但是存在了重傳握手的連接請(qǐng)求,server端接受連接請(qǐng)求,并發(fā)送synack給client端(150164)
150166 client端收到synack,標(biāo)記本地連接為ESTABLISHED狀態(tài),給server端應(yīng)答ack,connect系統(tǒng)調(diào)用完成。
Server收到ack后,嘗試將連接放到accept隊(duì)列,但是因?yàn)閍ccept隊(duì)列已滿,所以只是標(biāo)記連接為acked,并不會(huì)將連接移動(dòng)到accept隊(duì)列中,也不會(huì)為連接分配sendbuf和recvbuf等資源。
150167 client端的應(yīng)用程序,檢測(cè)到connect系統(tǒng)調(diào)用完成,開(kāi)始向該連接發(fā)送數(shù)據(jù)。
Server端收到數(shù)據(jù)包,由于acept隊(duì)列仍然是滿的,所以server端處理也只是標(biāo)記acked,然后返回。
150225 client端由于沒(méi)有收到剛才發(fā)送數(shù)據(jù)的ack,所以會(huì)重傳剛才的數(shù)據(jù)包
150296 同上
150496 同上
150920 同上
151112 server端連接建立定時(shí)器生效,遍歷半連接鏈表,發(fā)現(xiàn)剛才acked的連接,重新發(fā)送synack給client端。
151113 client端收到synack后,根據(jù)ack值,使用SACK算法,只重傳最后一個(gè)ack內(nèi)容。
Server端收到數(shù)據(jù)包,由于accept隊(duì)列仍然是滿的,所以server端處理也只是標(biāo)記acked,然后返回。
151896 client端等待3秒后,沒(méi)有收到對(duì)應(yīng)的ack,認(rèn)為之前的數(shù)據(jù)包也丟失,所以重傳之前的內(nèi)容數(shù)據(jù)包。
152579 server端連接建立定時(shí)器生效,遍歷半連接鏈表,發(fā)現(xiàn)剛才acked的連接,synack重傳次數(shù)在閥值以內(nèi),重新發(fā)送synack給client端。
152581 cient端收到synack后,根據(jù)ack值,使用SACK算法,只重傳最后一個(gè)ack內(nèi)容。
Server端收到數(shù)據(jù)包,由于accept隊(duì)列仍然是滿的,所以server端處理也只是標(biāo)記acked,然后返回
153455 client端等待3秒后,沒(méi)有收到對(duì)應(yīng)的ack,認(rèn)為之前的數(shù)據(jù)包也丟失,所以重傳之前的內(nèi)容數(shù)據(jù)包。
155399 server端連接建立定時(shí)器生效,遍歷半連接鏈表,發(fā)現(xiàn)剛才acked的連接,synack重傳次數(shù)在閥值以內(nèi),重新發(fā)送synack給client端。
155400 cient端收到synack后,根據(jù)ack值,使用SACK算法,只重傳最后一個(gè)ack內(nèi)容。
Server端收到數(shù)據(jù)包,由于accept隊(duì)列仍然是滿的,所以server端處理也只是標(biāo)記acked,然后返回。
156468 client端等待幾秒后,沒(méi)有收到對(duì)應(yīng)的ack,認(rèn)為之前的數(shù)據(jù)包也丟失,所以重傳之前的內(nèi)容數(shù)據(jù)包。
161309 server端連接建立定時(shí)器生效,遍歷半連接鏈表,發(fā)現(xiàn)剛才acked的連接,synack重傳次數(shù)在閥值以內(nèi),重新發(fā)送synack給client端。
評(píng)論