一套億級用戶的IM架構技術乾貨(下篇):可靠性、有序性、弱網優化等

本文內容和編寫思路是基於鄧昀澤的「大規模併發IM服務架構設計」、「IM的弱網場景優化」兩文的提綱進行的,感謝鄧昀澤的無私分享。html

一、引言

接上篇《一套億級用戶的IM架構技術乾貨(上篇):總體架構、服務拆分等》,本文主要聚焦這套億級用戶的IM架構的一些比較細節但很重要的熱門問題上,好比:消息可靠性、消息有序性、數據安全性、移動端弱網問題等。算法

以上這些熱門IM問題每一個話題其實均可以單獨成文,但限於文章篇幅,本文不會逐個問題詳細深刻地探討,主要以拋磚引玉的方式引導閱讀者理解問題的關鍵,並針對問題提供專項研究文章連接,方便有選擇性的深刻學習。但願本文能給你的IM開發帶來一些益處。後端

二、系列文章

爲了更好以進行內容呈現,本文拆分兩了上下兩篇。緩存

本文是2篇文章中的第2篇:安全

一套億級用戶的IM架構技術乾貨(上篇):總體架構、服務拆分等服務器

一套億級用戶的IM架構技術乾貨(下篇):可靠性、有序性、弱網優化等》(本文)微信

本篇主要聚焦這套億級用戶的IM架構的一些比較細節但很重要的熱門問題上。網絡

三、消息可靠性問題

消息的可靠性是IM系統的典型技術指標,對於用戶來講,消息能不能被可靠送達(不丟消息),是使用這套IM的信任前提。架構

換句話說,若是這套IM系統不能保證不丟消息,那至關於發送的每一條消息都有被丟失的機率,對於用戶而言,必定會不會「放心」地使用它,即「不信任」這套IM。併發

從產品經理的角度來講,有這樣的技術障礙存在,再怎麼費力的推廣,最終用戶都會很快流失。因此一套IM若是不能保證消息的可靠性,那問題是很嚴重的。

PS:若是你對IM消息可靠性的問題尚未一個直觀的映象的話,經過《零基礎IM開發入門(三):什麼是IM系統的可靠性?》這篇文章能夠通俗易懂的理解它。

如上圖所示,消息可靠性主要依賴2個邏輯來保障:

  • 1)上行消息可靠性;
  • 2)下行消息可靠性。

1)針對上行消息的可靠性,能夠這樣的思路來處理:

用戶發送一個消息(假設協議叫PIMSendReq),用戶要給這個消息設定一個本地ID,而後等待服務器操做完成給發送者一個PIMSendAck(本地ID一致),告訴用戶發送成功了。

若是等待一段時間,沒收到這個ACK,說明用戶發送不成功,客戶端SDK要作重試操做。

2)針對下行消息的可靠性,能夠這樣的思路來處理:

服務收到了用戶A的消息,要把這個消息推送給B、C、D 3我的。假設B臨時掉線了,那麼在線推送極可能會失敗。

所以確保下行可靠性的核心是:在作推送前要把這個推送請求緩存起來。

這個緩存由存儲系統來保證,MsgWriter要維護一個(離線消息列表),用戶的一條消息,要同時寫入B、C、D的離線消息列表,B、C、D收到這個消息之後,要給存儲系統一個ACK,而後存儲系統把消息ID從離線消息列表裏拿掉。

針對消息的可靠性問題,具體的解決思路還能夠從另外一個維度來考慮:即實時消息的可靠性和離線消息的可靠性。

有興趣能夠深刻讀一讀這兩篇:

IM消息送達保證機制實現(一):保證在線實時消息的可靠投遞

IM消息送達保證機制實現(二):保證離線消息的可靠投遞

而對於離線消息的可靠性來講,單聊和羣聊又有很大區別,有關羣聊的離線消息可靠投遞問題,能夠深刻讀一讀《IM開發乾貨分享:如何優雅的實現大量離線消息的可靠投遞》。

四、消息有序性問題

消息的有序性問題是分佈式IM系統中的另外一個技術「硬骨頭」。

