關(guān)于TCP 半連接隊列和全連接隊列

轉(zhuǎn)載:關(guān)于TCP 半連接隊列和全連接隊列

最近碰到一個client端連接異常問題,然后定位分析并查閱各種資料文章镀梭,對TCP連接隊列有個深入的理解

查資料過程中發(fā)現(xiàn)沒有文章把這兩個隊列以及怎么觀察他們的指標(biāo)說清楚令野,希望通過這篇文章能把他們說清楚一點

問題描述

JAVA的client和server贮勃,使用socket通信魔眨。server使用NIO。

1.間歇性的出現(xiàn)client向server建立連接三次握手已經(jīng)完成底扳,但server的selector沒有響應(yīng)到這連接铸抑。

2.出問題的時間點,會同時有很多連接出現(xiàn)這個問題衷模。

3.selector沒有銷毀重建鹊汛,一直用的都是一個蒲赂。

4.程序剛啟動的時候必會出現(xiàn)一些,之后會間歇性出現(xiàn)刁憋。

分析問題

正常TCP建連接三次握手過程:

image.png

第一步:client 發(fā)送 syn 到server 發(fā)起握手滥嘴;

第二步:server 收到 syn后回復(fù)syn+ack給client;

第三步:client 收到syn+ack后职祷,回復(fù)server一個ack表示收到了server的syn+ack(此時client的56911端口的連接已經(jīng)是established)

從問題的描述來看氏涩,有點像TCP建連接的時候全連接隊列(accept隊列)滿了,尤其是癥狀2有梆、4. 為了證明是這個原因,馬上通過 ss -s 去看隊列的溢出統(tǒng)計數(shù)據(jù):

667399 times the listen queue of a socket overflowed

反復(fù)看了幾次之后發(fā)現(xiàn)這個overflowed 一直在增加意系,那么可以明確的是server上全連接隊列一定溢出了

接著查看溢出后泥耀,OS怎么處理:

# cat /proc/sys/net/ipv4/tcp_abort_on_overflow

0

tcp_abort_on_overflow 為0表示如果三次握手第三步的時候全連接隊列滿了那么server扔掉client 發(fā)過來的ack(在server端認(rèn)為連接還沒建立起來)

為了證明客戶端應(yīng)用代碼的異常跟全連接隊列滿有關(guān)系,我先把tcp_abort_on_overflow修改成 1蛔添,1表示第三步的時候如果全連接隊列滿了痰催,server發(fā)送一個reset包給client,表示廢掉這個握手過程和這個連接(本來在server端這個連接就還沒建立起來)迎瞧。

接著測試然后在客戶端異常中可以看到很多connection reset by peer的錯誤夸溶,到此證明客戶端錯誤是這個原因?qū)е碌摹?/p>

于是開發(fā)同學(xué)翻看java 源代碼發(fā)現(xiàn)socket 默認(rèn)的backlog(這個值控制全連接隊列的大小,后面再詳述)是50凶硅,于是改大重新跑缝裁,經(jīng)過12個小時以上的壓測,這個錯誤一次都沒出現(xiàn)過足绅,同時 overflowed 也不再增加了捷绑。

到此問題解決,簡單來說TCP三次握手后有個accept隊列氢妈,進(jìn)到這個隊列才能從Listen變成accept粹污,默認(rèn)backlog 值是50,很容易就滿了首量。滿了之后握手第三步的時候server就忽略了client發(fā)過來的ack包(隔一段時間server重發(fā)握手第二步的syn+ack包給client)壮吩,如果這個連接一直排不上隊就異常了。

深入理解TCP握手過程中建連接的流程和隊列

