<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          服務(wù)端大量處于 TIME_WAIT/CLOSE_WAIT 狀態(tài)連接的原因

          共 7559字,需瀏覽 16分鐘

           ·

          2022-12-31 05:42

          大家好,我是小林。

          之前有位讀者面字節(jié)被問(wèn)到兩個(gè)很經(jīng)典的 TCP 問(wèn)題:

          第一個(gè)問(wèn)題:服務(wù)端大量處于 TIME_WAIT 狀態(tài)連接的原因。

          第二個(gè)問(wèn)題:服務(wù)端大量處于 CLOSE_WAIT 狀態(tài)連接的原因。

          這兩個(gè)問(wèn)題在面試中很常問(wèn),主要也是因?yàn)樵诠ぷ髦幸埠艹S龅竭@個(gè)問(wèn)題。

          這次,我們就來(lái)聊聊這兩個(gè)問(wèn)題。

          服務(wù)端出現(xiàn)大量 TIME_WAIT 狀態(tài)的原因有哪些?

          我們先來(lái)看一下 TCP 四次揮手的流程吧,看看 TIME_WAIT 狀態(tài)發(fā)生在哪一個(gè)階段。

          下面這個(gè)圖,是由「客戶端」作為「主動(dòng)關(guān)閉方」的 TCP 四次揮手的流程。

          TCP 四次揮手的流程

          從上面我們可以知道,TIME_WAIT 狀態(tài)是「主動(dòng)關(guān)閉連接方」才會(huì)出現(xiàn)的狀態(tài)。而且 TIME_WAIT 狀態(tài)會(huì)持續(xù) 2MSL 時(shí)間才會(huì)進(jìn)入到 close 狀態(tài)。在 Linux 上 2MSL 的時(shí)長(zhǎng)是 60 秒,也就是說(shuō)停留在 TIME_WAIT 的時(shí)間為固定的 60 秒

          為什么需要 TIME_WAIT 狀態(tài)?(老八股文了,幫大家復(fù)習(xí)一波)主要有兩個(gè)原因:

          • 保證「被動(dòng)關(guān)閉連接」的一方,能被正確的關(guān)閉。TCP 協(xié)議在關(guān)閉連接的四次揮手中,在主動(dòng)關(guān)閉方發(fā)送的最后一個(gè) ACK 報(bào)文,有可能丟失,這時(shí)被動(dòng)方會(huì)重新發(fā) FIN 報(bào)文, 如果這時(shí)主動(dòng)方處于 CLOSE 狀態(tài) ,就會(huì)響應(yīng) RST 報(bào)文而不是 ACK 報(bào)文。所以主動(dòng)方要處于 TIME_WAIT 狀態(tài),而不能是 CLOSE。
          • 防止歷史連接中的數(shù)據(jù),被后面相同四元組的連接錯(cuò)誤的接收。TCP 報(bào)文可能由于路由器異常而 “迷路”,在迷途期間,TCP 發(fā)送端可能因確認(rèn)超時(shí)而重發(fā)這個(gè)報(bào)文,迷途的報(bào)文在路由器修復(fù)后也會(huì)被送到最終目的地,這個(gè)原來(lái)的迷途報(bào)文就稱為 lost duplicate。在關(guān)閉一個(gè) TCP 連接后,馬上又重新建立起一個(gè)相同的 IP 地址和端口之間的 TCP 連接,后一個(gè)連接被稱為前一個(gè)連接的化身,那么有可能出現(xiàn)這種情況,前一個(gè)連接的迷途重復(fù)報(bào)文在前一個(gè)連接終止后出現(xiàn),從而被誤解成從屬于新的化身。為了避免這個(gè)情 況, TIME_WAIT 狀態(tài)需要持續(xù) 2MSL,因?yàn)檫@樣就可以保證當(dāng)成功建立一個(gè) TCP 連接的時(shí)候,來(lái)自連接先前化身的重復(fù)報(bào)文已經(jīng)在網(wǎng)絡(luò)中消逝。

          很多人誤解以為只有客戶端才會(huì)有 TIME_WAIT 狀態(tài),這是不對(duì)的。TCP 是全雙工協(xié)議,哪一方都可以先關(guān)閉連接,所以哪一方都可能會(huì)有 TIME_WAIT 狀態(tài)。

          總之記住,誰(shuí)先關(guān)閉連接的,它就是主動(dòng)關(guān)閉方,那么 TIME_WAIT 就會(huì)出現(xiàn)在主動(dòng)關(guān)閉方

          什么場(chǎng)景下服務(wù)端會(huì)主動(dòng)斷開連接呢?

          如果服務(wù)端出現(xiàn)大量的 TIME_WAIT 狀態(tài)的 TCP 連接,就是說(shuō)明服務(wù)端主動(dòng)斷開了很多 TCP 連接

          問(wèn)題來(lái)了,什么場(chǎng)景下服務(wù)端會(huì)主動(dòng)斷開連接呢?

          • 第一個(gè)場(chǎng)景:HTTP 沒有使用長(zhǎng)連接
          • 第二個(gè)場(chǎng)景:HTTP 長(zhǎng)連接超時(shí)
          • 第三個(gè)場(chǎng)景:HTTP 長(zhǎng)連接的請(qǐng)求數(shù)量達(dá)到上限

          接下來(lái),分別介紹下。

          第一個(gè)場(chǎng)景:HTTP 沒有使用長(zhǎng)連接

          我們先來(lái)看看 HTTP 長(zhǎng)連接(Keep-Alive)機(jī)制是怎么開啟的。

          在 HTTP/1.0 中默認(rèn)是關(guān)閉的,如果瀏覽器要開啟 Keep-Alive,它必須在請(qǐng)求的 header 中添加:

          Connection: Keep-Alive

          然后當(dāng)服務(wù)器收到請(qǐng)求,作出回應(yīng)的時(shí)候,它也被添加到響應(yīng)中 header 里:

          Connection: Keep-Alive

          這樣做,TCP 連接就不會(huì)中斷,而是保持連接。當(dāng)客戶端發(fā)送另一個(gè)請(qǐng)求時(shí),它會(huì)使用同一個(gè) TCP 連接。這一直繼續(xù)到客戶端或服務(wù)器端提出斷開連接。

          從 HTTP/1.1 開始, 就默認(rèn)是開啟了 Keep-Alive,現(xiàn)在大多數(shù)瀏覽器都默認(rèn)是使用 HTTP/1.1,所以 Keep-Alive 都是默認(rèn)打開的。一旦客戶端和服務(wù)端達(dá)成協(xié)議,那么長(zhǎng)連接就建立好了。

          如果要關(guān)閉 HTTP Keep-Alive,需要在 HTTP 請(qǐng)求或者響應(yīng)的 header 里添加 Connection:close 信息,也就是說(shuō),只要客戶端和服務(wù)端任意一方的 HTTP header 中有 Connection:close 信息,那么就無(wú)法使用 HTTP 長(zhǎng)連接的機(jī)制

          關(guān)閉 HTTP 長(zhǎng)連接機(jī)制后,每次請(qǐng)求都要經(jīng)歷這樣的過(guò)程:建立 TCP -> 請(qǐng)求資源 -> 響應(yīng)資源 -> 釋放連接,那么此方式就是 HTTP 短連接,如下圖:

          HTTP 短連接

          在前面我們知道,只要任意一方的 HTTP header 中有 Connection:close 信息,就無(wú)法使用 HTTP 長(zhǎng)連接機(jī)制,這樣在完成一次 HTTP 請(qǐng)求/處理后,就會(huì)關(guān)閉連接。

          問(wèn)題來(lái)了,這時(shí)候是客戶端還是服務(wù)端主動(dòng)關(guān)閉連接呢?

          在 RFC 文檔中,并沒有明確由誰(shuí)來(lái)關(guān)閉連接,請(qǐng)求和響應(yīng)的雙方都可以主動(dòng)關(guān)閉 TCP 連接。

          不過(guò),根據(jù)大多數(shù) Web 服務(wù)的實(shí)現(xiàn),不管哪一方禁用了 HTTP Keep-Alive,都是由服務(wù)端主動(dòng)關(guān)閉連接,那么此時(shí)服務(wù)端上就會(huì)出現(xiàn) TIME_WAIT 狀態(tài)的連接。

          客戶端禁用了 HTTP Keep-Alive,服務(wù)端開啟 HTTP Keep-Alive,誰(shuí)是主動(dòng)關(guān)閉方?

          當(dāng)客戶端禁用了 HTTP Keep-Alive,這時(shí)候 HTTP 請(qǐng)求的 header 就會(huì)有 Connection:close 信息,這時(shí)服務(wù)端在發(fā)完 HTTP 響應(yīng)后,就會(huì)主動(dòng)關(guān)閉連接。

          為什么要這么設(shè)計(jì)呢?HTTP 是請(qǐng)求-響應(yīng)模型,發(fā)起方一直是客戶端,HTTP Keep-Alive 的初衷是為客戶端后續(xù)的請(qǐng)求重用連接,如果我們在某次 HTTP 請(qǐng)求-響應(yīng)模型中,請(qǐng)求的 header 定義了 connection:close 信息,那不再重用這個(gè)連接的時(shí)機(jī)就只有在服務(wù)端了,所以我們?cè)?HTTP 請(qǐng)求-響應(yīng)這個(gè)周期的「末端」關(guān)閉連接是合理的。

          客戶端開啟了 HTTP Keep-Alive,服務(wù)端禁用了 HTTP Keep-Alive,誰(shuí)是主動(dòng)關(guān)閉方?

          當(dāng)客戶端開啟了 HTTP Keep-Alive,而服務(wù)端禁用了 HTTP Keep-Alive,這時(shí)服務(wù)端在發(fā)完 HTTP 響應(yīng)后,服務(wù)端也會(huì)主動(dòng)關(guān)閉連接。

          為什么要這么設(shè)計(jì)呢?在服務(wù)端主動(dòng)關(guān)閉連接的情況下,只要調(diào)用一次 close() 就可以釋放連接,剩下的工作由內(nèi)核 TCP 棧直接進(jìn)行了處理,整個(gè)過(guò)程只有一次 syscall;如果是要求 客戶端關(guān)閉,則服務(wù)端在寫完最后一個(gè) response 之后需要把這個(gè) socket 放入 readable 隊(duì)列,調(diào)用 select / epoll 去等待事件;然后調(diào)用一次 read() 才能知道連接已經(jīng)被關(guān)閉,這其中是兩次 syscall,多一次用戶態(tài)程序被激活執(zhí)行,而且 socket 保持時(shí)間也會(huì)更長(zhǎng)。

          因此,當(dāng)服務(wù)端出現(xiàn)大量的 TIME_WAIT 狀態(tài)連接的時(shí)候,可以排查下是否客戶端和服務(wù)端都開啟了 HTTP Keep-Alive,因?yàn)槿我庖环經(jīng)]有開啟  HTTP Keep-Alive,都會(huì)導(dǎo)致服務(wù)端在處理完一個(gè) HTTP 請(qǐng)求后,就主動(dòng)關(guān)閉連接,此時(shí)服務(wù)端上就會(huì)出現(xiàn)大量的 TIME_WAIT 狀態(tài)的連接。

          針對(duì)這個(gè)場(chǎng)景下,解決的方式也很簡(jiǎn)單,讓客戶端和服務(wù)端都開啟 HTTP Keep-Alive 機(jī)制。

          第二個(gè)場(chǎng)景:HTTP 長(zhǎng)連接超時(shí)

          HTTP 長(zhǎng)連接的特點(diǎn)是,只要任意一端沒有明確提出斷開連接,則保持 TCP 連接狀態(tài)。

          HTTP 長(zhǎng)連接可以在同一個(gè) TCP 連接上接收和發(fā)送多個(gè) HTTP 請(qǐng)求/應(yīng)答,避免了連接建立和釋放的開銷。

          可能有的同學(xué)會(huì)問(wèn),如果使用了 HTTP 長(zhǎng)連接,如果客戶端完成一個(gè) HTTP 請(qǐng)求后,就不再發(fā)起新的請(qǐng)求,此時(shí)這個(gè) TCP 連接一直占用著不是挺浪費(fèi)資源的嗎?

          對(duì)沒錯(cuò),所以為了避免資源浪費(fèi)的情況,web 服務(wù)軟件一般都會(huì)提供一個(gè)參數(shù),用來(lái)指定 HTTP 長(zhǎng)連接的超時(shí)時(shí)間,比如 nginx 提供的 keepalive_timeout 參數(shù)。

          假設(shè)設(shè)置了 HTTP 長(zhǎng)連接的超時(shí)時(shí)間是 60 秒,nginx 就會(huì)啟動(dòng)一個(gè)「定時(shí)器」,如果客戶端在完后一個(gè) HTTP 請(qǐng)求后,在 60 秒內(nèi)都沒有再發(fā)起新的請(qǐng)求,定時(shí)器的時(shí)間一到,nginx 就會(huì)觸發(fā)回調(diào)函數(shù)來(lái)關(guān)閉該連接,那么此時(shí)服務(wù)端上就會(huì)出現(xiàn) TIME_WAIT 狀態(tài)的連接

          HTTP 長(zhǎng)連接超時(shí)

          當(dāng)服務(wù)端出現(xiàn)大量 TIME_WAIT 狀態(tài)的連接時(shí),如果現(xiàn)象是有大量的客戶端建立完 TCP 連接后,很長(zhǎng)一段時(shí)間沒有發(fā)送數(shù)據(jù),那么大概率就是因?yàn)?HTTP 長(zhǎng)連接超時(shí),導(dǎo)致服務(wù)端主動(dòng)關(guān)閉連接,產(chǎn)生大量處于 TIME_WAIT 狀態(tài)的連接。

          可以往網(wǎng)絡(luò)問(wèn)題的方向排查,比如是否是因?yàn)榫W(wǎng)絡(luò)問(wèn)題,導(dǎo)致客戶端發(fā)送的數(shù)據(jù)一直沒有被服務(wù)端接收到,以至于 HTTP 長(zhǎng)連接超時(shí)。

          第三個(gè)場(chǎng)景:HTTP 長(zhǎng)連接的請(qǐng)求數(shù)量達(dá)到上限

          Web 服務(wù)端通常會(huì)有個(gè)參數(shù),來(lái)定義一條 HTTP 長(zhǎng)連接上最大能處理的請(qǐng)求數(shù)量,當(dāng)超過(guò)最大限制時(shí),就會(huì)主動(dòng)關(guān)閉連接。

          比如 nginx 的 keepalive_requests 這個(gè)參數(shù),這個(gè)參數(shù)是指一個(gè) HTTP 長(zhǎng)連接建立之后,nginx 就會(huì)為這個(gè)連接設(shè)置一個(gè)計(jì)數(shù)器,記錄這個(gè) HTTP 長(zhǎng)連接上已經(jīng)接收并處理的客戶端請(qǐng)求的數(shù)量。如果達(dá)到這個(gè)參數(shù)設(shè)置的最大值時(shí),則 nginx 會(huì)主動(dòng)關(guān)閉這個(gè)長(zhǎng)連接,那么此時(shí)服務(wù)端上就會(huì)出現(xiàn) TIME_WAIT 狀態(tài)的連接。

          keepalive_requests 參數(shù)的默認(rèn)值是 100 ,意味著每個(gè) HTTP 長(zhǎng)連接最多只能跑 100  次請(qǐng)求,這個(gè)參數(shù)往往被大多數(shù)人忽略,因?yàn)楫?dāng) QPS (每秒請(qǐng)求數(shù)) 不是很高時(shí),默認(rèn)值 100 湊合夠用。

          但是,對(duì)于一些 QPS 比較高的場(chǎng)景,比如超過(guò) 10000 QPS,甚至達(dá)到 30000 , 50000 甚至更高,如果 keepalive_requests 參數(shù)值是 100,這時(shí)候就 nginx 就會(huì)很頻繁地關(guān)閉連接,那么此時(shí)服務(wù)端上就會(huì)出大量的 TIME_WAIT 狀態(tài)

          針對(duì)這個(gè)場(chǎng)景下,解決的方式也很簡(jiǎn)單,調(diào)大 nginx 的 keepalive_requests 參數(shù)就行。

          TIME_WAIT 狀態(tài)過(guò)多有什么危害?

          過(guò)多的 TIME-WAIT 狀態(tài)主要的危害有兩種:

          • 第一是占用系統(tǒng)資源,比如文件描述符、內(nèi)存資源、CPU 資源等;
          • 第二是占用端口資源,端口資源也是有限的,一般可以開啟的端口為 32768~61000,也可以通過(guò) net.ipv4.ip_local_port_range參數(shù)指定范圍。

          客戶端和服務(wù)端 TIME_WAIT 過(guò)多,造成的影響是不同的。

          如果客戶端(主動(dòng)發(fā)起關(guān)閉連接方)的 TIME_WAIT 狀態(tài)過(guò)多,占滿了所有端口資源,那么就無(wú)法對(duì)「目的 IP+ 目的 PORT」都一樣的服務(wù)端發(fā)起連接了,但是被使用的端口,還是可以繼續(xù)對(duì)另外一個(gè)服務(wù)端發(fā)起連接的。具體可以看我這篇文章:客戶端的端口可以重復(fù)使用嗎?

          因此,客戶端(發(fā)起連接方)都是和「目的 IP+ 目的 PORT 」都一樣的服務(wù)端建立連接的話,當(dāng)客戶端的 TIME_WAIT 狀態(tài)連接過(guò)多的話,就會(huì)受端口資源限制,如果占滿了所有端口資源,那么就無(wú)法再跟「目的 IP+ 目的 PORT」都一樣的服務(wù)端建立連接了。

          不過(guò),即使是在這種場(chǎng)景下,只要連接的是不同的服務(wù)端,端口是可以重復(fù)使用的,所以客戶端還是可以向其他服務(wù)端發(fā)起連接的,這是因?yàn)閮?nèi)核在定位一個(gè)連接的時(shí)候,是通過(guò)四元組(源IP、源端口、目的IP、目的端口)信息來(lái)定位的,并不會(huì)因?yàn)榭蛻舳说亩丝谝粯樱鴮?dǎo)致連接沖突。

          如果服務(wù)端(主動(dòng)發(fā)起關(guān)閉連接方)的 TIME_WAIT 狀態(tài)過(guò)多,并不會(huì)導(dǎo)致端口資源受限,因?yàn)榉?wù)端只監(jiān)聽一個(gè)端口,而且由于一個(gè)四元組唯一確定一個(gè) TCP 連接,因此理論上服務(wù)端可以建立很多連接,但是 TCP 連接過(guò)多,會(huì)占用系統(tǒng)資源,比如文件描述符、內(nèi)存資源、CPU 資源等。

          如何優(yōu)化 TIME_WAIT 狀態(tài)?

          這里給出優(yōu)化 TIME-WAIT 的幾個(gè)方式,都是有利有弊:

          • 打開 net.ipv4.tcp_tw_reuse 和 net.ipv4.tcp_timestamps 選項(xiàng);
          • net.ipv4.tcp_max_tw_buckets
          • 程序中使用 SO_LINGER ,應(yīng)用強(qiáng)制使用 RST 關(guān)閉。

          方式一:net.ipv4.tcp_tw_reuse 和 tcp_timestamps

          開啟 tcp_tw_reuse,則可以復(fù)用處于 TIME_WAIT 的 socket 為新的連接所用

          有一點(diǎn)需要注意的是,tcp_tw_reuse 功能只能用客戶端(連接發(fā)起方),因?yàn)殚_啟了該功能,在調(diào)用 connect() 函數(shù)時(shí),內(nèi)核會(huì)隨機(jī)找一個(gè) time_wait 狀態(tài)超過(guò) 1 秒的連接給新的連接復(fù)用。

          net.ipv4.tcp_tw_reuse = 1

          使用這個(gè)選項(xiàng),還有一個(gè)前提,需要打開對(duì) TCP 時(shí)間戳的支持,即

          net.ipv4.tcp_timestamps=1(默認(rèn)即為 1)

          這個(gè)時(shí)間戳的字段是在 TCP 頭部的「選項(xiàng)」里,它由一共 8 個(gè)字節(jié)表示時(shí)間戳,其中第一個(gè) 4 字節(jié)字段用來(lái)保存發(fā)送該數(shù)據(jù)包的時(shí)間,第二個(gè) 4 字節(jié)字段用來(lái)保存最近一次接收對(duì)方發(fā)送到達(dá)數(shù)據(jù)的時(shí)間。

          由于引入了時(shí)間戳,可以使得重復(fù)的數(shù)據(jù)包會(huì)因?yàn)闀r(shí)間戳過(guò)期被自然丟棄,因此 TIME_WAIT 狀態(tài)才可以被復(fù)用。

          方式二:net.ipv4.tcp_max_tw_buckets

          這個(gè)值默認(rèn)為 18000,當(dāng)系統(tǒng)中處于 TIME_WAIT 的連接一旦超過(guò)這個(gè)值時(shí),系統(tǒng)就會(huì)將后面的 TIME_WAIT 連接狀態(tài)重置,這個(gè)方法比較暴力。

          net.ipv4.tcp_max_tw_buckets = 18000

          方式三:程序中使用 SO_LINGER

          我們可以通過(guò)設(shè)置 socket 選項(xiàng),來(lái)設(shè)置調(diào)用 close 關(guān)閉連接行為。

          struct linger so_linger;
          so_linger.l_onoff = 1;
          so_linger.l_linger = 0;
          setsockopt(s, SOL_SOCKET, SO_LINGER, &so_linger,sizeof(so_linger));

          如果l_onoff為非 0, 且l_linger值為 0,那么調(diào)用close后,會(huì)立該發(fā)送一個(gè)RST標(biāo)志給對(duì)端,該 TCP 連接將跳過(guò)四次揮手,也就跳過(guò)了TIME_WAIT狀態(tài),直接關(guān)閉。

          但這為跨越TIME_WAIT狀態(tài)提供了一個(gè)可能,不過(guò)是一個(gè)非常危險(xiǎn)的行為,不值得提倡。

          前面介紹的方法都是試圖越過(guò) TIME_WAIT狀態(tài)的,這樣其實(shí)不太好。雖然 TIME_WAIT 狀態(tài)持續(xù)的時(shí)間是有一點(diǎn)長(zhǎng),顯得很不友好,但是它被設(shè)計(jì)來(lái)就是用來(lái)避免發(fā)生亂七八糟的事情。

          《UNIX網(wǎng)絡(luò)編程》一書中卻說(shuō)道:TIME_WAIT 是我們的朋友,它是有助于我們的,不要試圖避免這個(gè)狀態(tài),而是應(yīng)該弄清楚它

          如果服務(wù)端要避免過(guò)多的 TIME_WAIT 狀態(tài)的連接,就永遠(yuǎn)不要主動(dòng)斷開連接,讓客戶端去斷開,由分布在各處的客戶端去承受 TIME_WAIT

          服務(wù)端出現(xiàn)大量 CLOSE_WAIT 狀態(tài)的原因有哪些?

          還是拿這張圖:

          TCP 四次揮手的流程

          從上面這張圖我們可以得知,CLOSE_WAIT 狀態(tài)是「被動(dòng)關(guān)閉方」才會(huì)有的狀態(tài),而且如果「被動(dòng)關(guān)閉方」沒有調(diào)用 close 函數(shù)關(guān)閉連接,那么就無(wú)法發(fā)出 FIN 報(bào)文,從而無(wú)法使得 CLOSE_WAIT 狀態(tài)的連接轉(zhuǎn)變?yōu)?LAST_ACK 狀態(tài)。

          所以,當(dāng)服務(wù)端出現(xiàn)大量 CLOSE_WAIT 狀態(tài)的連接的時(shí)候,說(shuō)明服務(wù)端的程序沒有調(diào)用 close 函數(shù)關(guān)閉連接

          那什么情況會(huì)導(dǎo)致服務(wù)端的程序沒有調(diào)用 close 函數(shù)關(guān)閉連接?這時(shí)候通常需要排查代碼。

          我們先來(lái)分析一個(gè)普通的 TCP 服務(wù)端的流程:

          1. 創(chuàng)建服務(wù)端 socket,bind 綁定端口、listen 監(jiān)聽端口
          2. 將服務(wù)端 socket 注冊(cè)到 epoll
          3. epoll_wait 等待連接到來(lái),連接到來(lái)時(shí),調(diào)用 accpet 獲取已連接的 socket
          4. 將已連接的 socket 注冊(cè)到 epoll
          5. epoll_wait 等待事件發(fā)生
          6. 對(duì)方連接關(guān)閉時(shí),我方調(diào)用 close

          可能導(dǎo)致服務(wù)端沒有調(diào)用 close 函數(shù)的原因,如下。

          第一個(gè)原因:第 2 步?jīng)]有做,沒有將服務(wù)端 socket 注冊(cè)到 epoll,這樣有新連接到來(lái)時(shí),服務(wù)端沒辦法感知這個(gè)事件,也就無(wú)法獲取到已連接的 socket,那服務(wù)端自然就沒機(jī)會(huì)對(duì) socket 調(diào)用 close 函數(shù)了。

          不過(guò)這種原因發(fā)生的概率比較小,這種屬于明顯的代碼邏輯 bug,在前期 read view 階段就能發(fā)現(xiàn)的了。

          第二個(gè)原因:第 3 步?jīng)]有做,有新連接到來(lái)時(shí)沒有調(diào)用 accpet 獲取該連接的 socket,導(dǎo)致當(dāng)有大量的客戶端主動(dòng)斷開了連接,而服務(wù)端沒機(jī)會(huì)對(duì)這些 socket 調(diào)用 close 函數(shù),從而導(dǎo)致服務(wù)端出現(xiàn)大量 CLOSE_WAIT 狀態(tài)的連接。

          發(fā)生這種情況可能是因?yàn)榉?wù)端在執(zhí)行 accpet  函數(shù)之前,代碼卡在某一個(gè)邏輯或者提前拋出了異常。

          第三個(gè)原因:第 4 步?jīng)]有做,通過(guò) accpet 獲取已連接的 socket 后,沒有將其注冊(cè)到 epoll,導(dǎo)致后續(xù)收到 FIN 報(bào)文的時(shí)候,服務(wù)端沒辦法感知這個(gè)事件,那服務(wù)端就沒機(jī)會(huì)調(diào)用 close 函數(shù)了。

          發(fā)生這種情況可能是因?yàn)榉?wù)端在將已連接的 socket 注冊(cè)到 epoll 之前,代碼卡在某一個(gè)邏輯或者提前拋出了異常。之前看到過(guò)別人解決 close_wait 問(wèn)題的實(shí)踐文章,感興趣的可以看看:一次 Netty 代碼不健壯導(dǎo)致的大量 CLOSE_WAIT 連接原因分析

          第四個(gè)原因:第 6 步?jīng)]有做,當(dāng)發(fā)現(xiàn)客戶端關(guān)閉連接后,服務(wù)端沒有執(zhí)行 close 函數(shù),可能是因?yàn)榇a漏處理,或者是在執(zhí)行 close 函數(shù)之前,代碼卡在某一個(gè)邏輯,比如發(fā)生死鎖等等。

          可以發(fā)現(xiàn),當(dāng)服務(wù)端出現(xiàn)大量 CLOSE_WAIT 狀態(tài)的連接的時(shí)候,通常都是代碼的問(wèn)題,這時(shí)候我們需要針對(duì)具體的代碼一步一步的進(jìn)行排查和定位,主要分析的方向就是服務(wù)端為什么沒有調(diào)用 close

           點(diǎn)擊上方卡片關(guān)注K8s技術(shù)圈,掌握前沿云原生技術(shù)


          瀏覽 69
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  成人欧美一区二区三区牛牛影视 | 亚洲综合免费观看高清 | 成人福利电影 | 欧美成人a v | 欧美三级片一区二区 |