零基礎IM開發入門(四):什麼是IM系統的消息時序一致性?

本文引用了沈劍《如何保證IM實時消息的「時序性」與「一致性」?》一文的圖片和內容(因爲太懶,圖沒從新畫),原文連接在文末。php

一、引言

本文接上篇《零基礎IM開發入門(三):什麼是IM系統的可靠性?》,講解IM系統中消息時序的一致性問題。html

所謂的一致性,在IM中一般指的是消息時序的一致性,那就是:程序員

  • 1)聊天消息的上下文連續性;
  • 2)聊天消息的絕對時間序。

再具體一點,IM消息的一致性體如今:web

  • 1)單聊時:要保證發送方發出聊天消息的順序與接收方看到的順序一致;
  • 2)羣聊時:要保證全部羣員看到的聊天消息,與發送者發出消息時的絕對時間序是一致的。

IM系統中消息時序的一致性問題是個看似簡單,實則很是有難度的技術熱點話題之一,本文儘可能以通俗簡顯的文字爲你講解IM消息時序一致性問題的產品意義、發生緣由、解決思路等算法

二、系列文章

零基礎IM開發入門(一):什麼是IM系統?後端

零基礎IM開發入門(二):什麼是IM系統的實時性?安全

零基礎IM開發入門(三):什麼是IM系統的可靠性?服務器

