1. 程式人生 > 其它 >im即時通訊開發:訊息模型、萬人群、已讀回執、訊息撤回功能

im即時通訊開發:訊息模型、萬人群、已讀回執、訊息撤回功能

企業微信作為一款辦公協同的產品,聊天訊息收發是最基礎的功能。訊息系統的穩定性、可靠性、安全性尤其重要。

訊息系統的構建與設計的過程中,面臨著較多的難點。而且針對toB場景的訊息系統,需要支援更為複雜的業務場景。

針對toB場景的特有業務有:

1)訊息鑑權:關係型別有群關係、同企業同事關係、好友關係、集團企業關係、圈子企業關係。收發訊息雙方需存在至少一種關係才允許發訊息;

2)回執訊息:每條訊息都需記錄已讀和未讀人員列表,涉及頻繁的狀態讀寫操作;

3)撤回訊息:支援24小時的有效期撤回動作;

4)訊息儲存:雲端儲存時間跨度長,最長可支援180天訊息儲存,數百TB使用者訊息需優化,減少機器成本;

5)萬人群聊

:群人數上限可支援10000人,一條群訊息就像一次小型的DDoS攻擊;

6)微信互通:兩個異構的im系統直接打通,可靠性和一致性尤其重要。

整體架構分層如下。

1)接入層:統一入口,接收客戶端的請求,根據型別轉發到對應的CGI層。客戶端可以通過長連或者短連連線wwproxy。活躍的客戶端,優先用長連線發起請求,如果長連失敗,則選用短連重試。即時通訊開發

2)CGI層:http服務,接收wwproxy的資料包,校驗使用者的session狀態,並用後臺派發的祕鑰去解包,如解密失敗則拒絕請求。解密成功,則把明文包體轉發到後端邏輯層對應的svr。

3)邏輯層:大量的微服務和非同步處理服務,使用自研的hikit rpc框架,svr之間使用tcp短連進行通訊。進行資料整合和邏輯處理。和外部系統的通訊,通過http協議,包括微信互通、手機廠商的推送平臺等。

4)儲存層:訊息儲存是採用的是基於levelDB模型開發msgkv。SeqSvr是序列號生成器,保證派發的seq單調遞增不回退,用於訊息的收發協議。

傳送方請求後臺,把訊息寫入到接收方的儲存,然後push通知接收方。接受方收到push,主動上來後臺收訊息。

不重、不丟、及時觸達,這三個是訊息系統的核心指標:

1)實時觸達:客戶端通過與後臺建立長連線,保證訊息push的實時觸達;

2)及時通知:如果客戶端長連線不在,程序被kill了,利用手機廠商的推送平臺,推送通知,或者直接拉起程序進行收訊息;

3)訊息可達:假如遇到訊息洪峰,後臺的push滯後,客戶端有輪訓機制進行兜底,保證訊息可達;

4)訊息防丟

:為了防止訊息丟失,只要後臺邏輯層接收到請求,保證訊息寫到接收方的儲存,失敗則重試。如果請求在CGI層就失敗,則返回給客戶端出訊息紅點;

5)訊息排重:客戶端在弱網路的場景下,有可能請求已經成功寫入儲存,回包超時,導致客戶端重試發起相同的訊息,那麼就造成訊息重複。為了避免這種情況發生,每條訊息都會生成唯一的appinfo,後臺通過建立索引進行排重,相同的訊息直接返回成功,保證儲存只有一條。

 

擴散讀

即:每條訊息只存一份,群聊成員都讀取同一份資料。

優點:節省儲存容量。

缺點:

① 每個使用者需儲存會話列表,通過會話id去拉取會話訊息;

② 收訊息的協議複雜,每個會話都需要增量同步訊息,則每個會話都需要維護一個序列號。

擴散寫

即:每條訊息存多份,每個群聊成員在自己的儲存都有一份。

優點:

① 只需要通過一個序列號就可以增量同步所有訊息,收訊息協議簡單;

② 讀取速度快,前端體驗好;

③ 滿足更多ToB的業務場景:回執訊息、雲端刪除。

同一條訊息,在每個人的視角會有不同的表現。例如:回執訊息,傳送方能看到已讀未讀列表,接受方只能看到是否已讀的狀態。雲端刪除某條群訊息,在自己的訊息列表消失,其他人還是可見。

缺點:儲存容量的增加。

企業微信採用了擴散寫的方式,訊息收發簡單穩定。儲存容量的增加,可以通過冷熱分離的方案解決,冷資料存到廉價的SATA盤,擴散讀體驗稍差,協議設計也相對複雜些。

