優化內核時的坑,切記不要啓用net.ipv4.tcp_tw_recycle

1. tw_reuse,tw_recycle 必須在客戶端和服務端timestamps 開啓時才管用(默認打開)

2. tw_reuse 只對客戶端起作用,開啓後客戶端在1s內回收

3. tw_recycle 對客戶端和服務器同時起作用,開啓後在 3.5*RTO 內回收,RTO 200ms~ 120s 具體時間視網絡狀況。內網狀況比tw_reuse 稍快,公網尤其移動網絡大多要比tw_reuse 慢,優點就是能夠回收服務端的TIME_WAIT數量


* linux 內核文檔中,對net.ipv4.tcp_tw_recycle的描述並不是很明確。

tcp_tw_recycle (Boolean; default: disabled; since Linux 2.4)[譯者注:來自linux man tcp的描述]

Enable fast recycling of TIME-WAIT sockets. Enabling this option is not recommended since this causes

problems when working with NAT (Network Address Translation).

啓用TIME-WAIT狀態sockets的快速回收,這個選項不推薦啓用。在NAT(Network Address Translation)網絡下,會導致大量的TCP連接建立錯誤。


* 假如啓用net.ipv4.tcp_tw_recycle,可以用於快速減少在TIME-WAIT狀態TCP連接數。但是,在TCP(7)手冊中,參數net.ipv4.tcp_tw_recycle 非常蛋疼,尤其是在普通用戶家中,有多臺設備,或者網吧、公司等多臺設備,共用同一個NAT設備環境下,TW回收選項是很有問題的面向公共服務器作爲它不會把手連接兩臺不同的計算機上,這問題很難發現,無從下手


* Connection table slot連接表槽

處於TIME-WAIT狀態的TCP連接,在鏈接表槽中存活1分鐘,意味着另一個相同四元組(源地址,源端口,目標地址,目標端口)的連接不能出現,也就是說新的TCP(相同四元組)連接無法建立。對於web服務器來說,目標地址、目標端口都是固定值。如果web服務器是在L7層的負載均衡後面,那麼源地址更是固定值。在LINUX上,作爲客戶端時,客戶端端口默認可分配的數量是3W個(可以在參數net.ipv4.up_local_port_range上調整)。

這意味着,在web服務器跟負載均衡服務器之間,每分鐘只有3W個端口是處於established狀態,也就大約500連接每秒。如果TIME-WAIT狀態的socket出現在客戶端,那這個問題很容易被發現。調用connect()函數會返回EADDRNOTAVAIL,程序也會記錄相關的錯誤到日誌。如果TIME-WATI狀態的socket出現在服務端,問題會非常複雜,因爲這裏並沒有日誌記錄,也沒有計數器參考。不過,可以列出服務器上當前所有四元組連接的數量來確認


* 解決辦法:增加四元組的範圍,這有很多方法去實現。(以下建議的順序,實施難度從小到大排列)

修改net.ipv4.ip_local_port_range參數,增加客戶端端口可用範圍。

增加服務端端口,多監聽一些端口,比如81、82、83這些,web服務器前有負載均衡,對用戶友好。

增加客戶端IP,尤其是作爲負載均衡服務器時,使用更多IP去跟後端的web服務器通訊。

增加服務端IP。

當然了,最後的辦法是調整net.ipv4.tcp_tw_reuse和net.ipv4.tcp_tw_recycle。但不到萬不得已,千萬別這麼做。


net.ipv4.tcp_tw_recycle

這種機制也依賴時間戳選項,這也會影響到所有連接進來和連接出去的連接。「譯者注:linux上tcp_timestamps默認開啓」

TIME-WAIT狀態計劃更早的過期:它將會在超時重發(RTO)間隔後移除(底層會根據當前連接的延遲狀況根據RTT來計算RTO值,上篇《PHP-FPM中backlog參數變更的一些思考》也有提到過,比較複雜的算法)。可以執行ss指令,獲取當前存活的TCP連接狀態,查看這些數據。「譯者注:linux指令ss的結果中rto,rtt值單位均爲ms」



與其功能相似的參數net.ipv4.tcp_tw_reuse,手冊裏稍微有點描述,如下:

tcp_tw_reuse (Boolean; default: disabled; since Linux 2.4.19/2.6)

Allow to reuse TIME-WAIT sockets for new connections when it is safe from protocol viewpoint. It

should not be changed without advice/request of technical experts.

//從協議設計上來看,對於TIME-WAIT狀態的sockets重用到新的TCP連接上來說,是安全的。(用於客戶端時的配置)


net.ipv4.tcp_tw_reuse

TIME-WAIT狀態是爲了防止不相關的延遲請求包被接受。但在某些特定條件下,很有可能出現,新建立的TCP連接請求包,被老連接(同樣的四元組,暫時還是TIME-WAIT狀態,回收中)的連接誤處理。RFC 1323 實現了TCP拓展規範,以保證網絡繁忙狀態下的高可用。除此之外,另外,它定義了一個新的TCP選項–兩個四字節的timestamp fields時間戳字段,第一個是TCP發送方的當前時鐘時間戳,而第二個是從遠程主機接收到的最新時間戳。

啓用net.ipv4.tcp_tw_reuse後,如果新的時間戳,比以前存儲的時間戳更大,那麼linux將會從TIME-WAIT狀態的存活連接中,選取一個,重新分配給新的連接出去的TCP連接。

連出的TIME-WAIT狀態連接,僅僅1秒後就可以被重用了。


最後注意的是:

    * 最合適的解決方案是增加更多的四元組數目,比如,服務器可用端口,或服務器IP,讓服務器能容納足夠多的TIME-WAIT狀態連接。在我們常見的互聯網架構中(NGINX反代跟NGINX,NGINX跟FPM,FPM跟redis、mysql、memcache等),減少TIME-WAIT狀態的TCP連接,最有效的是使用長連接,不要用短連接,尤其是負載均衡跟web服務器之間。尤其是鏈家事件中的PHP連不上redis。

    * 在服務端,不要啓用net.ipv4.tcp_tw_recycle,除非你能確保你的服務器網絡環境不是NAT。在服務端上啓用net.ipv4.tw_reuse對於連接進來的TCP連接來說,並沒有任何卵用。

    * 在客戶端(尤其是服務器上,某服務以客戶端形式運行時,比如上面提到的nginx反代,連接着redis、mysql的FPM等等)上啓用net.ipv4.tcp_tw_reuse,還算稍微安全的解決TIME-WAIT的方案。再開啓net.ipv4.tcp_tw_recycle的話,對客戶端(或以客戶端形式)的回收,也沒有什麼卵用,反而會發生很多詭異的事情(尤其是FPM這種服務器上,相對nginx是服務端,相對redis是客戶端)。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章