(圖片來源:http://www.cnxct.com/something-about-phpfpm-s-backlog/)

如上圖所示加缘,這里有兩個隊列:syns queue(半連接隊列)鸭叙;accept queue(全連接隊列)

三次握手中,在第一步server收到client的syn后生百,把相關(guān)信息放到半連接隊列中递雀,同時回復(fù)syn+ack給client(第二步);

比如syn floods 攻擊就是針對半連接隊列的蚀浆,攻擊方不停地建連接缀程,但是建連接的時候只做第一步搜吧,第二步中攻擊方收到server的syn+ack后故意扔掉什么也不做,導(dǎo)致server上這個隊列滿其它正常請求無法進(jìn)來

第三步的時候server收到client的ack杨凑,如果這時全連接隊列沒滿滤奈,那么從半連接隊列拿出相關(guān)信息放入到全連接隊列中,否則按tcp_abort_on_overflow指示的執(zhí)行撩满。

這時如果全連接隊列滿了并且tcp_abort_on_overflow是0的話蜒程,server過一段時間再次發(fā)送syn+ack給client(也就是重新走握手的第二步),如果client超時等待比較短伺帘,就很容易異常了昭躺。

在我們的os中retry 第二步的默認(rèn)次數(shù)是2(centos默認(rèn)是5次):

net.ipv4.tcp_synack_retries = 2

如果TCP連接隊列溢出,有哪些指標(biāo)可以看呢伪嫁?

上述解決過程有點繞领炫,那么下次再出現(xiàn)類似問題有什么更快更明確的手段來確認(rèn)這個問題呢?

netstat -s

[root@server ~]#? netstat -s | egrep "listen|LISTEN"

667399 times the listen queue of a socket overflowed

667399 SYNs to LISTEN sockets ignored

比如上面看到的 667399 times 张咳,表示全連接隊列溢出的次數(shù)帝洪,隔幾秒鐘執(zhí)行下,如果這個數(shù)字一直在增加的話肯定全連接隊列偶爾滿了脚猾。

ss 命令

[root@server ~]# ss -lnt

Recv-Q Send-Q Local Address:Port? Peer Address:Port

0? ? ? ? 50? ? ? ? ? ? ? *:3306? ? ? ? ? ? *:*

上面看到的第二列Send-Q 表示第三列的listen端口上的全連接隊列最大為50葱峡,第一列Recv-Q為全連接隊列當(dāng)前使用了多少

全連接隊列的大小取決于:min(backlog, somaxconn) . backlog是在socket創(chuàng)建的時候傳入的,somaxconn是一個os級別的系統(tǒng)參數(shù)

半連接隊列的大小取決于:max(64, /proc/sys/net/ipv4/tcp_max_syn_backlog)龙助。 不同版本的os會有些差異

實踐驗證下上面的理解

把java中backlog改成10(越小越容易溢出)砰奕,繼續(xù)跑壓力,這個時候client又開始報異常了泌参,然后在server上通過 ss 命令觀察到:

Fri May? 5 13:50:23 CST 2017

Recv-Q Send-QLocal Address:Port? Peer Address:Port

11? ? ? ? 10? ? ? ? *:3306? ? ? ? ? ? ? *:*

按照前面的理解脆淹,這個時候我們能看到3306這個端口上的服務(wù)全連接隊列最大是10,但是現(xiàn)在有11個在隊列中和等待進(jìn)隊列的沽一,肯定有一個連接進(jìn)不去隊列要overflow掉

容器中的Accept隊列參數(shù)

Tomcat默認(rèn)短連接盖溺,backlog(Tomcat里面的術(shù)語是Accept count)Ali-tomcat默認(rèn)是200, Apache Tomcat默認(rèn)100.

#ss -lnt

Recv-Q Send-Q? Local Address:Port Peer Address:Port

0? ? ? 100? ? ? ? ? ? ? ? *:8080? ? ? ? ? ? *:*

Nginx默認(rèn)是511

$sudo ss -lnt

State? Recv-Q Send-Q Local Address:PortPeer Address:Port

LISTEN? ? 0? ? 511? ? ? ? ? ? ? *:8085? ? ? ? ? *:*

LISTEN? ? 0? ? 511? ? ? ? ? ? ? *:8085? ? ? ? ? *:*

因為Nginx是多進(jìn)程模式,也就是多個進(jìn)程都監(jiān)聽同一個端口以盡量避免上下文切換來提升性能

進(jìn)一步思考

如果client走完第三步在client看來連接已經(jīng)建立好了铣缠,但是server上的對應(yīng)連接實際沒有準(zhǔn)備好烘嘱,這個時候如果client發(fā)數(shù)據(jù)給server,server會怎么處理呢蝗蛙?(有同學(xué)說會reset蝇庭,還是實踐看看)

先來看一個例子:

image.png

(圖片來自:http://blog.chinaunix.net/uid-20662820-id-4154399.html)

如上圖,150166號包是三次握手中的第三步client發(fā)送ack給server捡硅,然后150167號包中client發(fā)送了一個長度為816的包給server哮内,因為在這個時候client認(rèn)為連接建立成功,但是server上這個連接實際沒有ready,所以server沒有回復(fù)北发,一段時間后client認(rèn)為丟包了然后重傳這816個字節(jié)的包纹因,一直到超時,client主動發(fā)fin包斷開該連接琳拨。

這個問題也叫client fooling瞭恰,可以看這里:https://github.com/torvalds/linux/commit/5ea8ea2cb7f1d0db15762c9b0bb9e7330425a071(感謝淺奕的提示)

從上面的實際抓包來看不是reset,而是server忽略這些包狱庇,然后client重傳惊畏,一定次數(shù)后client認(rèn)為異常,然后斷開連接密任。

過程中發(fā)現(xiàn)的一個奇怪問題

[root@server ~]# date; netstat -s | egrep "listen|LISTEN"

Fri May? 5 15:39:58 CST 2017

1641685 times the listen queue of a socket overflowed

1641685 SYNs to LISTEN sockets ignored

[root@server ~]# date; netstat -s | egrep "listen|LISTEN"

Fri May? 5 15:39:59 CST 2017

1641906 times the listen queue of a socket overflowed

1641906 SYNs to LISTEN sockets ignored

如上所示:

overflowed和ignored居然總是一樣多颜启,并且都是同步增加,overflowed表示全連接隊列溢出次數(shù)批什,socket ignored表示半連接隊列溢出次數(shù)农曲,沒這么巧吧。

翻看內(nèi)核源代碼(http://elixir.free-electrons.com/linux/v3.18/source/net/ipv4/tcp_ipv4.c):

image.png

可以看到overflow的時候一定會drop++(socket ignored)驻债,也就是drop一定大于等于overflow。

同時我也查看了另外幾臺server的這兩個值來證明drop一定大于等于overflow:

server1

150 SYNs to LISTEN sockets dropped

server2

193 SYNs to LISTEN sockets dropped

server3

16329 times the listen queue of a socket overflowed

16422 SYNs to LISTEN sockets dropped

server4

20 times the listen queue of a socket overflowed

51 SYNs to LISTEN sockets dropped

server5

984932 times the listen queue of a socket overflowed

988003 SYNs to LISTEN sockets dropped

那么全連接隊列滿了會影響半連接隊列嗎形葬?

來看三次握手第一步的源代碼(http://elixir.free-electrons.com/linux/v2.6.33/source/net/ipv4/tcp_ipv4.c#L1249):

image.png

TCP三次握手第一步的時候如果全連接隊列滿了會影響第一步drop 半連接的發(fā)生合呐。大概流程的如下:

tcp_v4_do_rcv->tcp_rcv_state_process->tcp_v4_conn_request

//如果accept backlog隊列已滿,且未超時的request socket的數(shù)量大于1笙以,則丟棄當(dāng)前請求

if(sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_yong(sk)>1)

goto drop;

總結(jié)

全連接隊列淌实、半連接隊列溢出這種問題很容易被忽視,但是又很關(guān)鍵猖腕,特別是對于一些短連接應(yīng)用(比如Nginx拆祈、PHP,當(dāng)然他們也是支持長連接的)更容易爆發(fā)倘感。 一旦溢出放坏,從cpu、線程狀態(tài)看起來都比較正常老玛,但是壓力上不去淤年,在client看來rt也比較高(rt=網(wǎng)絡(luò)+排隊+真正服務(wù)時間),但是從server日志記錄的真正服務(wù)時間來看rt又很短蜡豹。

希望通過本文能夠幫大家理解TCP連接過程中的半連接隊列和全連接隊列的概念麸粮、原理和作用,更關(guān)鍵的是有哪些指標(biāo)可以明確看到這些問題镜廉。

另外每個具體問題都是最好學(xué)習(xí)的機(jī)會弄诲,光看書理解肯定是不夠深刻的,請珍惜每個具體問題娇唯,碰到后能夠把來龍去脈弄清楚齐遵。

參考文章:

http://veithen.github.io/2014/01/01/how-tcp-backlog-works-in-linux.html

http://www.cnblogs.com/zengkefu/p/5606696.html

http://www.cnxct.com/something-about-phpfpm-s-backlog/

http://jaseywang.me/2014/07/20/tcp-queue-%E7%9A%84%E4%B8%80%E4%BA%9B%E9%97%AE%E9%A2%98/

http://jin-yang.github.io/blog/network-synack-queue.html#

http://blog.chinaunix.net/uid-20662820-id-4154399.html

https://www.atatech.org/articles/12919

企業(yè)級互聯(lián)網(wǎng)架構(gòu)Aliware寂玲,讓您的業(yè)務(wù)能力云化:https://www.aliyun.com/aliware

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市洛搀,隨后出現(xiàn)的幾起案子敢茁,更是在濱河造成了極大的恐慌,老刑警劉巖留美,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件彰檬,死亡現(xiàn)場離奇詭異,居然都是意外死亡谎砾,警方通過查閱死者的電腦和手機(jī)逢倍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來景图,“玉大人较雕,你說我怎么就攤上這事≈勘遥” “怎么了亮蒋?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長妆毕。 經(jīng)常有香客問我慎玖,道長,這世上最難降的妖魔是什么笛粘? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任趁怔,我火速辦了婚禮,結(jié)果婚禮上薪前,老公的妹妹穿的比我還像新娘润努。我一直安慰自己,他們只是感情好示括,可當(dāng)我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布铺浇。 她就那樣靜靜地躺著,像睡著了一般例诀。 火紅的嫁衣襯著肌膚如雪随抠。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天繁涂,我揣著相機(jī)與錄音拱她,去河邊找鬼。 笑死扔罪,一個胖子當(dāng)著我的面吹牛秉沼,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼唬复,長吁一口氣:“原來是場噩夢啊……” “哼矗积!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起敞咧,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤棘捣,失蹤者是張志新(化名)和其女友劉穎箱靴,沒想到半個月后堤舒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體序攘,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡橄抹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了即供。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片绸吸。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡环疼,死狀恐怖砌些,靈堂內(nèi)的尸體忽然破棺而出呜投,到底是詐尸還是另有隱情,我是刑警寧澤存璃,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布仑荐,位于F島的核電站,受9級特大地震影響纵东,放射性物質(zhì)發(fā)生泄漏释漆。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一篮迎、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧示姿,春花似錦甜橱、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至子檀,卻和暖如春镊掖,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背褂痰。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工亩进, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人缩歪。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓归薛,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子主籍,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容