由於是分佈式系統,客戶端和服務器的時鐘多是不一樣步的。若是簡單依賴某一方的時鐘,就會出現大量的消息亂序。

好比只依賴客戶端的時鐘,A比B時間晚30分鐘。全部A給B發消息,而後B給A回覆。

發送順序是:

客戶端A:「XXX」

客戶端B:「YYY」

接收方的排序就會變成:

客戶端B:「YYY」

客戶端A:「XXX」

由於A的時間晚30分鐘,全部A的消息都會排在後面。

若是隻依賴服務器的時鐘,也會出現相似的問題,由於2個服務器時間可能也不一致。雖然客戶端A和客戶端B時鐘一致,可是A的消息由服務器S1處理,B的消息由服務器S2處理,也會致使一樣消息亂序。

爲了解決這種問題,個人思路是經過能夠作這樣一系列的操做來實現。

1)服務器時間對齊:

這部分就是後端運維的鍋了,由系統管理員來儘可能保障,沒有別的招兒。

2)客戶端經過時間調校對齊服務器時間:

好比:客戶端登陸之後,拿客戶端時間和服務器時間作差值計算,發送消息的時候考慮這部分差值。

在個人im架構裏,這個能把時間對齊到100ms這個級,差值再小的話就很困難了,由於協議在客戶端和服務器之間傳遞速度RTT也是不穩定的(網絡傳輸存在不可控的延遲風險嘛)。

3)消息同時帶上本地時間和服務器時間:

具體能夠這樣的處理:排序的時候,對於同一我的的消息,按照消息本地時間來排;對於不一樣人的消息,按照服務器時間來排,這是插值排序算法。

PS:關於消息有序性的問題,顯然也不是上面這三兩句話能講的清楚,若是你想更通俗一理解它,能夠讀一讀《零基礎IM開發入門(四):什麼是IM系統的消息時序一致性?》。

另外:從技術實踐可行性的角度來講,《一個低成本確保IM消息時序的方法探討》、《如何保證IM實時消息的「時序性」與「一致性」?》這兩篇中的思路能夠借鑑一下。

實際上,消息的排序問題,還能夠從消息ID的角度去處理(也就是經過算法讓消息ID產生順序性,從而根據消息ID就能達到消息排序的目的)。

有關順序的消息ID算法問題,這兩篇很是值得借鑑:IM消息ID技術專題(一):微信的海量IM聊天消息序列號生成實踐(算法原理篇)》、《IM消息ID技術專題(三):解密融雲IM產品的聊天消息ID生成策略》,我就不廢話了。

五、消息已讀同步問題

消息的已讀未讀功能,以下圖所示: 

上圖就是釘釘中的已讀未讀消息。這在企業IM場景下很是有用(由於作領導的都喜歡,你懂的)。

已讀未讀功能,對於一對一的單聊消息來講,還比較好理解:就是多加一條對應的回執息(當用戶閱讀這條消息時發回)。

但對於羣聊這說,這一條消息有多少人已讀、多少人未讀,想實現這個效果,那就真的有點麻煩了。對於羣聊的已讀未讀功能實現邏輯,這裏就不展開了,有興趣能夠讀一下這篇《IM羣聊消息的已讀回執功能該怎麼實現?》。

迴歸到本節的主題「已讀同步」的問題,這顯示難度又進一級,由於已讀未讀回執不僅是針對「帳號」,如今還要細分到「同一帳號在不一樣端登錄」的狀況,對於已讀回執的同步邏輯來講,這就有點複雜化了。

在這裏,根據我這邊IM架構的實踐經驗,提供一些思路。

具體來講就是:用戶可能有多個設備登陸同一個帳戶(好比:Web PC和移動端同時登錄),這種狀況下的已讀未讀功能,就須要來實現已讀同步,不然在設備1看過的消息,設備2看到依然是未讀消息,從產品的角度來講,這就影響用戶體驗了。

對於個人im架構來講,已讀同步主要依賴2個邏輯來保證:

  • 1)同步狀態維護,爲用戶的每個Session,維護一個時間戳,保存最後的讀消息時間;
  • 2)若是用戶打開了某個Session,且用戶有多個設備在線,發送一條PIMSyncRead消息,通知其它設備。

