開發與維運

如何有效縮短閒魚消息處理時長

作者:閒魚技術——書閒

背景

隨著用戶數的快速增長,閒魚的IM也迎來了前所未有的挑戰。多年的業務迭代,端側IM的代碼已經因為多年的迭代層次結構不足夠清晰,之前消息一些隱藏起來的數據同步問題,也隨著用戶數的增大而被放大。

現狀分析

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

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

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

這樣的一個同步策略是可以基本保障IM的數據同步的,但是也伴隨著一些隱含的問題:

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

要做什麼

針對遇見的這些問題,對閒魚IM進行分層改造,抽離數據同步層。除了希望以後這個數據的同步內容可以用在IM之外,也希望隨著穩定性的增加,賦能其他的業務場景。

本文重點來看下端側閒魚IM數據同步的一些解決思路。

數據同步優化

拆分&分層

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

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

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

業務側只需要關心業務側對接的協議,數據側只需要關心數據側包裝的協議,網絡層負責真實的數據傳輸。

分層結構.png

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

數據層結構模型

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

數據層結構 (1).png

step1:App啟動時建立ACCS長鏈接服務,保證推推送信道鏈接,並且根據當前本地數據域信息觸發一次數據拉取。

step2:數據消費者註冊消費者信息和需要監聽的數據域信息,這裡是一對多的關係。

step3:新的數據抵達端側後,將數據包放到指定的數據域的緩衝池,一批數據歸納結束後,重新出發數據的讀取。

step4:根據當前數據域優先級彈出最高優的數據包,判斷數據域版本是否符合消費者要求,符合則將數據包脫殼後丟給消費者消費,不符合則根據上一次正確的數據包的域信息觸發增量的數據域同步拉取。

step5:觸發數據域同步拉取時,block數據讀取,此時通過ACCS觸達的數據依舊會在繼續歸納到指定的數據域隊列中,等待數據域同步拉取結果,將數據包進行排序、去重,合併到對應的數據域隊列中。然後重新激活數據讀取。

step6:數據包體被消費者正確消費後,更新域信息並且通過上行信道告知服務端已經正確的數據域信息。

數據域同步協議

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

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

排序策略

針對於域數據歸納,無論是在寫入數據的時候進行排序還是在讀取的時候進行查找都需要進行一次排序的操作,時間複雜度最優也是O(logn)級別的,在實際coding中發現由於在一個數據域裡面,數據包的Version信息是連續唯一併且不存在斷層的,上一個穩定消費的數據體的Version信息自增就是下一個數據包的Version,所以這裡採用了以Versio為主鍵的Map存儲,既降低了時間複雜度,也使得唯一標識的數據包後抵達端側的包內容可以覆蓋之前的包內容。

一些問題&解決策略

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

每當產生一條針對於當前用戶的數據包,如果當前ACCS長鏈接存在,就會通過ACCS將數據包推送到客戶端,如果App切換到後臺一段時間,或者直接被殺死,ACCS鏈接斷開,那麼只能通過離線推送到用戶的通知面板。所以每當App切換到活躍狀態,都需要根據當前本地存儲的數據域信息從後臺觸發一次數據同步

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

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

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

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

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

數據域優先級

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

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

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

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

優化效果

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

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

消息處理時長(接收-上屏)提升 31%

流量損耗(最終拉取到端側數據包累積大小)降低35%

後續計劃

數據同步層能力提升

數據同步側的目標,既要保證數據包完整的到達端側,又要在保證穩定性的前提下儘可能的減少數據的拉取,使得每一次數據的獲取都有效。後續數據同步層會著手於有效數據率和到達率進行更進一步的優化。

  • 針對不同的場景,動態智能調整數據同步的優先級策略。
  • 阻塞式長鏈接推送,保證同一時間只存在推模式或者拉模式,進一步減少冗餘數據包的推送。

閒魚IM端側整體架構升級

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

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

後續閒魚端側IM體驗升級系列文章歡迎大家關注

注: 該改造升級將於十一月中旬的版本和大家見面,敬請期待

Leave a Reply

Your email address will not be published. Required fields are marked *