1. tw_reuse,tw_recycle 必須在客戶端和服務端timestamps 開啓時才管用(默認打開)mysql
2. tw_reuse 只對客戶端起做用,開啓後客戶端在1s內回收linux
3. tw_recycle 對客戶端和服務器同時起做用,開啓後在 3.5*RTO 內回收,RTO 200ms~ 120s 具體時間視網絡情況。內網情況比tw_reuse 稍快,公網尤爲移動網絡大多要比tw_reuse 慢,優勢就是可以回收服務端的TIME_WAIT數量nginx
* linux 內核文檔中,對net.ipv4.tcp_tw_recycle的描述並非很明確。
web
tcp_tw_recycle (Boolean; default: disabled; since Linux 2.4)[譯者注:來自linux man tcp的描述]redis
Enable fast recycling of TIME-WAIT sockets. Enabling this option is not recommended since this causes算法
problems when working with NAT (Network Address Translation).sql
啓用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參數,增長客戶端端口可用範圍。
增長服務端端口,多監聽一些端口,好比8一、8二、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是客戶端)。