六、數據安全問題

6.1 基礎

IM系統架構中的數據安全比通常系統要複雜一些,從通訊的角度來講,它涉及到socket長鏈接通訊的安全性和http短鏈接的兩重安全性。而隨着IM在移動端的流行,又要在安全性、性能、數據流量、用戶體驗這幾個維度上作權衡,因此想要實現一套完善的IM安全架構,要面臨的挑戰是不少的。

IM系統架構中,所謂的數據安全,主要是通訊安全和內容安全。

6.2 通訊安全

所謂的通訊安全,這就要理解IM通訊的服務組成。

目前來講,一個典型的im系統,主要由兩種通訊服務組成:

  • 1)socket長鏈接服務:技術上也就是多數人耳熟能詳的網絡通訊這一塊,再細化一點也就是tcp、udp協議這一塊;
  • 2)http短鏈接服務:也就是最經常使用的http rest接口那些。

對於提高長鏈接的安全性思路,能夠深刻閱讀《通俗易懂:一篇掌握即時通信的消息傳輸安全原理》。另外,微信團隊分享的《微信新一代通訊安全解決方案:基於TLS1.3的MMTLS詳解》一文,也很是有參考意義。

若是是通訊安全級別更高的場景,能夠參考《即時通信安全篇(二):探討組合加密算法在IM中的應用》,文中關於組合加密算法的使用思路很是不錯。

至於短鏈接安全性,你們就很熟悉了,開啓https多數狀況下就夠用了。若是對於https不甚瞭解,能夠從這幾篇開始:《一文讀懂Https的安全性原理、數字證書、單項認證、雙項認證等》、《即時通信安全篇(七):若是這樣來理解HTTPS,一篇就夠了》。

6.3 內容安全

這個可能不太好理解,上面既然實現了通訊安全,那爲何還要糾結「內容安全」?

咱們瞭解一下所謂的密碼學三大做用:加密( Encryption)、認證(Authentication),鑑定(Identification) 。

詳細來講就是:

加密:防止壞人獲取你的數據。

認證:防止壞人修改了你的數據而你卻並無發現。

鑑權:防止壞人假冒你的身份。

在上節中,惡意攻擊者若是在通訊環節繞開或突破了「鑑權」、「認證」,那麼依賴於「鑑權」、「認證」的「加密」,實際上也有可有被破解。

針對上述問題,那麼咱們須要對內容進行更加安全獨立的加密處理,就這是所謂的「端到端加密」(E2E)。

好比,那個號稱沒法被破解的IM——Telegram,實際上就是使用了端到端加密技術。

關於端到端加密,這裏就不深刻探討,這裏有兩篇文章有興趣地能夠深刻閱讀:

移動端安全通訊的利器——端到端加密(E2EE)技術詳解

簡述實時音視頻聊天中端到端加密(E2EE)的工做原理

七、雪崩效應問題

在分佈式的IM架構中,存在雪崩效應問題。

咱們知道,分佈式的IM架構中,爲了高可用性,用戶每次登錄都是根據負載均衡算法分配到不一樣的服務器。那麼問題就來了。

舉個例子:假設有5個機房,其中A機房故障,致使這個機房先前服務的用戶都跑去B機房。B機房不堪重負也崩潰了,A+B的用戶跑去機房C,連鎖反應會致使全部服務掛掉。

防止雪崩效應須要在服務器架構,客戶端連接策略上有一些配合的解決方案。服務器須要有限流能力做爲基礎,主要是限制總服務用戶數和短期連接用戶數。

在客戶端層面,發現服務斷開以後要有一個策略,防止大量用戶同一時間去連接某個服務器。

一般有2種方案:

  • 1)退避:重連之間設置一個隨機的間隔;
  • 2)LBS:跟服務器申請重連的新的服務器IP,而後由LBS服務去下降短期分配到同一個服務器的用戶量。

這2種方案互不衝突,能夠同時作。

八、弱網問題

8.1 弱網問題的緣由

鑑於現在IM在移動端的流行,弱網是很常態的問題。電梯、火車上、開車、地鐵等等場景,都會遇到明顯的弱網問題。

