天天看點

也說說TIME_WAIT狀态也說說TIME_WAIT狀态TIME_WAIT狀态是怎麼産生的服務端TIME_WAIT過多解決方法

一個朋友問到,自己用go寫了一個簡單的HTTP服務端程式,為什麼壓測的時候服務端會出現一段時間的TIME_WAIT超高的情況,導緻壓測的效果不好呢?

記得老王有兩篇文章專門說這個,當時粗粗看了一遍,正好碰上這個問題,又翻出來細細摟了。

第一個要弄懂的,是TIME_WAIT是怎麼産生的。

要弄懂TIME_WAIT要從TCP的四次握手的分手協定說起。

上面這個圖檔展示了TCP從連接配接建立到連接配接釋放的過程中,用戶端和服務端的狀态變化圖。如果隻看連接配接釋放階段,四次握手

用戶端先發送FIN,進入FIN_WAIT1狀态

服務端收到FIN,發送ACK,進入CLOSE_WAIT狀态,用戶端收到這個ACK,進入FIN_WAIT2狀态

服務端發送FIN,進入LAST_ACK狀态

用戶端收到FIN,發送ACK,進入TIME_WAIT狀态,服務端收到ACK,進入CLOSE狀态

用戶端TIME_WAIT持續2倍MSL時長,在linux體系中大概是60s,轉換成CLOSE狀态

當然在這個例子和上面的圖檔中,使用用戶端和服務端來描述是不準确的,TCP主動斷開連接配接的一方可能是用戶端,也可能是服務端。是以使用主動斷開的一方,和被動斷開的一方替換上面的圖可能更為貼切。

不管怎麼說,TIME_WAIT的狀态就是主動斷開的一方,發送完最後一次ACK之後進入的狀态。并且持續時間還挺長的。

能不能發送完ACK之後不進入TIME_WAIT就直接進入CLOSE狀态呢?不行的,這個是為了TCP協定的可靠性,由于網絡原因,ACK可能會發送失敗,那麼這個時候,被動一方會主動重新發送一次FIN,這個時候如果主動方在TIME_WAIT狀态,則還會再發送一次ACK,進而保證可靠性。那麼從這個解釋來說,2MSL的時長設定是可以了解的,MSL是封包最大生存時間,如果重新發送,一個FIN+一個ACK,再加上不定期的延遲時間,大緻是在2MSL的範圍。

是以從理論上說,網上調試參數降低TIME_WAIT的持續時間的方法是一種以可靠性換取性能的一種方式。嗯,品質守恒定理還是鐵律。

回到上面的問題,go寫了一個HTTP服務,壓測發現TIME_WAIT過多。

首先判斷是不是壓測程式放在服務的同一台機器...當然不會犯這麼低級的錯誤...

那麼這個感覺就有點奇怪了,HTTP服務并沒有依賴外部mysql或者redis等服務,就是一個簡單的Hello world,而TIME_WAIT的是主動斷開方才會出現的,是以主動斷開方是服務端?

答案是是的。在HTTP1.1協定中,有個 Connection 頭,Connection有兩個值,close和keep-alive,這個頭就相當于用戶端告訴服務端,服務端你執行完成請求之後,是關閉連接配接還是保持連接配接,保持連接配接就意味着在保持連接配接期間,隻能由用戶端主動斷開連接配接。還有一個keep-alive的頭,設定的值就代表了服務端保持連接配接保持多久。

HTTP預設的Connection值為close,那麼就意味着關閉請求的一方幾乎都會是由服務端這邊發起的。那麼這個服務端産生TIME_WAIT過多的情況就很正常了。

雖然HTTP預設Connection值為close,但是現在的浏覽器發送請求的時候一般都會設定Connection為keep-alive了。是以,也有人說,現在沒有必要通過調整參數來使TIME_WAIT降低了。

按照HTTP協定的頭,我們在壓測程式發出的HTTP協定頭裡面加上connection:keep-alive當然能解決這個問題。

這個參數作用是當新的連接配接進來的時候,可以複用處于TIME_WAIT的socket。預設值是0。

預設TIME_WAIT的逾時時間是2倍的MSL,但是MSL一般會設定的非常長。如果tcp_timestamps是關閉的,開啟tcp_tw_recycle是沒用的。但是一般情況下tcp_timestamps是預設開啟的,是以直接開啟就有用了。

本文轉自軒脈刃部落格園部落格,原文連結:http://www.cnblogs.com/yjf512/p/5327886.html,如需轉載請自行聯系原作者

繼續閱讀