1)每個使用者只有一條獨立的訊息流。同一條訊息多副本存在於每個使用者的訊息流中;

2)每條訊息有一個seq,在同個使用者的訊息流中,seq是單調遞增的;

3)客戶端儲存訊息列表中最大seq,說明客戶端已經擁有比該seq小的所有訊息。若客戶端被push有新訊息到達,則用該seq向後臺請求增量資料,後臺把比此seq大的訊息資料返回。

高峰期系統壓力大,偶發的網路波動或者機器過載,都有可能導致大量的系統失敗。im系統對及時性要求比較高,沒辦法進行削峰處理。那麼引入一些柔性的策略,保證系統的穩定性和可用性非常有必要。

具體的做法就是啟動過載保護策略:當svr已經達到最大處理能力的時候,說明處於一個過載的狀態,服務能力會隨著負載的增高而急劇下降。如果svr過載,則拒絕掉部分正常請求,防止機器被壓垮,依然能對外服務。通過統計svr的被調耗時情況、worker使用情況等,判定是否處於過載狀態。過載保護策略在請求高峰期間起到了保護系統的作用,防止雪崩效應。

解決方案思路就是:儘管失敗,也返回前端成功,後臺保證最終成功。

為了保證訊息系統的可用性,規避高峰期系統出現過載失敗導致前端出紅點,做了很多優化。

具體策略如下:

1)邏輯層hold住失敗請求,返回前端成功,不出紅點,後端非同步重試,直至成功;

2)為了防止在系統出現大面積故障的時候,重試請求壓滿佇列,只hold住半小時的失敗請求,半小時後新來的請求則直接返回前端失敗;

3)為了避免重試加劇系統過載,指數時間延遲重試;

4)複雜的訊息鑑權(好友關係,企業關係,集團關係,圈子關係),耗時嚴重,後臺波動容易造成失敗。如果並非明確鑑權不通過,則冪等重試;

5)為了防止作惡請求,限制單個使用者和單個企業的請求併發數。例如,單個使用者的消耗worker數超過20%,則直接丟棄該使用者的請求,不重試。

優化後,後臺的波動,前端基本沒有感知。

系統穩定性設計2:系統解耦

由於產品形態的原因,企業微信的訊息系統,會依賴很多外部模組,甚至外部系統。

例如:與微信訊息互通,傳送訊息的許可權需要放到ImUnion去做判定,ImUnion是一個外部系統,呼叫耗時較長。

再如:金融版的訊息審計功能,需要把訊息同步到審計模組,增加rpc呼叫。

再如:客戶服務的單聊群聊訊息,需要把訊息同步到crm模組,增加rpc呼叫。為了避免外部系統或者外部模組出現故障,拖累訊息系統,導致耗時增加,則需要系統解耦。

 

我們的方案:與外部系統的互動,全設計成非同步化。

思考點:需要同步返回結果的請求,如何設計成非同步化?

例如:群聊互通訊息需經過ImUnion鑑權返回結果,前端用於展示訊息是否成功傳送。先讓客戶端成功,非同步失敗,則回撥客戶端使得出紅點。

如果是非主流程,則非同步重試保證成功,主流程不受影響,如訊息審計同步功能。那麼,只需要保證內部系統的穩定,發訊息的主流程就可以不受影響。

系統穩定性設計3:業務隔離

企業微信的訊息型別有多種:

1)單聊群聊:基礎聊天,優先順序高;

2)api 訊息:企業通過api介面下發的訊息,有頻率限制,優先順序中;

3)應用訊息:系統應用下發的訊息,例如公告,有頻率限制,優先順序中;

4)控制訊息:不可見的訊息。例如群資訊變更,會下發控制訊息通知群成員,優先順序低。

群聊按群人數,又分成3類:

1)普通群:小於100人的群,優先順序高;

2)大 群:小於2000人的群,優先順序中;

3)萬人群:優先順序低。

業務繁多:如果不加以隔離,那麼其中一個業務的波動有可能引起整個訊息系統的癱瘓。

重中之重:需要保證核心鏈路的穩定,就是企業內部的單聊和100人以下群聊,因為這個業務是最基礎的,也是最敏感的,稍有問題,投訴量巨大。

其餘的業務:互相隔離,減少牽連。按照優先順序和重要程度進行隔離,對應的併發度也做了調整,儘量保證核心鏈路的穩定性。