1. 程式人生 > >tcp短連線TIME_WAIT問題解決方法大全(3)——tcp_tw_recycle

tcp短連線TIME_WAIT問題解決方法大全(3)——tcp_tw_recycle

【tcp_tw_recycle和tcp_timestamps】
參考官方文件(http://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt),tcp_tw_recycle解釋如下:
tcp_tw_recycle選項作用為:Enable fast recycling TIME-WAIT sockets. Default value is 0.
tcp_timestamps選項作用為:Enable timestamps as defined in RFC1323. Default value is 1.


這兩個選項是linux核心提供的控制選項,和具體的應用程式沒有關係,而且網上也能夠查詢到大量的相關資料,但資訊都不夠完整,最主要的幾個問題如下;
1)快速回收到底有多快?
2)有的資料說只要開啟tcp_tw_recycle即可,有的又說要tcp_timestamps同時開啟,具體是哪個正確?
3)為什麼從虛擬機器NAT出去發起客戶端連線時選項無效,非虛擬機器連線就有效?


為了回答上面的疑問,只能看程式碼,看出一些相關的程式碼供大家參考:
=====linux-2.6.37 net/ipv4/tcp_minisocks.c 269======
void tcp_time_wait(struct sock *sk, int state, int timeo)
{
struct inet_timewait_sock *tw = NULL;
const struct inet_connection_sock *icsk = inet_csk(sk);
const struct tcp_sock *tp = tcp_sk(sk);
int recycle_ok = 0;


    //判斷是否快速回收,這裡可以看出tcp_tw_recycle和tcp_timestamps兩個選項都開啟的時候才進行快速回收,
    //且還有進一步的判斷條件,後面會分析,這個進一步的判斷條件和第三個問題有關

if (tcp_death_row.sysctl_tw_recycle && tp->rx_opt.ts_recent_stamp)
recycle_ok = icsk->icsk_af_ops->remember_stamp(sk);


if (tcp_death_row.tw_count < tcp_death_row.sysctl_max_tw_buckets)
tw = inet_twsk_alloc(sk, state);


if (tw != NULL) {
struct tcp_timewait_sock *tcptw = tcp_twsk((struct sock *)tw);
        //計算快速回收的時間,等於 RTO * 3.5,回答第一個問題的關鍵是RTO(Retransmission Timeout)大概是多少

const int rto = (icsk->icsk_rto << 2) - (icsk->icsk_rto >> 1);
        
        //。。。。。。此處省略很多程式碼。。。。。。
        
    if (recycle_ok) {
            //設定快速回收的時間
tw->tw_timeout = rto;
} else {
tw->tw_timeout = TCP_TIMEWAIT_LEN;
if (state == TCP_TIME_WAIT)
timeo = TCP_TIMEWAIT_LEN;
}
        
        //。。。。。。此處省略很多程式碼。。。。。。
}


RFC中有關於RTO計算的詳細規定,一共有三個:RFC-793、RFC-2988、RFC-6298,Linux的實現是參考RFC-2988。
對於這些演算法的規定和Linuxde 實現,有興趣的同學可以自己深入研究,實際應用中我們只要記住Linux如下兩個邊界值:
=====linux-2.6.37 net/ipv4/tcp.c 126================
#define TCP_RTO_MAX((unsigned)(120*HZ))
#define TCP_RTO_MIN((unsigned)(HZ/5))
==========================================

這裡的HZ是1s,因此可以得出RTO最大是120s,最小是200ms,對於區域網的機器來說,正常情況下RTO基本上就是200ms,因此3.5 RTO就是700ms
也就是說,快速回收是TIME_WAIT的狀態持續700ms,而不是正常的2MSL(Linux是1分鐘,請參考:include/net/tcp.h 109行TCP_TIMEWAIT_LEN定義)。
實測結果也驗證了這個推論,不停的檢視TIME_WAIT狀態的連線,偶爾能看到1個。

最後一個問題是為什麼從虛擬機發起的連線即使設定了tcp_tw_recycle和tcp_timestamps,也不會快速回收,繼續看程式碼:
tcp_time_wait函式中的程式碼行:recycle_ok = icsk->icsk_af_ops->remember_stamp(sk);對應的實現如下:
=====linux-2.6.37 net/ipv4/tcp_ipv4.c 1772=====
int tcp_v4_remember_stamp(struct sock *sk)
{
    //。。。。。。此處省略很多程式碼。。。。。。


    //當獲取對端資訊時,進行快速回收,否則不進行快速回收
if (peer) {
if ((s32)(peer->tcp_ts - tp->rx_opt.ts_recent) <= 0 ||
   ((u32)get_seconds() - peer->tcp_ts_stamp > TCP_PAWS_MSL &&
    peer->tcp_ts_stamp <= (u32)tp->rx_opt.ts_recent_stamp)) {
peer->tcp_ts_stamp = (u32)tp->rx_opt.ts_recent_stamp;
peer->tcp_ts = tp->rx_opt.ts_recent;
}
if (release_it)
inet_putpeer(peer);
return 1;
}


return 0;
}
上面這段程式碼應該就是測試的時候虛擬機器環境不會釋放的原因,當使用虛擬機器NAT出去的時候,伺服器無法獲取隱藏在NAT後的機器資訊。
生產環境也出現了設定了選項,但TIME_WAIT連線數達到4W多的現象,可能和虛擬機器有關,也可能和組網有關。


總結一下:
1)快速回收到底有多快?
區域網環境下,700ms就回收;
2)有的資料說只要開啟tcp_tw_recycle即可,有的又說要tcp_timestamps同時開啟,具體是哪個正確?
需要同時開啟,但預設情況下tcp_timestamps就是開啟的,所以會有人說只要開啟tcp_tw_recycle即可;
3)為什麼從虛擬機發起客戶端連線時選項無效,非虛擬機器連線就有效?
和網路組網有關係,無法獲取對端資訊時就不進行快速回收;

綜合上面的分析和總結,可以看出這種方法不是很保險,在實際應用中可能受到虛擬機器、網路組網、防火牆之類的影響從而導致不能進行快速回收。

附: