閒魚IM的在線、離線聊天數據同步機制優化實踐

本文由阿里閒魚技術團隊書閒分享,原題“如何有效縮短閒魚消息處理時長”,有修訂和改動。

1、引言

閒魚技術團隊圍繞IM這個技術範疇,已經分享了好幾篇實踐性總結文章,本篇將要分享的是閒魚IM系統中在線和離線聊天消息數據的同步機制上所遇到的一些問題,以及實踐性的解決方案。

2、系列文章

本文是系列文章的第7篇,總目錄如下:

  1. 阿里IM技術分享(一):企業級IM王者——釘釘在後端架構上的過人之處
  2. 阿里IM技術分享(二):閒魚IM基於Flutter的移動端跨端改造實踐
  3. 阿里IM技術分享(三):閒魚億級IM消息系統的架構演進之路
  4. 阿里IM技術分享(四):閒魚億級IM消息系統的可靠投遞優化實踐
  5. 阿里IM技術分享(五):閒魚億級IM消息系統的及時性優化實踐
  6. 阿里IM技術分享(六):閒魚億級IM消息系統的離線推送到達率優化
  7. 阿里IM技術分享(七):閒魚IM的在線、離線聊天數據同步機制優化實踐》(* 本文

3、問題背景

隨着用戶數的快速增長,閒魚IM系統也迎來了前所未有的挑戰。

歷經多年的業務迭代,客戶端側IM的代碼已經因爲多年的迭代層次結構不足夠清晰,之前一些隱藏起來的聊天數據同步問題,也隨着用戶數的增大而被放大。

這裏面的具體流程在於:後臺需要同步到用戶端側的數據包,後臺會根據數據包的業務類型劃分成不同的數據域,數據包在對應域裏面存在唯一且連續的編號,每一個數據包發送到端側並且被成功消費後,端側會記錄當前每一個數據域已經同步過的版本編號,下一次數據同步就以本地數據域的編號開始,不斷的同步到客戶端。

當然用戶不會一直在線等待消息,所以之前端側採用了推拉結合的方式保證數據的同步。

具體就是:

  • 1)客戶端在線時:使用ACCS實時的將最新的數據內容推送到客戶端(ACCS是淘寶無線向開發者提供全雙工、低延時、高安全的通道服務);
  • 2)客戶端從離線狀態啓動後:根據本地的數據域編號,拉取不在線時候的數據差;
  • 3)當數據獲取出現黑洞時:觸發數據同步拉取(“黑洞”即指數據包Version不連續的狀態)。

4、問題分析

當前的聊天數據同步策略確實是可以基本保障IM的數據同步的,但是也伴隨着一些隱含的問題。

這些隱含的問題主要有:

  • 1)短時間密集數據推送時,會快速的觸發多次數據域同步。域同步回來的數據如果存在問題,又會觸發新一輪的同步,造成網絡資源的浪費。冗餘數據包/無效的數據內容會佔用有效內容的處理資源,又對CPU和內存資源造成浪費;
  • 2)數據域中的數據包客戶端是否正常消費,服務端側無感知,只能被動地根據當前數據域信息返回數據;
  • 3)數據收取/消息數據體解析/存儲落庫邏輯拆分不夠清晰,無法針對性的對某一層的代碼拆分替換進行ABTest。

針對上述問題,我們對閒魚IM進行了分層改造——即抽離數據同步層。這樣優化,除了希望以後這個數據的同步內容可以用在IM之外,也希望隨着穩定性的增加,賦能其他的業務場景。

接下來的內容,我們重點來看下解決客戶端側閒魚IM聊天數據同步問題的一些實踐思路。

5、優化思路

5.1 分層拆分

對於服務端來說:業務側產出數據包後,會拼接上當前的數據域信息,然後通過數據同步層將數據推送到端側。

對於客戶端來說:接收到數據包後,會根據當前的數據域信息,來確定需要消費數據包的業務方,確保數據包在數據域內完整連續後,將數據體脫殼後交於業務側消費,並且應答消費的狀況。

數據同步層的抽取:把數據同步中的加殼、脫殼、校驗、重試流程封裝到一起,可以讓上層業務只需要關心自己需要監聽的數據域信息,然後當這些數據域更新數據的時候,可以獲取到這些數據進行消費,而不再需要關心數據包是否完整。

這樣做的話:

  • 1)業務側只需要關心業務側對接的協議;
  • 2)數據側只需要關心數據側包裝的協議;
  • 3)網絡層負責真實的數據傳輸。

整體的架構原理如下:

總結一下就是:

  • 1)對齊數據層數據傳輸協議、描述當前數據包體數據域信息;
  • 2)將消息的處理/合併/落庫抽離成數據消費者;
  • 3)上下樓依賴抽象化,去除對於具體實現的依賴。

5.2 數據層結構模型

基於對於數據模型剝離和對當下遇見問題的解決方案規整,將數據同步層拆分爲下圖這樣的架構。

具體的實施思路就是:

  • 1)App啓動時建立ACCS長鏈接服務,保證推推送信道鏈接,並且根據當前本地數據域信息觸發一次數據拉取;
  • 2)數據消費者註冊消費者信息和需要監聽的數據域信息,這裏是一對多的關係;
  • 3)新的數據抵達端側後,將數據包放到指定的數據域的緩衝池,批量數據歸納結束後,重新出發數據的讀取;
  • 4)根據當前數據域優先級彈出最高優的數據包,判斷數據域版本是否符合消費者要求,符合則將數據包脫殼後丟給消費者消費,不符合則根據上一次正確的數據包的域信息觸發增量的數據域同步拉取;
  • 5)觸發數據域同步拉取時,block數據讀取,此時通過ACCS觸達的數據依舊會在繼續歸納到指定的數據域隊列中,等待數據域同步拉取結果,將數據包進行排序、去重,合併到對應的數據域隊列中。然後重新激活數據讀取;
  • 6)數據包體被消費者正確消費後,更新域信息並且通過上行信道告知服務端已經正確處理的數據域信息。

* 數據域同步協議:

Region中攜帶的數據不必過多,但需將數據包的內容描述清楚,具體是:

  • 1)目標用戶的ID,用以確定目標數據包是否正確;
  • 2)數據域ID和優先級信息;
  • 3)當前數據包的域優先級版本。

* 排序策略:

針對於域數據歸納,無論是在寫入數據的時候進行排序還是在讀取的時候進行查找都需要進行一次排序的操作,時間複雜度最優也是O(logn)級別的。

在實際coding中發現由於在一個數據域裏面,數據包的Version信息是連續唯一併且不存在斷層的,上一個穩定消費的數據體的Version信息自增就是下一個數據包的Version,所以這裏採用了以Versio爲主鍵的Map存儲,既降低了時間複雜度,也使得唯一標識的數據包後抵達端側的包內容可以覆蓋之前的包內容。

6、新的問題及解決策略

6.1 多數據來源和唯一數據消費的平衡

每當產生一條針對於當前用戶的數據包:

  • 1)如果當前ACCS長鏈接存在,就會通過ACCS將數據包推送到客戶端;
  • 2)如果App切換到後臺一段時間,或者直接被殺死,ACCS鏈接斷開,那麼只能通過離線推送到用戶的通知面板。

所以:每當App切換到活躍狀態,都需要根據當前本地存儲的數據域信息從後臺觸發一次數據同步。

數據包觸達到客戶端側的來源主要是ACCS長鏈接的推送和域同步時的拉取,但是數據包的消費是根據數據域的監聽劃分的唯一消費者,也就是同一時間內只能消費一個數據包。

在壓力測試中:當後臺短時間內密集的將數據包通過ACCS推送到端側時,端側接收到的數據包並不有序,不連續的數據包域版本又會觸發新的數據域同步,導致同樣的一份數據包會通過兩個不同的渠道多次的觸達到端側,浪費了不必要的流量。

當數據域同步時:這個時間節點產生的新數據包也會推送到端側,數據體有效,並且需要被正確的消費。

針對上述這些問題的解決策略:

即在數據消費和數據獲取中間裝載一個數據中間層,當觸發數據域同步的時候block數據的讀取並且ACCS推送下來的數據包會被存放在一個數據的中轉站裏面,當數據域同步拉取的數據回來後,對數據進行合併後再重啓數據讀取流程。

6.2 數據域優先級

需要推送到客戶端側的數據包,根據業務的不同優先級也有不同的劃分。

用戶和用戶的聊天產生的數據包會比運營類的消息的數據包優先級要高一些,所以要當多優先級的數據包快速的抵達端側時,高優先級數據域的數據包需要被優先消費,而數據域的優先級也是需要動態調整,需要不斷變換的優先級策略。

針對這個問題的解決策略:

不同的數據域,產生不同的數據隊列,高優隊列裏面的數據包會被優先讀取消費。

每一個數據包體中帶回的數據域信息,都可以標註當前的數據域優先級,當數據域優先級發生變化的時候,調整數據包消費優先級策略。

7、優化後的效果

除去結構上分層梳理,使得數據同步層和依賴的服務內容可便捷解耦/每一個環節可插拔之外,數據同步中對於消息消費時長/流量節省,壓力測試場景下優化效果更加明顯。

在“500ms內100條全亂序數據包推送”壓力測試場景下:

  • 1)消息處理時長(接收-上屏)縮短 31%;
  • 2)流量損耗(最終拉取到端側數據包累積大小)降低35%。

8、後續的優化計劃

8.1 數據同步層能力提升

數據同步側的目標,既要保證數據包完整的到達端側,又要在保證穩定性的前提下儘可能的減少數據的拉取,使得每一次數據的獲取都有效。

後續數據同步層會着手於有效數據率和到達率進行更進一步的優化。

針對不同的場景,動態智能調整數據同步的優先級策略。

阻塞式長鏈接推送,保證同一時間只存在推模式或者拉模式,進一步減少冗餘數據包的推送。

8.2 IM端側整體架構升級

升級數據同步層策略主要還是要提升IM的能力,將數據同步分層後,接下來就是將消息的處理流程化,對每一個流程都可監控可回溯,提升IM數據包的正確解析存儲和落庫率。

細化一下就是:

  • 1)在數據來源側剝離開後,後續對IM的整改也會逐步的將消息的處理分層剝離;
  • 2)消息處理關鍵節點的流程式上報、建立完整的監控體系,讓問題發現先於用戶輿情;
  • 3)消息完整性的動態自檢,最小化數據補償補全。

9、參考資料

[1] IM單聊和羣聊中的在線狀態同步應該用“推”還是“拉”?

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

[3] 一套高可用、易伸縮、高併發的IM羣聊、單聊架構方案設計實踐

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

[5] 從新手到專家:如何設計一套億級消息量的分佈式IM系統

[6] 融雲技術分享:全面揭祕億級IM消息的可靠投遞機制

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

[8] 現代IM系統中聊天消息的同步和存儲方案探討

[9] 新手入門一篇就夠:從零開發移動端IM

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

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

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

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

學習交流:

- 移動端IM開發入門文章:《新手入門一篇就夠:從零開發移動端IM

- 開源IM框架源碼:https://github.com/JackJiang2011/MobileIMSDK 

本文已同步發佈於:http://www.52im.net/thread-3856-1-1.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章