那麼爲何會出現弱網問題?

要回答這個問題,那就須要從無線通訊的原理上去尋找答案。

由於無線通訊的質量受制於不少方面的因素,好比:無線信號強弱變化快、信號干擾、通訊基站分佈不均、移動速度太快等等。要說清楚這個問題,那就真是三天三夜都講不完。

有興趣的讀者,必定要仔細閱讀下面這幾篇文章,相似的跨學科科譜式文章並很少見:

IM開發者的零基礎通訊技術入門(十一):爲何WiFi信號差?一文即懂!

IM開發者的零基礎通訊技術入門(十二):上網卡頓?網絡掉線?一文即懂!

IM開發者的零基礎通訊技術入門(十三):爲何手機信號差?一文即懂!

IM開發者的零基礎通訊技術入門(十四):高鐵上無線上網有多難?一文即懂!

弱網問題是移動端APP的必修課,下面這幾篇總結也值得借鑑:

移動端IM開發者必讀(一):通俗易懂,理解移動網絡的「弱」和「慢」

移動端IM開發者必讀(二):史上最全移動弱網絡優化方法總結

現代移動端網絡短鏈接的優化手段總結:請求速度、弱網適應、安全保障

百度APP移動端網絡深度優化實踐分享(三):移動端弱網優化篇

8.2 IM針對弱網問題的處理

對於IM來講,弱網問題並非很複雜,核心是作好消息的重發、排序以及接收端的重試。

爲了解決好弱網引起的IM問題,一般能夠經過如下手段改善:

  • 1)消息自動重發;
  • 2)離線消息接收;
  • 3)重發消息排序;
  • 4)離線指令處理。

下面將逐一展開討論。

8.3 消息自動重發

坐地鐵的時候,常常遇到列車開起來之後,網絡斷開,發送消息失敗。

這時候產品有2種表現形式:

  • a、直接告訴用戶發送失敗;
  • b、保持發送狀態,自動重試3-5次(3分鐘)之後告訴用戶發送失敗。

顯然:自動重試失敗之後再告訴用戶發送失敗體驗要好不少。尤爲是在網絡閃斷狀況下,重試成功率很高,極可能用戶根本感知不到有發送失敗。

從技術上:客戶端IMSDK要把每條消息的狀態監控起來。發送消息不能簡單的調用一下網絡發送請求,而是要有一個狀態機,管理幾個狀態:初始狀態,發送中,發送失敗,發送超時。對於失敗和超時的狀態,要啓用重試機制。

這裏還有一篇關於重試機制設計的討論帖子,有興趣能夠看看:徹底自已開發的IM該如何設計「失敗重試」機制?》。

IM消息送達保證機制實現(一):保證在線實時消息的可靠投遞》一文中關於消息超時與重傳機制的實現思路,也能夠參考一下。

8.4 離線消息接收

現代IM是沒有「在線」這個狀態的,不須要給用戶這個信息。可是從技術的層面,用戶掉線了仍是要正確的去感知的。

感知方法有幾條:

  • a、信令長鏈接狀態:若是長時間沒收到到服務器的心跳反饋,說明掉線了;
  • b、網絡請求失敗次數:若是屢次網絡請求失敗,說明」可能「掉線了;
  • c、設備網絡狀態檢測:直接檢測網卡狀態就好,通常Android/iOS/Windows/Mac都有相應系統API。

正確檢測到網絡狀態之後,發現網絡從」斷開到恢復「的切換,要去主動拉取離線階段的消息,就能夠作到弱網狀態不丟消息(從服務器的離線消息列表拉取)。

上面文字中提到的網絡狀態的肯定,涉及到IM裏網絡鏈接檢查和保活機制問題,是IM裏比較頭疼的問題。

一不當心,又踩進了IM網絡保活這個坑,我就不在這裏展開,有興趣必定要讀讀下面的文章:

爲什麼基於TCP協議的移動端IM仍然須要心跳保活機制?

一文讀懂即時通信應用中的網絡心跳包機制:做用、原理、實現思路等

微信團隊原創分享:Android版微信後臺保活實戰分享(網絡保活篇)

移動端IM實踐:實現Android版微信的智能心跳機制