《零基礎IM開發入門(四):什麼是IM系統的消息時序一致性?》(* 本文微信

《零基礎IM開發入門(五):什麼是IM系統的安全性? (稍後發佈)》網絡

《零基礎IM開發入門(六):什麼是IM系統的的心跳機制? (稍後發佈)》

《零基礎IM開發入門(七):如何理解並實現IM系統消息未讀數? (稍後發佈)》

《零基礎IM開發入門(八):如何理解並實現IM系統的多端消息漫遊? (稍後發佈)》

三、消息時序的一致性,對於IM的意義

現現在,因爲移動互聯網的普及,現代人的實際社交關係,幾乎徹底是靠IM這種即時通信社交工具所組織起來的,IM這種工具的重要性不言而喻。

IM在現代人的生活中,愈來愈重要,但也越來日常。如今想聯繫一我的,第一時間想到的不是打個電話,而是發個「微信」或「QQ」。是的,IM這玩意承載的意義愈來愈多。

消息時序的一致性問題,對於IM的意義,毫無疑問帶來的不僅是簡簡單單的所謂用戶體驗問題。咱們來看看例子。

假設:你跟女神的表白正進入到關鍵階段,聊着聊着就由於這爛IM,致使聊天消息前言不搭後語,此刻1000千米外你的女神真一臉蒙逼的盯着手機看你的「醉話」,後果是多麼嚴重——這半年來的「舔狗」生活、忍辱負重,算是白白被程序員這羣「格子衫」、「地中海」們給禍害了。

再往嚴重了說,就由於這爛IM,讓你失去了這可貴的藉助女神優良基因,改造家族後代顏值的絕佳機會,無不讓人痛心疾首。。。

上面這個例子,說的還只是單聊,若是是羣聊,則問題可能還會被無限放大:試想一個技術交流羣,正在激烈的爭吵着「php是世界最好語言」這種話題的時候,突然就想砸手機了,不是由於吵的太兇,而由於消息順序全亂徹底無法看,已經嚴重影響鍵盤俠們積極發表我的意見了。

四、憑什麼說保證消息時序的一致性很困難?

4.1 基本認知

在普通IM用戶的眼裏,消息無非是從一臺手機傳遞到另外一臺手機而已,保證時序有何困難?

是的,普通用戶這麼認爲,從技術上講,他只是單純的將IM消息的收發過程理解爲單線程的工做模式而已。

實際上,在IM這種高性能場景下,服務端爲了追求高吞吐、高併發,用到了多線程、異步IO等等技術。

在這種狀況下,「高併發」與「順序」對於IM服務端來講,原本就是矛盾的,這就有點魚與熊掌的味道了(二者很難兼得)。

因此,要實現IM場景下的消息時序一致性,須要作出權衡,並且要考慮的技術維度至關多。這就致使具體技術實施起來沒有固定的套路,而因爲開發者技術能力的良莠不齊,也就使得不少IM系統在實際的效果上會有較大問題,對於用戶而言也將直接在產品體驗上反應出來。

下面將具體說明技術難點所在。

4.2 沒有全局時鐘

如上圖所示,一個真正堪用的生產系統,顯示不可能全部服務都跑在一臺服務器上,分佈式環境是確定的。

那麼:在分佈式環境下,客戶端+服務端後臺的各類後臺服務,都各自分佈在不一樣的機器上,機器之間都是使用的本地時鐘,沒有一個所謂的「全局時鐘」(也沒辦法作到真正的全局時鐘),那麼所謂的消息時序也就沒有真正意義上的時序基準點。因此消息時序問題顯然不是「本地時間」能夠徹底決定的。

4.3 多發送方問題

服務端分佈式的狀況下,不能用「本地時間」來保證時序性,那麼可否用接收方本地時間表示時序呢?

遺憾的是,因爲多個客戶端的存在(好比羣聊時),即便是一臺服務器的本地時間,也沒法表示「絕對時序」。

如上圖所示:絕對時序上,APP1先發出msg1,APP2後發出msg2,都發往服務器web1,網絡傳輸是不能保證msg1必定先於msg2到達的,因此即便以一臺服務器web1的時間爲準,也不能精準描述msg1與msg2的絕對時序。

4.4 多接收方問題

多發送方不能保證時序,假設只有一個發送方,可否用發送方的本地時間表示時序呢?遺憾的是,因爲多個接收方的存在,沒法用發送方的本地時間,表示「絕對時序」。

如上圖,絕對時序上,web1先發出msg1,後發出msg2,因爲網絡傳輸及多接收方的存在,沒法保證msg1先被接收到先被處理,故也沒法保證msg1與msg2的處理時序。

4.5 網絡傳輸與多線程問題

既然多發送方與多接收方都難以保證絕對時序,那麼假設只有單一的發送方與單一的接收方,可否保證消息的絕對時序一致性呢?

結論是悲觀的,因爲網絡傳輸與多線程的存在,這仍然不行。

如上圖所示,web1先發出msg一、後發出msg2,即便msg1先到達(網絡傳輸其實還不能保證msg1先到達),因爲多線程的存在,也不能保證msg1先被處理完。

五、如何保證絕對的消息時序一致性?

經過上一章內容的總結,咱們已經對IM中消息時序一致性問題所產生的原因,有了較爲深入的認識。

從純技術的角度來講,假設:

  • 1)只有一個發送方;
  • 2)一個接收方;
  • 3)上下游鏈接只有一條socket鏈接;
  • 4)經過阻塞的方式通信。

這樣的狀況下,難道不能保證先發出的消息被先處理,進而被先展現給消息的接收者嗎?

是的,能夠!

但實際生產狀況下不太可能出現這種IM系統,必竟單發送方、單接收方、單socket鏈接、阻塞方式,這樣的IM一旦作出來,產品經理會立馬死給你看。。。

六、實用的優化思路

6.1 一對一單聊的消息一致性保證思路

假設兩人一對一聊天,發送方A依次發出了msg一、msg二、msg3三條消息給接收方B,這三條消息該怎麼保證顯示時序的一致性(發送與顯示的順序一致)?

咱們知道,發送方A依次發出的msg一、msg二、msg3三條消息,到底服務端後,再由服務端中轉發出時,這個順序因爲多線程的網絡的問題,是有可能亂序的。

那麼結果就多是這樣: 

如上圖所示,會出現與發出時的消息時序不一致問題(收到的消息順序是:msg三、msg一、msg2)。

不過,實際上一對一聊天的兩我的,並不須要全局消息時序的一致(由於聊天只在兩人的同一會話在發生),只須要對於同一個發送方A,發給B的消息時序一致就好了。

常見優化方案,在A往B發出的消息中,加上發送方A本地的一個絕對時序(好比本機時間戳),來表示接收方B的展示時序。

