TIME_WAIT 的問題
應(yīng)用服務(wù)器需要通過發(fā)起 TCP 連接其他服務(wù)器,比如 代理服務(wù)器需要請(qǐng)求上游服務(wù)器,每個(gè)連接會(huì)占用一個(gè)port,在高并發(fā)場景下,可能會(huì)導(dǎo)致端口耗盡。為了TCP正常斷開連接,TCP 發(fā)起揮手的一端為了確保最后一個(gè)ACK 能夠達(dá)到被動(dòng)關(guān)閉方,所以會(huì)等待 2MSL。

MSL (maximum segment lifetime):LINUX 的硬編碼字段,名稱為 TCP_TIMEWAIT_LEN,值為60s,而一個(gè)time_wait 默認(rèn)等待的時(shí)間為 2MSL
TIME_WAIT 的危害
- 內(nèi)存資源占用,內(nèi)存資源的占用不是很嚴(yán)重,可暫且忽略
- 端口資源占用,一個(gè)TCP連接需要消耗一個(gè)本地端口,一般可開啟的端口為32768 ~ 61000。
如何優(yōu)化
net.ipv4.ip_local_port_range
制定主動(dòng)建聯(lián)時(shí)的端口范圍
net.ipv4.tcp_max_tw_buckets
此值默認(rèn)是 18000,當(dāng)系統(tǒng)中處于 TIME_WAIT 的連接大于該值后,系統(tǒng)會(huì)將所有的 TIME_WAIT 連接重置,并打印出警告信息。這個(gè)方法過于暴力,解決的問題比帶來的問題多,不建議使用
調(diào)低 TCP_TIMEWAIT_LEN,重新編譯系統(tǒng)
得編譯內(nèi)核,而且TCP發(fā)明至今這些固化到內(nèi)核的參數(shù)都是有一定道理的,不要亂改。
SO_LINGER 設(shè)置
在應(yīng)用程序中設(shè)置套接字選項(xiàng),調(diào)用close 或者 shutdown 關(guān)閉連接時(shí)候的行為。
int setsockopt(int sockfd, int level, int optname, const void *optval,
socklen_t optlen);
struct linger {
int l_onoff; /* 0=off, nonzero=on */
int l_linger; /* linger time, POSIX specifies units as seconds */
}
- l_onoff: linger 的開關(guān)
- l_onoff 為 0:關(guān)閉linger 選項(xiàng),默認(rèn)行為,close 或 shutdown 立即返回,如果在套接字發(fā)送緩沖區(qū)有數(shù)據(jù)殘留,系統(tǒng)會(huì)將試著把這些數(shù)據(jù)都發(fā)送出去
- l_onoff 為 1:打開linger 選項(xiàng),具體行為看 l_linger
- l_linger 為0:調(diào)用close后,立即發(fā)送一個(gè)RST標(biāo)志給對(duì)端,該TCP跳過四次揮手,直接關(guān)閉,這種方式被稱為“強(qiáng)行關(guān)閉”,這種情況下,排隊(duì)的數(shù)據(jù)不會(huì)被發(fā)送,被動(dòng)關(guān)閉方也不知道對(duì)端已經(jīng)徹底斷開,只有當(dāng)被動(dòng)關(guān)閉方正阻塞在recv() 調(diào)用上,接受到 RST 時(shí),會(huì)立刻得到一個(gè) “connect reset by peer”的異常。
- l_linger 為1:調(diào)用close后,調(diào)用close的線程將阻塞,直到數(shù)據(jù)都被發(fā)送出去,或者設(shè)置 l_linger 的計(jì)時(shí)時(shí)間到。
net.ipv4.tcp_tw_reuse : 更安全的設(shè)置
Linux 對(duì) net.ipv4_tw_reuse的解析如下:
Allow to reuse TIME-WAIT sockets for new connections when it is safe from protocol viewpoint. Default value is 0.It should not be changed without advice/request of technical experts.
這段話意思是說從協(xié)議角度理解如果是安全的話,可以復(fù)用處于 TIME_WAIT 的套接字為新的連接所用。
協(xié)議角度 的安全是指:
- 只適用與連接的發(fā)起方,即客戶端
- 對(duì)應(yīng)的TIME_WAIT 狀態(tài)的連接創(chuàng)建時(shí)間超過1s才可以被復(fù)用
使用的這個(gè)選項(xiàng)的前提,需要打開對(duì)TCP時(shí)間戳的支持。
即 net.ipv4.tcp_timestamps = 1 (默認(rèn)即為1),重復(fù)的數(shù)據(jù)包會(huì)因?yàn)闀r(shí)間戳過期被自然丟棄。
SO_REUSEADDR
這個(gè)比較特殊,網(wǎng)上有很多教程都說拿這個(gè)解決 TIME_WAIT,其實(shí)是對(duì)的,但是不是一回事。為什么?
這個(gè)是解決端口復(fù)用問題的,并不是解決 TIME_WAIT ,這個(gè)是告訴內(nèi)核,即使TIME_WAIT 的套接字,也可以作為新的套接字使用,這是為了避免服務(wù)端監(jiān)聽端口時(shí),因?yàn)楸槐O(jiān)聽的端口處于 TIME_WAIT 導(dǎo)致服務(wù)端無法啟動(dòng)。
其本質(zhì)是解決 服務(wù)端 監(jiān)聽端口時(shí)的 TIME_WAIT ,而我們上面一直說的是作為客戶端建聯(lián)時(shí)沒有足夠的隨機(jī)端口導(dǎo)致的無法建聯(lián)。
終極解決方案: 長連接
條件允許的,把連接保持住,避免頻繁的建連和斷開。但是僅限內(nèi)網(wǎng)這么搞,公網(wǎng)的話,一條連接總是不斷,運(yùn)營商可能會(huì)搞些小動(dòng)作,給你限個(gè)速。