本文做者「商文默」,有修訂和改動。php
我整理的大量IM技術文章中(見本文末「參考資料」一節),有關消息可靠性和一致性問題的文章佔了很大比重,緣由是IM這類系統拋開各類眼花繚亂的產品功能和技術特性,保證消息的可靠性和一致性幾乎是IM產品必需的素質。html
試想若是一個IM連發出的消息都不知道對方到底能不能收到、發出的聊天內容對方看到的究竟是不是「胡言亂語」(嚴重亂序問題),這樣的APP用戶確定不會讓他在手機上過夜(確定第一時間卸載了),由於最基本的聊天邏輯都沒法實現,它已經失去了IM軟件自己的意義。前端
不過,另外一個方面來說,IM系統是不標準的(雖然曾經XMPP這種協議試圖解決這個問題,但事實證實那根本不現實),各家幾乎都是自已的私有協議、不一樣的實現邏輯,這也決定了即便同一個技術問題,對於IM來講很難有固定的實現套路和標準的解決方案。git
因此,對於本文來講,文中做者雖然提供了有關IM消息「可靠性」與「一致性」問題的解決方案,但方案到底合不合理、適不適合你,這就是仁者見仁、智者見智的事了。用人話說就是:本文內容僅供參考,具體的解決方案請務結合自已的系統構架和實現狀況,多閱讀幾篇有關這個技術話題的文章,取其精華,找到適合自已的技術方案和思路纔是最明智的。github
叢所周之,即時通信聊天(IM)系統必須要解決消息可靠性及消息一致性問題(**PS:**若是具體IM系統是什麼你都還沒弄明白,先讀這篇《零基礎IM開發入門(一):什麼是IM系統?》)。算法
這兩個問題,通俗來講就是:服務器
本文會從典型的IM消息發送邏輯開始,簡單易懂地闡明消息可靠性、一致性問題的原理及可參考的技術解決方法,或許技術方案並不完美,但但願能爲你的IM技術問題解決帶來啓發。微信
IM的消息發送通常的實現過程能夠分爲兩個階段:markdown
判斷消息發送是否成功主要依據第一階段——即服務器是否接受到消息。架構
對於消息發送者來講,消息狀態能夠分爲三類:
具體來講,這三類狀態的具體意義是:
對應的消息發送流程以下圖所示:
限於篇幅,對於IM消息可靠性的基本概念和詳細原理建議閱讀《零基礎IM開發入門(三):什麼是IM系統的可靠性?》,本文着重談談解決思路。
保證消息發送第一階段(見本文「三、典型IM消息發送過程」一節)消息成功發送的方法是設立重發機制:
PS: 具體的完整方案級代碼實現,能夠參考MobileIMSDK 中有關QoS機制的代碼實現。
消息發送第二階段(見本文「三、典型IM消息發送過程」一節)服務端推送消息到接收方,若是鏈接斷開,會丟失消息。
因此要保證消息完整,就須要在創建鏈接後,根據上一條消息(已經 ACK)時間戳,獲取會話記錄,一次返回一段時間內全部消息(PS: 中大型應用中,消息的拉取也不是個簡單事情,詳情能夠閱讀《IM開發乾貨分享:如何優雅的實現大量離線消息的可靠投遞》)。
另外一種保證方法是加入定時輪詢,檢查消息完整性,具體的思路以下圖所示。
創建鏈接流程圖:
消息重發、會話記錄檢查須要考慮兩個問題:
舉兩個例子。
關於消息重發問題:
關於消息順序問題:
同上節同樣,對於IM消息一致性的基本概念和詳細原理建議閱讀《零基礎IM開發入門(四):什麼是IM系統的消息時序一致性?》。
對於消息重發問題,能夠給每條消息增長屬性 uuid 做爲消息惟一標識,重發消息 uuid 不變,前端根據 uuid 去重。大體思路就是這樣。
PS: 對於IM來講,消息ID也是個很大的技術話題,有興趣能夠讀下面這個系列:
《IM消息ID技術專題(一):微信的海量IM聊天消息序列號生成實踐(算法原理篇)》
《IM消息ID技術專題(二):微信的海量IM聊天消息序列號生成實踐(容災方案篇)》
《IM消息ID技術專題(三):解密融雲IM產品的聊天消息ID生成策略》
《IM消息ID技術專題(四):深度解密美團的分佈式ID生成算法》
對於消息排序問題: 由於在聊天中,消息的順序對於發送方的表述有重要的影響,消息不完整或順序顛倒均可能形成語意不連貫,甚至曲解。因此須要保證發送方發送消息順序,而會話雙方消息排序須要考慮實際狀況。
在通常的認知裏: 狀態是正在發送的消息,應該尚未被對方看到,只有發送成功的消息,纔會被對方看到。但在實現中,消息發送成功是以服務器接收消息並返回 ACK 成功爲判斷依據,而不是被對方接收到。
那麼就會出現這樣一個問題: 若是一條消息狀態是正在發送,此時收到一條消息,那麼收到的消息是在正在發送的消息以前仍是以後?
這是一個上下文關係,關鍵問題是:發送方是以哪條所見消息爲依據發送消息的。
這裏提供一種思路: 借鑑分佈式系統中的向量時鐘算法(見《分佈式系統中的向量時鐘算法》)。
先簡單描述向量時鐘算法:
向量時鐘算法用於在分佈式系統中生成事件偏序關係,並糾正因果關係。一個系統包含 N 個節點,每一個節點產生的消息體中包含該節點的邏輯時鐘,總體系統的向量時鐘由 N 維邏輯時鐘組成,並在每一個節點產生的消息體中傳遞。
簡單來講,向量時鐘算法的實現原理以下:
針對上述的第5)點:
偏序關係: 若是 A 向量中的每一維都大於等於 B 向量,則 A、B 之間存在偏序關係,不然不存在偏序關係。
對於IM爲聊天消息排序來講,其實就是處理聊天消息的上下文語境,決定消息之間的因果關係。
參考向量時鐘算法: 假設有 N 個消息會話方,系統的向量時鐘由 N 維時鐘組成,向量時鐘在各方發送的消息體中傳遞,並依據向量時鐘排序。
具體實現思路以下:
針對上述第4)點:
向量時鐘在理論上能夠解決大部分消息一致性的問題,但在實現中還須要考慮實際使用時的體驗。
這其中最須要關注的問題是: 是否要強制排序,或者說,若是實際顯示順序和向量時鐘之間的偏序關係不一致,是否要移動消息之間的順序。
舉個例子: 在一個有多人的會話中,若是有一方網速特別慢,收不到消息,也發不出消息。在他看到的最後的消息以後,其餘人已經開始新的話題,這時他關於上一個話題的消息終於發送成功,並被其餘人收到。
此時就存在這樣一個問題: 這條關於上一個話題的消息是顯示在最後,仍是移到較早時間?
IM 的場景不少,也很複雜,更多的時候須要從產品角度考慮問題。
對於消息是否須要排序的問題,這裏只提出一個比較通用的方案: 建議會話中不強制排序,會話歷史記錄中按照向量時鐘的偏序關係進行排序。
對於 IM 系統消息可靠性及一致性問題,經過消息重發機制保證消息成功被服務端接收,經過會話記錄檢查保證收取消息完整,從而保證整個消息發送過程的可靠性。使用 uuid 消息去重,參考向量時鐘算法進行消息排序,爲保證消息一致性提供一種解決方案。
總之,IM這類系統看似簡單,實則水深似海,若是你是IM開發新手,能夠從《新手入門一篇就夠:從零開發移動端IM》這篇入手系統學習。若是你自認爲已經是IM老手,這裏整理的 IM中大型架構設計 方面的文章或許能夠參考一下。
[2] 零基礎IM開發入門(四):什麼是IM系統的消息時序一致性?
[3] IM消息送達保證機制實現(一):保證在線實時消息的可靠投遞
[4] IM消息送達保證機制實現(二):保證離線消息的可靠投遞
[9] IM開發乾貨分享:如何優雅的實現大量離線消息的可靠投遞
[10] 從客戶端的角度來談談移動端IM的消息可靠性和送達機制
本文已同步發佈於**:** www.52im.net/thread-3574…