熟悉基於TCP協議進行linux高性能、高併發服務端編程的朋友確定應該知道每一個文件描述符及其所佔的資源對併發量的影響。在這種7*24甚至*365不間斷運行的服務器上,一個描述符被浪費,兩個被浪費...若是被浪費的多了,那還何談高併發,高性能。除去文件描述被正常佔用的狀況外,是什麼致使了咱們可用的文件描述符愈來愈少呢?linux
什麼是半開鏈接?編程
當客戶端與服務器創建起正常的TCP鏈接後,若是客戶主機掉線(網線斷開)、電源掉電、或系統崩潰,服務器進程將永遠不會知道(經過咱們經常使用的select,epoll監測不到斷開或錯誤事件),若是不主動處理或重啓系統的話對於服務端來講會一直維持着這個鏈接,任憑服務端進程如何望穿秋水,也永遠再等不到客戶端的任何迴應。這種狀況就是半開鏈接,浪費了服務器端可用的文件描述符。服務器
如何處理?網絡
熟悉套接字通用選項的朋友必定已經有了想法。TCP套接字不是有個保持存活選項SO_KEEPALIVE嘛,若是在兩個小時以內在該套接字的任何一個方向上都沒數據交換,TCP就自動給對端發送一個保持存活探測分節,若是此TCP探測分節的響應爲RST,說明對端已經崩潰且已經從新啓動,該套接字的待處理錯誤被置爲ECONNRESET,套接字自己則被關閉。若是沒有對此TCP探測分節的任何響應,該套接字的處理錯誤就被置爲ETIMEOUT,套接字自己則被關閉。多線程
確實,這個選項確實能夠處理咱們前面遇到的TCP半開鏈接的問題,可是默認兩小時間隔探測的實時性是否是差了些呢?固然,咱們能夠經過修改內核參數改小時間間隔,完美了吧?可是必須注意的是大多數內核是基於整個內核維護這些時間參數的,而不是基於每一個套接字維護的,所以若是把無活動週期從兩小時改成(好比)2分鐘,那將影響到該主機上全部開啓了此選項的套接字。我想你們都不會願意承擔服務器端的這種不肯定性吧。另外,心跳除了說明應用程序還活着(進程存在,網絡暢通),更重要的是代表應用程序能正常工做。而SO_KEEPALIVE由操做系統負責探查,即使是進程死鎖或有其餘異常,操做系統也會正常收發TCP keepalive消息,而對方沒法得知這一異常。併發
不要緊,其實咱們能夠在應用層模擬SO_KEEPALIVE的方式,用心跳包來模擬保活探測分節。因爲服務器一般要承擔成千上萬的併發鏈接,因此確定是由客戶端在應用層進行心跳來模擬保活探測分節,客戶端屢次收不到服務器的響應時可終止此TCP鏈接,而服務端可監測客戶端的心跳包,若在必定時間間隔內未收到任何來自客戶端的心跳包則能夠終止此TCP鏈接,這樣就有效避免了TCP半開鏈接的狀況。ide
參考書籍:高併發
《UNIX網絡編程:卷1》性能
《Linux多線程服務端編程》操作系統