《即時消息技術剖析與實戰》學習筆記4——IM系統如何保證消息的可靠性
- 2019 年 10 月 3 日
- 筆記
IM 系統中,保證消息的可靠投遞主要體現在兩方面,一是消息的不丟失,二是消息的不重複。
一、消息不丟失
消息丟失的原因
首先看一下發送消息的流程,如下圖所示:
用戶 A 發出的消息,先到達IM服務端(步驟1),由服務端暫存(步驟2),成功後,服務端將成功的結果返回給用戶A(步驟3),同時將消息推送給用戶B(步驟4)。
在這個過程中,丟失消息有以下幾種情況:
1)步驟 1 因為網路不通等原因導致用戶A把消息發送到IM伺服器失敗;
2)步驟 2 IM伺服器存儲消息失敗;
3)步驟 3 用戶A在超時時間內未收到IM伺服器返回的結果;
4)步驟 4 由於IM伺服器斷電等原因導致消息未能成功推送給用戶B(但步驟 3 用戶A可以收到IM伺服器返回的響應成功結果);
5)步驟 4 消息成功推送給用戶B的設備,但用戶B的設備因為一些原因如設備寫入本地DB失敗等,也會導致消息丟失。
前三種情況,用戶A將被提示消息發送失敗;後兩種情況,用戶B未收到消息。
消息丟失的解決方案
大部分場景中,業務層ACK確認機制 + 消息重傳機制 + 消息完整性檢查,能解決消息丟失的問題。
1.業務層的ACK確認機制和重傳機制
ACK是確認字元(Acknowledge character)的意思,TCP協議默認提供了ACK機制,如果接收方成功接收到數據,就會回復一個ACK數據,表示發送方發出的數據已確認接收無誤,在「三次握手」、「四次揮手」中經常見到。
ACK確認機制:TCP傳輸時將每個位元組的數據都進行編號,即序列號。TCP傳輸的過程中,每次接收方收到數據後,都會對傳輸方進行確認應答,也就是發送ACK報文。這個ACK報文當中帶有對應的確認序列號,告訴發送方,接收到了哪些數據,下一次的數據從哪裡發。有了序列號能夠將接收到的數據根據序列號排序,並且去掉重複序列號的數據。這也是TCP傳輸可靠性的保證之一。
重傳機制:發送方發送一部分數據後,都會等待接收方發送的ACK報文,並解析ACK報文,判斷數據是否傳輸成功。發送方遲遲收不到ACK報文的原因可能有兩個:
1)數據在傳輸過程中由於網路原因等直接全體丟包,接收方沒有接收到;
2)接收方接收到了響應的數據,但是發送的ACK報文響應卻由於網路原因丟包了。
超時重傳機制就是發送方在發送完數據後等待一個時間,如果在超時時間內沒有接收到ACK報文,就重新發送數據。如果是上述的第一個原因,接收方收到二次重發的數據後,便進行ACK應答。如果是第二個原因,接收方發現接收的數據已存在,就直接丟棄,仍舊發送ACK應答。
業務層的ACK確認機制參考了TCP的ACK確認機制,其策略是IM伺服器在推送消息時,攜帶一個標識SID(安全標識符,類似TCP的sequenceId),推送出消息後會將當前消息添加到「待ACK消息列表」,客戶端B成功接收完消息後,會給IM伺服器回一個業務層的ACK包,包中攜帶有本條接收消息的SID,IM伺服器接收後,會從「待ACK消息列表」記錄中刪除此條消息,本次推送才算真正結束。
業務層的消息重傳機制也參考了TCP協議的重傳機制,IM伺服器的「等待ACK隊列」一般會維護一個超時計時器,一定時間內如果沒有收到用戶B發回的ACK包,就從「等待ACK隊列」中重新拉取並進行重推。
為什麼有了TCP協議本身的ACK機制,還需要業務層的ACK機制?
這是因為TCP屬於傳輸層,而IM服務屬於應用層。TCP的ACK保證網路傳輸層的可靠性,即消息是否送達,但不能保證數據能夠被應用層正確可靠處理;業務層ACK進行消息是否送達和是否正確處理的邏輯,達到不丟消息、消息不重複的目的。
2.時間戳比對檢查消息完整性
在上面列舉的丟失消息的第 4 種可能性中,如果步驟 4 IM伺服器將消息推送出去後就宕機了,而這條消息又因為某些原因丟失了,伺服器由於宕機無法觸發重傳機制,導致用戶B收不到該消息。可以採取「時間戳比對」機制進行完整性檢查。
時間戳比對過程如下:
1)IM伺服器給用戶B推送msg1,同時帶上一個最新時間戳timestamp1。用戶B收到msg1後,更新本地的時間戳為timestamp1;
2)IM伺服器給用戶B推送msg2,同時帶上一個最新時間戳timestamp2。由於某種原因,用戶B和IM伺服器的連接斷開,導致msg2沒有成功推送到用戶B;
3)用戶B和IM伺服器重新建立連接後,將本地的時間戳timestamp1發送給IM伺服器,IM伺服器將時間戳大於timestamp1的所有消息全部發送給用戶B,同時帶上時間戳timestamp2(這裡假設時間戳大於timestamp1的消息只有msg2,如果有msg3、msg4等多條消息,應取最新消息的時間戳);
4)用戶B收到msg2後,更新本地的時間戳為timestamp2。
通過這樣的比對可以有效解決消息丟失的問題。但時間戳由於有時鐘不同步、或者一個時間戳內多條消息的可能性,存在誤差,因此可以使用全局的自增序列版本號來代替。
二、消息不重複
消息重複的原因
在上面列舉的丟失消息的幾種可能性中,第 3 種可能性存在一種場景,步驟 4 將消息成功推送給用戶B,但步驟 3 因為某些原因導致超時、用戶A收不到響應,這個時候會觸發重傳機制,用戶A重新發送請求,用戶B可能會收到重複消息。
消息重複的解決方案
IM伺服器推送消息時,攜帶一個Sequence ID,這個Sequence ID在本次連接會話中唯一,同時針對同一條消息不變。當接收方接收到消息後,會根據這個Sequence ID來進行業務層的去重,可以有效地保證消息的不重複。
三、小結
通過業務層的ACK機制、重傳機制和完整性檢查,可以有效解決推送過程中消息丟失的問題;
通過客戶端的去重機制,可以有效解決消息重複的問題。