在高並發短連接的TCP服務器上,當服務器處理完請求后立刻主動正常關閉連接。這個場景下會出現大量socket處於TIME_WAIT狀態。如果客戶端的並發量持續很高,此時部分客戶端就會顯示連接不上。我來解釋下這個場景。主動正常關閉TCP連接,都會出現TIMEWAIT。
為什么我們要關注這個高並發短連接呢?有兩個方面需要注意:
1. 高並發可以讓服務器在短時間范圍內同時占用大量端口,而端口有個0~65535的范圍,並不是很多,刨除系統和其他服務要用的,剩下的就更少了。
2. 在這個場景中,短連接表示“業務處理+傳輸數據的時間 遠遠小於 TIMEWAIT超時的時間”的連接。
這里有個相對長短的概念,比如取一個web頁面,1秒鍾的http短連接處理完業務,在關閉連接之后,這個業務用過的端口會停留在TIMEWAIT狀態幾分鍾,而這幾分鍾,其他HTTP請求來臨的時候是無法占用此端口的(占着茅坑不拉翔)。單用這個業務計算服務器的利用率會發現,服務器干正經事的時間和端口(資源)被掛着無法被使用的時間的比例是 1:幾百,服務器資源嚴重浪費。(說個題外話,從這個意義出發來考慮服務器性能調優的話,長連接業務的服務就不需要考慮TIMEWAIT狀態。同時,假如你對服務器業務場景非常熟悉,你會發現,在實際業務場景中,一般長連接對應的業務的並發量並不會很高。
綜合這兩個方面,持續的到達一定量的高並發短連接,會使服務器因端口資源不足而拒絕為一部分客戶服務。同時,這些端口都是服務器臨時分配,無法用SO_REUSEADDR選項解決這個問題。
存在即是合理的,既然TCP協議能盛行四十多年,就證明他的設計合理性。所以我們盡可能的使用其原本功能。
依靠TIME_WAIT狀態來保證我的服務器程序健壯,服務功能正常。
那是不是就不要性能了呢?並不是。如果服務器上跑的短連接業務量到了我真的必須處理這個TIMEWAIT狀態過多的問題的時候,我的原則是盡量處理,而不是跟TIMEWAIT干上,非先除之而后快。
如果盡量處理了,還是解決不了問題,仍然拒絕服務部分請求,那我會采取負載均衡來抗這些高並發的短請求。持續十萬並發的短連接請求,兩台機器,每台5萬個,應該夠用了吧。一般的業務量以及國內大部分網站其實並不需要關注這個問題,一句話,達不到時才需要關注這個問題的訪問量。
如何盡量處理TIMEWAIT過多?
編輯內核文件/etc/sysctl.conf,加入以下內容:
net.ipv4.tcp_syncookies = 1 表示開啟SYN Cookies。當出現SYN等待隊列溢出時,啟用cookies來處理,可防范少量SYN攻擊,默認為0,表示關閉;
net.ipv4.tcp_tw_reuse = 1 表示開啟重用。允許將TIME-WAIT sockets重新用於新的TCP連接,默認為0,表示關閉;
net.ipv4.tcp_tw_recycle = 1 表示開啟TCP連接中TIME-WAIT sockets的快速回收,默認為0,表示關閉。
net.ipv4.tcp_fin_timeout 修改系默認的 TIMEOUT 時間
http1.0 默認是短連接,client端主動發起,server端服務完畢后,將連接關畢,client端也借此判斷數據發送完畢。所以http1.0時代,服務端容易有較多的time_wait。
- 如果http 1.0協議,client希望使用長連接,需要在header中設置connection:keep-alive
http1.1默認是長連接
- 如果client不希望使用長連接,需要在header中設置connection:close
- 如果server不希望使用長連接,也需要在reponse中設置connection:close
長連接的問題
keep-alive模式帶來的問題:此時服務端不關閉連接,客戶端如何知道消息內容是否發送完畢(曾經因為對http1.0沒有主動關閉連接,導致一些老舊php上的的socket http包因為等待服務端關閉連接判斷消息發送完畢而超時)
keep-alive模式下,通過兩種方式
- 服務端reqponse設置content-length參數:告知內容大小,客戶端收到指定內容長度后即可關閉連接。客戶端發送post時也會采用這種方式,如若content-length大於實際發生的值會超時,小於則服務端會響應400。這種模式有缺陷,動態頁面服務器也不知道內容大小,只有等內容全准備完畢后才知道,若等到此時發送,效率太低了。
- transfer-encoding:chunked模式:此模式下簡單說是規定一直特殊的響應data格式,此種格式中會包含body結束的標志。客戶端收到此標志即知道數據發送完畢,可以組裝解密數據了。