移動端IM實踐:WhatsApp、Line、微信的心跳策略分析

8.5 重發消息排序

弱網邏輯的另外一個坑是消息排序。

假若有A、B、C  3條消息,A、C發送成功,B發送的時候遇到了網絡閃斷,B觸發自動重試。

那麼接收方的接收順序應該是 A B C仍是A C B呢?我觀察過不一樣的IM產品,處理邏輯各不相同,這個你們有興趣能夠去玩一下。

這個解決方法是要依賴上一篇服務架構裏提到的差值排序,同一我的發出的消息,排序按消息附帶的本地時間來排。不一樣人的消息,按照服務器時間排序。

具體我這邊就再也不得復,能夠回頭看看本篇中的第四節「四、消息有序性問題」。

8.6 離線指令處理

部分指令操做的時候,網絡可能出現了問題,等網絡恢復之後,要自動同步給服務器。

舉一個例子,你們能夠試試手機設置爲飛行模式,而後在微信裏刪除一個聯繫人,看看能不能刪除。而後從新打開網絡,看看這個數據會不會同步到服務器。

相似的邏輯也適用於已讀同步等場景,離線狀態看過的信息,要正確的跟服務器同步。

8.7 小結一下

IM的弱網處理,其實相對仍是比較簡單的,基本上自動重試+消息狀態就能夠解決絕大部分的問題了。

一些細節處理也並不複雜,主要緣由是IM的消息量比較小,網絡恢復後能快速的恢復操做。

視頻會議在弱網下的邏輯,就要複雜的多了。尤爲是高丟包的弱網環境下,要盡力去保證音視頻的流暢性。

九、本文小結

《一套億級用戶的IM架構技術乾貨》這期文章的上下兩篇就這麼侃完了,上篇涉及到的IM架構問題倒還好,下篇一不當心又帶出了IM裏的各類熱門問題「坑」,搞IM開發直是一言難盡。。。

建議IM開發的入門朋友們,若是想要系統地學習移動端IM開發的話,應該去讀一讀我整理的那篇IM開發「從入門到放棄」的文章(哈哈哈),就是這篇《新手入門一篇就夠:從零開發移動端IM》。具體我就再也不展開了,否則這篇幅又要剎不住車了。。。

十、參考資料

[1] 大規模併發IM服務架構設計

[2] IM的弱網場景優化

[3] 零基礎IM開發入門(三):什麼是IM系統的可靠性?

[4] IM消息送達保證機制實現(一):保證在線實時消息的可靠投遞

[5] IM開發乾貨分享:如何優雅的實現大量離線消息的可靠投遞

[6] 即時通信安全篇(二):探討組合加密算法在IM中的應用

[7] 微信新一代通訊安全解決方案:基於TLS1.3的MMTLS詳解

附錄:更多IM開發文章彙總

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

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

IM開發乾貨分享:如何優雅的實現大量離線消息的可靠投遞

IM開發乾貨分享:有贊移動端IM的組件化SDK架構設計實踐

IM開發寶典:史上最全,微信各類功能參數和邏輯規則資料彙總

IM開發乾貨分享:我是如何解決大量離線消息致使客戶端卡頓的

從客戶端的角度來談談移動端IM的消息可靠性和送達機制

騰訊技術分享:社交網絡圖片的帶寬壓縮技術演進之路

移動端IM中大規模羣消息的推送如何保證效率、實時性?

移動端IM開發須要面對的技術問題

開發IM是本身設計協議用字節流好仍是字符流好?

請問有人知道語音留言聊天的主流實現方式嗎?

IM單聊和羣聊中的在線狀態同步應該用「推」仍是「拉」?

IM羣聊消息如此複雜,如何保證不丟不重?

談談移動端 IM 開發中登陸請求的優化

移動端IM登陸時拉取數據如何做到省流量?

通俗易懂:基於集羣的移動端IM接入層負載均衡方案分享

微信對網絡影響的技術試驗及分析(論文全文)

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

▲ 本文在公衆號上的連接是:點此進入。同步發佈連接是:http://www.52im.net/thread-3445-1-1.html

相關文章
相關標籤/搜索