epoll非阻塞伺服器,在20k並發測試結束產生大量establish狀態假連接,可能原因?

在練習epoll編程時,採用邊沿觸發模式,非阻塞讀寫。

寫了一個簡單的http伺服器,響應請求,返回一個響應。

已經做了循環讀寫一直到出錯,排除Eagain等錯誤等問題,read/write 出錯關閉連接處理,響應epoll出錯事件並關閉連接(感覺上是都考慮了,或是還不夠)

伺服器是個雙核,Ubuntu。區域網是百兆乙太網(請求帶寬不大,因為響應很小)。

用 ab(ApacheBench ) 做壓力測試工具,在 20k 並發時,每秒可以處理60k左右的請求。cpu單核基本跑滿。

但是最後ab測試結束後,伺服器端保留了大量的establish狀態連接。

在測試結束後ab所在 client PC 對應埠已無連接。

a:這時如果 kill 掉伺服器程序,這些 establish 連接立刻消失,而不是 time_wait 狀態。

(此時對面已經沒有連接了不是應該超時在time_wait?)。

ps:又在 kill 掉 server 前,將 client 的網線拔掉,然後再 kill server 程序,這時,大量的 伺服器埠的那些死連接卡在了 FIN_WAIT1 (發送了 FIN 沒收到對方 ACK)狀態。

ps 和 a 的情況的對比(也就是 kill server 時,client 是否正常連接),也就是說:

1)出問題的情況確實是某種原因導致 server 沒有收到 client 的 FIN。

2)Client 雖然早已完成了 TCP 連接的關閉,但是還是能響應 server 的 FIN 包。

3)前面 client(ab程序)在關閉連接時,其實server協議棧正確的做了響應(返回了 ACK 和 FIN 包),但是協議棧卻把連接的close狀態丟失了?(亦或是 client 的 ab 自己把連接清除了?但是之前其他測試是會出現 FIN_WAIT1 等狀態的)

(問題越補充越多,感謝能夠讀完的大神,請給些思路也好,待我解決我會總結下答案。)

那麼這種情況:

1)這麼多establish狀態的連接可能是怎麼產生的?(這個是某種原因導致 socket FIN 未收到,但是感覺協議棧已經收到並處理了對方和自己的 FIN/ACK?)

2)是程序問題,還是由於並發大TCP協議棧出錯?是一種常見問題,需要通過保活來處理嗎?

3)為何 client 退出後(它的 FIN 被 server 丟掉了)沒有卡在 time_wait/FIN_WAIT2 狀態(協議棧已經正確響應了?)

4)上午想想又困惑了,為啥 TIME_WAIT 也沒了?不是只要是關閉就會產生 TIME_WAIT 嘛?(難道被 ab 主動給消除掉了?)

5)非阻塞時 close() 的狀態需要判斷嗎?(網上討論的比較少,我捕獲了一下,並沒有返回錯誤。 )

ps:圖

1)client 端 ab 完成了測試後,立即查看埠佔用,並沒有殘存的連接(正確退出了?):

2)server 端在一次測試後殘存的大量死連接(只列了幾條):


Client 發的 FIN 丟包。


抓包吧,client / server 同時抓,只抓 FIN / FINACK ,這樣才能搞清楚到底發生了什麼


1)ab close了連接,而你寫的服務端的連接都是ESTABLISHED,最可能的情況是ab的FIN丟包了(可能是伺服器所在主機cpu使用率過高未響應),此時客戶端在FIN_WAIT_1狀態,進入TCP超時重傳一定次數後依然沒有收到ACK, 放棄了連接,不會繼續FIN_WAIT_2和TIME_WAIT狀態了。

2)如果你kill掉服務端程序,伺服器所在的主機協議棧會給客戶端主機發送FIN,但客戶端程序已經結束了,客戶端所在主機的協議棧會以RST響應之, 導致伺服器所在主機立刻釋放連接。


在做AB壓測時,我也遇到類似問題,client壓測過去之後,client有少量timewait,而server端有大量timewait,但是其數值仍然小於/proc/sys/net/ipv4/tcp_max_tw_buckets。

所以產生如下懷疑

1. ab壓測時並沒有發送FIN給服務端,直接關閉connect。而服務端只能保持timewait狀態,等待/proc/sys/net/ipv4/tcp_fin_timeout超時關閉,而該內核設置通常為60秒。

2. ab壓測時發送了FIN給服務端,但是不等服務端ACK即將connect關閉。服務端收到FIN返回ACK信息時發現connect已經關閉,server端不正常處理FIN信息?

糾正題主1處錯誤,從題主截圖來看ab並發並非20k而是2k, 另外推薦使用ss命令取代netstat

ss -s 可以直接查看tcp的所有狀態的當前情況

另外 如果單機要實現20k並發需要做很多內核上的調整,如下(但不限於如下):

1. 在滿足業務的情況下,盡量減少timewait close等待時間 /proc/sys/net/ipv4/tcp_fin_timeout

2. 開啟TCP timewait重用,但是慎重,可能帶來不可預知的問題 /proc/sys/net/ipv4/tcp_tw_reuse

3. 每個socket內存耗用大概在15-20k左右,再加上應用層的內存佔用和各種buffer,可以估算下內存和socket的對等關係, /proc/sys/net/ipv4/tcp_rmem /proc/sys/net/ipv4/tcp_wmem

4. 修改ulimit限制 如 open files 系統同時最大打開的文件描述符等

5. 基於linux2.6及以後內核的epoll並發機制,實現非堵塞並發,如果是堵塞並發還是遠離20k這個目標吧

將如上5個細節調整到系統最優,或許有望可以實現20K並發。


client在close套接字的時候是否判斷close成功沒有?


客戶端的fin包丟了,fin_wait1 狀態下 fin包重試是有次數限制的,重試仍不成功鏈接被釋放了。另外,你kill服務,因為客戶端這個鏈接已經釋放,所以收到fin包直接回復rest鏈接直接被釋放。但是拔網線,服務端一直收不到客戶端的響應,所以卡在fin_wait1重試


能說下關閉連接的發起方是哪一邊嗎?謝謝


不會是fin lose,應該是你代碼的問題。因為netstat 裡面sendbuff recv buff都是空的,不存在過載現象。你可以netstat -s 看細節,有無drop包。最認真的做法是wireshark 抓包來驗證。必須匿


推薦閱讀:

用 wireshark抓包工具能做到哪些有趣的事情?
為什麼 ssh root@163.com 或者 ssh root@zhihu.com 都沒有反應?
最近在nginx1.9.1中支持了reuse_port這個功能 是准許多個socket監聽同一個埠?
在 TCP/IP 協議中,客戶端發出請求,服務端回復響應,客戶端在數據鏈路層會校驗目的 MAC 地址嗎???
為什麼http下載不是直接下載而是一點一點地加快速度?如果直接下載會有什麼後果?

TAG:伺服器 | epoll | Socket | TCP | 並發測試 |