長連接服務開發的一些實戰經驗分享

linux下我們開發的程序通常扮演者兩種角色,要麼是服務端程序即提供listen 節點給業務發調用,另外一種是訪問別的服務獲取一些數據進行進一步處理。

如果你的程序是一個有網絡交互的程序,並且是頻繁的網絡交互,為了提高性能,你會盡可能的重複利用已經存在的socket連接,所以就有了我們所謂的連接池,比如mysql連接池,redis連接池。如果每次業務網絡請求都需要進行一次socket連接,一次tcp連接需要三次握手,簡單的思考一下TTL就知道這裡的服務性能是多少了,如果是服務端和客戶端在不同的機房比如一個在北京、一個在深圳單單是物理距離就要30+ms的延遲。所以我們在開發過程中都會盡可能的重複利用已經建立好的連接。如果一個號稱是高性能的TCP服務端程序(網絡IO密集型),如果不是使用長連接那純粹那所謂的高性能估計也噱頭。

長連接服務開發的一些實戰經驗分享

Fig Redis 連接池

這裡要簡單介紹下連接複用、IO複用、併發使用鏈路的區別:

1)連接複用就是利用已經存在的socket鏈路,來發起新的網絡請求

2) IO複用,他是一種網絡IO、磁盤IO的模型,利用這些模型來更方便、更高效地管理多個socket鏈路而已

3) 併發使用鏈路,他是在1)的基礎之上,一個鏈路可以併發發起多個網絡請求(這裡的併發實際上也是串行的,只不過是他不需要等待前一個請求1的應答回來就可以發起另外一個新的請求2),比如HTTP/2就是屬於併發使用鏈路,HTTP/1.x 、redis協議都是屬於連接複用的層面。

如果想要重複利用已經建立好的連接那麼就必須要確保鏈路是長期active的。所謂的長連接無非就是保證服務端跟客戶端的socket在linux內核中維護的時間戳在合理的範圍內。如何保持這個時間戳?大家可能都很容易想到可以採用心跳來保持tcp長連接。心跳機制就是通過一些極小的網絡數據消耗(但是網絡協議棧的頭開銷是免不了的),來告訴對端我還活著,別把我清理掉。

長連接服務開發的一些實戰經驗分享

心跳分為業務心跳、tcp探活心跳。

1)業務心跳由業務通過開發代碼定時給對端發送一個類似ping的這種請求。

2)TCP探活心跳(tcp_keepalive_time)

它的作用主要有兩點,一維持連接,二是能把一些異常斷開的鏈路及時回收(半打開的連接)

它是系統層面的心跳機制,具體時間長短需要充分了解對端的超時機制,才能合理設置,尤其是涉及到移動客戶端的這裡就需要非常謹慎了,過於頻繁的心跳會導致用戶流量消耗過快、功耗過高(耗電快),說不定你的app很快就上黑名單了。

另外如果你的服務是部署在雲上,則需要了解雲上的虛擬化session超時時長才能合理的設置

比如阿~雲上宿主機和客戶機(虛擬機)的鏈路如果超過15分鐘沒有任何業務數據,鏈路就會被回收,回收了還不告訴客戶端和服務端,等到你發數據包的時候你才發現鏈路已經斷掉了(會收到RESET包),所以大家需要根據自己的使用場景合理的配置心跳:

/proc/sys/net/ipv4/tcp_keepalive_time

/proc/sys/net/ipv4/tcp_keepalive_probes

/proc/sys/net/ipv4/tcp_keepalive_intvl

關於tcp keepalive_time,僅僅是配置上面幾個是不夠的,需要在程序中調用setsockopt,打開tcpkeepalive,你的服務建立的連接才會享受到linux內核的探活機制。(一般只需要服務端設置,當然客戶端也可以設置但是沒有這個必要)

int keepAlive = 1;
setsockopt(client_fd, SOL_SOCKET, SO_KEEPALIVE, (void*)&keepAlive, sizeof(keepAlive));

另外阿~雲上的單個物理機連接數峰值是200萬,所以分配到單個虛擬機的據我連接大概在100萬左右,如果你的服務是50萬,別人的服務是170萬長連接而且都在同一個物理機上,那麼此時肯定會出現丟包情況,要麼是你的服務丟包,要麼是別人的服務丟包,所以如果你的服務是部署在阿~雲上需要注意下自己服務長連接的限制(不同的套餐最大連接數限制可能不同),提前做好溝通。


分享到:


相關文章: