1. 程式人生 > >《即時訊息技術剖析與實戰》學習筆記5——IM系統如何保證訊息的一致性

《即時訊息技術剖析與實戰》學習筆記5——IM系統如何保證訊息的一致性

一、什麼是訊息一致性

訊息一致性指的是訊息的時序一致性,即訊息收發的一致性。如果不能保證時序一致性,就會造成聊天語義不連貫,引起誤會。

對於點對點的聊天場景,時序一致性保證接收方的接收順序和傳送方的發出順序一致;對於群聊場景,時序一致性保證所有接收人看到的訊息展現順序一致。

二、訊息一致性的難點

1.多傳送方、多接收方、服務端多執行緒併發處理情況下,無法保證時序一致性。

2.分散式環境下,多個機器的本地時鐘不一致,沒有“全域性時鐘”,不能用“本地時間”保證時序的一致性。

三、訊息的一致性的實現

▶1. “全域性序號生成器”作為“時序基準”

為什麼不以客戶端(傳送方)的本地序號/本地時鐘作為“時序基準”?

如果以傳送方的本地時鐘作為時序基準,傳送方傳送訊息時,將訊息本身和本地的時間戳或一個本地維護的序號傳送到IM服務端,IM服務端再將這個訊息和時間戳或序號傳送給訊息接收方,訊息接收方根據這個時間戳或序號進行排序。

若傳送方隨時調整時鐘,會導致時間戳回退;若傳送方重灌應用,會導致序號清零,從而回退。

而針對多傳送方場景,如群聊和多點登入,存在同一時鐘的某個時間點、多條訊息傳送給同一接收物件的可能。比如一個群聊內,使用者A先發言、使用者B後發言,但如果使用者A的時鐘比使用者B的時鐘慢,已傳送方的本地時鐘作為“時序基準”就會出問題;再比如微信在手機、電腦上同時登入,兩臺裝置可以給某一接收方傳送訊息;若裝置的本地時鐘不一致,接收方可能會出現訊息不連貫的問題。

為什麼不以伺服器的本地時鐘作為“時序基準”?

如果以IM伺服器的本地時鐘作為時序基準,傳送方將訊息傳送到IM服務端,IM服務端依據自身伺服器的時鐘生成一個時間戳,然後將訊息和時間戳一起傳送給訊息接收方,訊息接收方根據這個時間戳進行排序。
一旦IM服務叢集化部署,就會出現多臺伺服器本地時鐘不一致的問題。雖然多臺伺服器可以通過NTP時間同步服務,但依然存在一定的時間誤差。

將“全域性序號生成器”作為“時序基準”,可以解決每條訊息沒有標準“生產日期”的問題,按著實現方式可以分為兩類,一是支援單調自增序號的生成,如Redis的原子自增命令incr、MySQL的自增ID,二是分散式時間相關的ID生成,如snowflake演算法、時間相關的分散式序號生成服務等。

對於群聊和多點登入的場景,只需要保證一個群的訊息有序即可,無需全域性的跨多個群的絕對時序性。每個群聊有其獨立的“ID生成器”,可以通過雜湊規則路由到對應的主庫例項上,降低多個群聊共用一個“ID生成器”的壓力。

▶2. 訊息整流
當 IM 服務端接收到訊息後,若 IM 伺服器是叢集化部署,可能因為伺服器效能的差異,導致後收到的訊息先發出去;又或者多執行緒處理訊息的流程不能保證先到達的訊息先發送出去,從而使接收方收到的訊息順序有誤,因此需要訊息整流。訊息整流又分為服務端包內整流和客戶端(接收方)整流。

  • 服務端包內整流

    比如實現離線推送,當用戶上線,閘道器機會通知業務層使用者已上線,業務層就會把該使用者的多條離線訊息 pub 給這個閘道器機的 Topic,閘道器機再把收到的多條訊息通過長連線推送給使用者。

    圖片來源於《即時訊息技術剖析與實戰》第 05 講

    1)生產者為每個訊息包生成一個packageID,為包內的每條訊息加個有序自增的seqID;

    2)消費者根據每條訊息的packageID 和 seqID 進行整流和排序;

    3)執行模組只有在一定超時時間內完整有序地收到所有訊息才執行最終操作,否則根據業務需要觸發重試或直接放棄操作。

  • 訊息接收端整流

    1)傳送方傳送訊息時,連同訊息和序號一起傳送給接收方;

    2)接收方接收到訊息後,先去查詢上一條訊息的序號,然後比對收到的序號和上一條訊息的序號;

    3)如果收到的訊息序號大於上一條訊息序號,直接追加;反之則去查詢小於該序號的最大訊息序號,並追加到其後。

四、參考

訊息時序一致性還可以參考58沈劍大佬的文章:訊息“時序”與“一致性”為何這麼難?,又會有不一樣的收貨