常見的IM類應用,比如遊戲,直播,聊天室或者客服系統,一般都要依靠伺服器端做訊息中轉,將從傳送方接受的訊息推播給接收方,為保證可靠,快速到達對端,⼤部分IM使⽤長連線建⽴通道,並且建⽴TCP連線和使用者裝置的對映關係,長連線⼀旦建⽴,就會⼀直存在,除非意外被中斷,並依靠該連結接受和推播訊息。
心跳機制存在的意義:這個長連線並不是物理意義上的連線,⽽是⼀個無感知的虛擬TCP連線,即使斷開兩端也不會感知,因此心跳機制來讓連線在出現問題的時候,迅速的讓對端感知到,心跳要做的就是快速不間斷的識別來探測連線可⽤性。
降低伺服器端維持連線的開銷:伺服器端除了維持⽹絡連線和使用者裝置的對映之外,還會儲存⼀些包括app版本號,os系統型別等資訊,這些資訊第⼀次建立連線傳送,以後都維持到伺服器端快取,如果存在⼤量的斷開的連線,就會造成具柄和快取的浪費,因此⼼跳機制保證了在連線斷開後儘早的清理伺服器端連線使⽤的資源。
⽀持使用者端斷線重連:⼀般移動裝置連線的都是運營商的內網,⽽IPv4地址總共約為43億個,因此為了節省IP地址資源,運營商通常會採⽤NAT⼿段,為聯⽹裝置分配內⽹IP,變成了內網IP:port-外網IP:port的對映,使得內網IP的⼿機能和外網聯通,但為了提⾼IP利⽤率,如果⼀個連線⼀段時間沒有資料收發,運營商就會把這個對映從NAT對映表清除掉,為了避免長連線被被運營商下掉,就需要長跳機制定期傳送⼀些資料對連線讓運營商以為這個連結還在用從而進行保活。
一般有三種:
⼼跳作⽤:
問題:可否結合TCP的keep-Alive和應⽤層心跳使⽤?
tcp的keep-alive解決網路可⽤性,應⽤層keep-alive解決⽹絡和服務可⽤性,但是應⽤層無法區分網路還是服務問題,因此加上tcp心跳包可以進⼀步區分,tcp心跳探測到連結正常,而此時應用層通訊出現問題,則可能是服務出現問題。
微服務專案中為了讓客⼾端知道具體服務部署的地址,通常要使用服務註冊中心。
通過註冊中心,即使需要擴容,或者摘除節點,也不⽤重啟客⼾端伺服器。
主動探測:服務要開啟⼀個埠,然後由註冊中心每隔⼀段時間(比如30秒)探測這些埠是否可⽤,如果不可⽤就從服務列表摘除。
問題:
心跳機制:服務節點註冊到服務中心後,會按照⼀定的時間間隔向註冊中心傳送心跳包,註冊中心收到後會更新最新續約時間,啟動⼀個定時器,如果⼀定時間還沒有收到心跳,就認為服務不可⽤。服務也需要檢測是否存活,那麼也可以考慮使⽤心跳機制來檢測。
在即時通訊系統中,比如聊天室,一般都是採用傳送方與伺服器建立一條TCP連線,接收方也會與伺服器建立一條TCP連線。
首先使用者有⼀個登陸的過程:
具體的即使通訊系統應用場景可以瞭解:實時線上線上人數
因為要基於該TCP連線傳送訊息,所以要先保證該連線的可靠性,因此要使用如上中所說要通過心跳及時的探測到連結的是否仍有效。
邏輯如下:
實現上:可以用redis的hash結構,配合記錄所有的使用者的最後線上時間。
hash結構中字串是一個key對應一個value,value中通常只有一個對應key的資料,而hash中,把很多個資料(field:value)存到一個value中,Hash型別可以看成具有String Key和String Value的map容器新增和刪除操作都是O(1)(平均)的複雜度,每個hash可以儲存232-1 鍵值對(40多億),操作複雜度和儲存上限一般不會有問題。
hash的key為chat-room
,表示這是聊天室key。
當用戶傳送心跳,設定hash欄位,field為user_id,value為當前系統時間戳
hset chat-room user_id time.Now().Unix()
伺服器端定時指令碼檢測邏輯如下
res = hgetall chat-room # 將hash所有欄位fields和value儲存在res字典中
for field, value := range res: # 遍歷所有欄位,也就是檢視所有使用者的上一次心跳時間
if time.Now().Unix() - value > 60 * 3: # 如果上一次心跳距離現在超過3分鐘,則認為連線斷開,連線資源。
clearConnect()