那麼當接收方B收到消息後,即便極端狀況下消息可能存在亂序到達,但由於這個亂序的時間差對於普通用戶來講體感是很短的,在UI展示層按照消息中自帶的絕對時序排個序後再顯示,用戶實際上是沒有太多感知的。

6.2 多對多羣聊的消息一致性保證思路

假設N個羣友在一個IM羣裏聊天,應該怎樣保證全部羣員收到消息的顯示時序一致性呢?

首先:不能像一對聊天那樣利用發送方的絕對時序來保證消息順序,由於羣聊發送方不單點,時間也不一致。

或許:咱們能夠利用服務器的單點作序列化。

如上圖所示,此時IM羣聊的發送流程爲:

  • 1)sender1發出msg1,sender2發出msg2;
  • 2)msg1和msg2通過接入集羣,服務集羣;
  • 3)service層到底層拿一個惟一seq,來肯定接收方展現時序;
  • 4)service拿到msg2的seq是20,msg1的seq是30;
  • 5)經過投遞服務講消息給多個羣友,羣友即便接收到msg1和msg2的時間不一樣,但能夠統一按照seq來展示。

這個方法:

  • 1)優勢是:能實現全部羣友的消息展現時序相同;
  • 2)缺點是:這個生成全局遞增序列號的服務很容易成爲系統瓶頸。

還有沒有進一步的優化方法呢?

從技術角度看:羣消息其實也不用保證全局消息序列有序,而只要保證一個羣內的消息有序便可,這樣的話,「消息id序列化」就成了一個很好的思路。

上圖這個方案中,service層再也不須要去一個統一的後端拿全局seq(序列號),而是在service鏈接池層面作細小的改造,保證一個羣的消息落在同一個service上,這個service就能夠用本地seq來序列化同一個羣的全部消息,保證全部羣友看到消息的時序是相同的。

關於IM的系統架構下使用怎麼樣實現消息序列化,或者說全局消息ID的生成方案,這又是另外一個很熱門的技術話題。

有興趣,能夠深刻閱讀下面這個系列:

IM消息ID技術專題(一):微信的海量IM聊天消息序列號生成實踐(算法原理篇)

IM消息ID技術專題(二):微信的海量IM聊天消息序列號生成實踐(容災方案篇)

IM消息ID技術專題(三):解密融雲IM產品的聊天消息ID生成策略

IM消息ID技術專題(四):深度解密美團的分佈式ID生成算法

IM消息ID技術專題(五):開源分佈式ID生成器UidGenerator的技術實現

IM消息ID技術專題(六):深度解密滴滴的高性能ID生成器(Tinyid)

這個系列中,尤爲微信的趨勢遞增ID生成思路(注意:趨勢遞增不是嚴格遞增,趨勢遞增意味着中問有ID被跳過也沒事),對於分佈式IM的消息ID來講是很是切實可行的。

是的,對於IM系統來講,絕對意義上的時序很難保證,但經過服務端生成的單調遞增消息ID的方式,利用遞增ID來保證時序性,也是一個很可性的方案。

七、小結一下

IM系統架構下,消息的絕對時序是很困難的,緣由多種多樣,好比:沒有全局時鐘、多發送方、多接收方、多線程、網絡傳輸不肯定性等。

一對一單聊時,其實只須要保證發出的時序與接收的時序一致,就基本能讓用戶感受不到亂序了。

多對多的羣聊狀況下,保證同一羣內的全部接收方消息時序一致,也就能讓用戶感受不到亂序了,方法有兩種,一種單點絕對時序,另外一種實現消息id的序列化(也就是實現一種全局遞增消息ID)。

八、參考資料

[1] 如何保證IM實時消息的「時序性」與「一致性」?,做者:沈劍

[2] 一個低成本確保IM消息時序的方法探討,做者:封宇

本文已同步發佈於「即時通信技術圈」公衆號:

連接是:http://www.52im.net/thread-3189-1-1.html

相關文章
相關標籤/搜索