<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Jack Jiang

    我的最新工程MobileIMSDK:http://git.oschina.net/jackjiang/MobileIMSDK
    posts - 494, comments - 13, trackbacks - 0, articles - 1

    本文引用自InfoQ社區“5億用戶如何高效溝通?釘釘首次對外揭秘即時消息服務DTIM”一文,作者陳萬紅等、策劃褚杏娟,有修訂和改動。

    一、引言

    本文是國內企業IM的事實王者釘釘首次對外深度解密其即時消息服務(即DingTalk IM,簡稱DTIM)的技術設計實踐。

    本篇文章內容將從模型設計原理到具體的技術架構、最底層的存儲模型到跨地域的單元化等,全方位展現了 DTIM 在實際生產應用中所遇到的各種技術挑戰及相應的解決方案,希望借本文內容的分享能為國內企業級IM的開發帶來思考和啟發。

     

    學習交流:

    本文已同步發布于:http://www.52im.net/thread-4012-1-1.html

    二、系列文章

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

    相關文章:

    三、釘釘的技術挑戰

    釘釘已經有 2100 萬 + 組織、5 億 + 注冊用戶在使用。DTIM 為釘釘用戶提供即時消息服務,用于組織內外的溝通,這些組織包括公司、政府、學校等,規模從幾人到百萬人不等。

    DTIM 有著豐富的功能,單聊、各種類型的群聊、消息已讀、文字表情、多端同步、動態卡片、專屬安全和存儲等等。

    同時:釘釘內部很多業務模塊,比如文檔、釘閃會、Teambition、音視頻、考勤、審批等,每個業務都在使用 DTIM,用于實現業務流程通知、運營消息推送、業務信令下發等。每個業務模塊對于 DTIM 調用的流量峰值模型各有差別,對可用性要求也不盡相同。DTIM 需要能夠面對這些復雜的場景,保持良好的可用性和體驗,同時兼顧性能與成本。

    通用的即時消息系統對消息發送的成功率、時延、到達率有很高的要求,企業 IM 由于 ToB 的特性,在數據安全可靠、系統可用性、多終端體驗、開放定制等多個方面有著極致的要求。

    構建穩定高效的企業 IM 服務,DTIM 主要面臨的挑戰是:

    • 1)企業 IM 極致的體驗要求對于系統架構設計的挑戰:比如數據長期保存可漫游、多端數據同步、動態消息等帶來的數據存儲效率和成本壓力,多端數據同步帶來的一致性問題等;
    • 2)極限場景沖擊、依賴系統錯誤帶來的可用性問題:比如超大群消息,突發疫情帶來的線上辦公和線上教學高并發流量,系統需要能夠應對流量的沖擊,保障高可用;同時在中間件普遍可用性不到 99.99% 的時候,DTIM 服務需要保障核心功能的 99.995% 的可用性;
    • 3)不斷擴大的業務規模,對于系統部署架構的挑戰:比如持續增長的用戶規模,突發事件如席卷全球的疫情,單地域架構已經無法滿足業務發展的要求。

    DTIM 在系統設計上:

    • 1)為了實現消息收發體驗、性能和成本的平衡,設計了高效的讀寫擴散模型和同步服務,以及定制化的 NoSQL 存儲;
    • 2)通過對 DTIM 服務流量的分析,對于大群消息、單賬號大量的消息熱點以及消息更新熱點的場景進行了合并、削峰填谷等處理;
    • 3)核心鏈路的應用中間件的依賴做容災處理,實現了單一中間件失敗不影響核心消息收發,保障基礎的用戶體驗。

    在消息存儲過程中,一旦出現存儲系統寫入異常,系統會回旋緩沖重做,并且在服務恢復時,數據能主動向端上同步。

    隨著用戶數不斷增長,單一地域已無法承載 DTIM 的容量和容災需求,DTIM 實現了異地多單元的云原生的彈性架構。

    在分層上遵從的原則為重云輕端:業務數據計算、存儲、同步等復雜操作盡量后移到云端處理,客戶端只做終態數據的接收、展示,通過降低客戶端業務實現的復雜度,最大化地提升客戶端迭代速度,讓端上開發可以專注于提升用戶的交互體驗,所有的功能需求和系統架構都圍繞著該原則做設計和擴展。

    以下章節我們將對 DTIM 做更加詳細的介紹。

    四、模型設計

    4.1、DTIM系統架構

    低延遲、高觸達、高可用一直是 DTIM 設計的第一原則,依據這個原則在架構上 DTIM 將系統拆分為三個服務做能力的承載。

    三個服務分別是:

    • 1)消息服務:負責 IM 核心消息模型和開放 API,IM 基礎能力包括消息發送、單聊關系維護、群組元信息管理、歷史消息拉取、已讀狀態通知、IM 數據存儲以及跨地域的流量轉發;
    • 2)同步服務:負責用戶消息數據以及狀態數據的端到端同步,通過客戶端到服務端長連接通道做實時的數據交互,當釘釘各類設備在線時 IM 及上游各業務通過同步服務做多端的數據同步,保障各端數據和體驗一致;
    • 3)通知服務:負責用戶第三方通道維護以及通知功能,當釘釘的自建通道無法將數據同步到端上時,通過三方提供的通知和透傳能力做消息推送,保障釘釘消息的及時性和有效性。

    同步服務和通知服務除了服務于消息服務,也面向其他釘釘業務比如音視頻、直播、Ding、文檔等多端 (多設備) 數據同步。

    圖1:DTIM架構圖 ▼

    上圖展示了 DTIM 系統架構,接下來詳細介紹消息收發鏈路。

    4.2、消息收發鏈路

    圖2:DTIM消息處理架構 ▼

    1)消息發送:消息發送接口由 Receiver 提供,釘釘統一接入層將用戶從客戶端發送的消息請求轉發到 Receiver 模塊,Receiver 校驗消息的合法性(文字圖片等安全審核、群禁言功能是否開啟或者是否觸發會話消息限流規則等)以及成員關系的有效性(單聊校驗二者聊天、群聊校驗發送者在群聊成員列表中),校驗通過后為該消息生成一個全局唯一的 MessageId 隨消息體以及接收者列表打包成消息數據包投遞給異步隊列,由下游 Processor 處理。消息投遞成功之后,Receiver 返回消息發送成功的回執給客戶端。

    2)消息處理 :Processor 消費到 IM 發送事件首先做按接收者的地域分布(DTIM 支持跨域部署, Geography,Geo)做消息事件分流,將本域用戶的消息做本地存儲入庫(消息體、接收者維度、已讀狀態、個人會話列表紅點更新),最后將消息體以及本域接收者列表打包為 IM 同步事件通過異步隊列轉發給同步服務。

    3)消息接收 :同步服務按接收者維度寫入各自的同步隊列,同時查取當前用戶設備在線狀態,當用戶在線時撈取隊列中未同步的消息,通過接入層長連接推送到各端。當用戶離線時,打包消息數據以及離線用戶狀態列表為 IM 通知事件,轉發給通知服務的 PNS 模塊,PNS 查詢離線設備做三方廠商通道推送,至此一條消息的推送流程結束。

    4.3、存儲模型設計

    了解 IM 服務最快的途徑就是掌握它的存儲模型。

    業界主流 IM 服務對于消息、會話、會話與消息的組織關系雖然不盡相同,但是歸納起來主要是兩種形式:寫擴散讀聚合、讀擴散寫聚合。

    所謂讀寫擴散其實是定義消息在群組會話中的存儲形式。如下圖所示。

    圖3:讀模式和寫模式 ▼

    如上圖所示:

    • 1)讀擴散的場景:消息歸屬于會話,對應到存儲中相當于有張 conversation_message 的表存儲著該會話產生的所有消息 (cid->msgid->message,cid 會話 ID、msgid 消息 ID、message 消息),這樣實現的好處是消息入庫效率高,只存儲會話與消息的綁定關系即可;
    • 2)寫擴散的場景:會話產生的消息投遞到類似于個人郵件的收件箱,即 message_inbox 表,存儲個人的所有消息(uid->msgid->message, uid 用戶 ID、msgid 消息 ID、message 消息),基于這種實現,會話中的每條消息面向不同的接收者可以呈現出不同狀態。

    DTIM 對 IM 消息的及時性、前后端存儲狀態一致性要求異常嚴格,特別對于歷史消息漫游的訴求十分強烈,當前業界 IM 產品對于消息長時間存儲和客戶端歷史消息多端漫游都做得不盡如人意,主要是存儲成本過高。因此在產品體驗與投入成本之間需要找到一個平衡點。

    采用讀擴散:在個性化的消息擴展及實現層面有很大的約束。

    采用寫擴散帶來的問題也很明顯:一個群成員為 N 的會話一旦產生消息就會擴散 N 條消息記錄,如果在消息發送和擴散量較少的場景,這樣的實現相比于讀擴散落地更為簡單,存儲成本也不是問題。但是 DTIM 會話活躍度超高,一條消息的平均擴散比可以達到 1:30,超大群又是企業 IM 最核心的溝通場景,如果采用完全寫擴散所帶來存儲成本問題勢必制約釘釘業務發展。

    所以,在 DTIM 的存儲實現上,釘釘采取了混合的方案,將讀擴散和寫擴散針對不同場景做了適配,最終在用戶視角系統會做統一合并,如下圖所示。

    圖4:DTIM 讀寫混合模式 ▼

    作為讀擴散、寫擴散方案的混合形式存在,用戶維度的消息分別從 conversation_message 和 message_inbox 表中獲取,在應用側按消息發送時間做排序合并,conversation_message 表中記錄了該會話面向所有群成員接收的普通消息 N(Normal),而 message_inbox 表在以下兩種場景下被寫入:

    1)第一種是:定向消息 P(Private,私有消息),群會話中發送的消息指定了接收者范圍,那么會直接寫入到接收者的 message_inbox 表中,比如紅包的領取狀態消息只能被紅包發送者可見,那么這種消息即被定義為定向消息。

    2)第二種是:歸屬于會話消息的狀態轉換 NP(Normal to Private,普通消息轉私有消息),當會話消息通過某種行為使得某些消息接收者的消息狀態發生轉換時,該狀態會寫入到 message_inbox 表中,比如用戶在接收側刪除了消息,那么消息的刪除狀態會寫入到 message_inbox 中,在用戶拉取時會通過 message_inbox 的刪除狀態將 conversation_message 中的消息做覆蓋,最終用戶拉取不到自己已刪除的消息。

    當用戶在客戶端發起某個會話的歷史消息漫游請求時,服務端根據用戶上傳的消息截止時間(message_create_time)分別從 conversation_message 表和 message_inbox 表拉取消息列表,在應用層做狀態的合并,最終返回給用戶合并之后的數據,N、P、NP 三種類型消息在消息個性化處理和存儲成本之間取得了很好的平衡。

    4.4、同步模型設計

    4.4.1 推送模型

    用戶在會話中發出的消息和消息狀態變更等事件是如何同步到端上呢?

    業界關于消息的同步模型的實現方案大致有三種:

    • 1)客戶端拉取方案;
    • 2)服務端推送方案;
    • 3)服務端推送位點之后客戶端拉取的推拉結合方案。

    三種方案各有優劣,在此簡短總結:

    • 1)首先:客戶端拉取方案的優點是該方案實施簡單、研發成本低,是傳統的 B/S 架構。劣勢是效率低下,拉取間隔控制權在客戶端,對于 IM 這種實時的場景,很難設置一個有效的拉取間隔,間隔太短對服務端壓力大,間隔太長時效性差;
    • 2)其次:服務端主動推送方案的優點是低延遲、能做到實時,最重要的主動權在服務端。劣勢相對拉取方案,如何協調服務端和客戶端的處理能力存在問題;
    • 3)最后:推拉結合這個方案整合了拉和推的優點,但是方案更復雜,同時會比推的方案多一次 RTT,特別是在移動網絡的場景下,不得不面臨功耗和推送成功率的問題。

    DTIM 相對傳統 toC 的場景,有較明顯的區別:

    1)第一是對實時性的要求:在企業服務中,比如員工聊天消息、各種系統報警,又比如音視頻中的共享畫板,無不要求實時事件同步,因此需要一種低延時的同步方案。

    2)第二是弱網接入的能力:在 DTIM 服務的對象中,上千萬的企業組織涉及各行各業,從大城市 5G 的高速到偏遠的山區弱網,都需要 DTIM 的消息能發送、能觸達。對于復雜的網絡環境,需要服務端能判斷接入環境,并依據不同的環境條件調整同步數據的策略。

    3)第三是功耗可控成本可控:在 DTIM 的企業場景中,消息收發頻率比傳統的 IM 多出一個數量級,在這么大的消息收發場景怎么保障 DTIM 的功耗可控,特別是移動端的功耗可控,是 DTIM 必須面對的問題。在這種要求下,就需要 DTIM 盡量降低 IO 次數,并基于不同的消息優先級進行合并同步,既能要保障實時性不被破壞,又要做到低功耗。

    從以上三點可知,服務端主動推送的模型更適合 DTIM 場景:

    • 1)首先可以做到極低的延時,保障推送耗時在毫秒級別;
    • 2)其次是服務端能通過用戶接入信息判斷用戶接入環境好壞,進行對應的分包優化,保障弱網鏈路下的成功率;
    • 3)最后是主動推送相對于推拉結合來說,可以降低一次 IO,對 DTIM 這種每分鐘過億消息服務來說,能極大的降低設備功耗,同時配合消息優先級合并包的優化,進一步降低端的功耗。

    雖說主動推送有諸多優勢,但是客戶端會離線,甚至客戶端處理速度無法跟上服務端的速度,必然導致消息堆積。

    DTIM 為了協調服務端和客戶端處理能力不一致的問題,支持 Rebase 的能力,當服務端消息堆積的條數達到一定閾值時觸發 Rebase,客戶端會從 DTIM 拉取最新的消息,同時服務端跳過這部分消息從最新的位點開始推送消息。DTIM 稱這個同步模型為推優先模型(Preferentially-Push Model,PPM)。

    在基于 PPM 的推送方案下,為了保障消息的可靠達到,DTIM 還做一系列優化。

    這些優化具體是:

    1)支持消息可重入:服務端可能會針對某條消息做重復推送,客戶端需要根據 msgId 做去重處理,避免端上消息重復展示。

    2)支持消息排序:服務端推送消息特別是群比較活躍的場景,某條消息由于推送鏈路或者端側網絡抖動,推送失敗,而新的消息正常推送到端側,如果端上不做消息排序的話,消息列表就會發生亂序,所以服務端會為每條消息分配一個時間戳,客戶端每次進入消息列表就是根據時間戳做排序再投遞給 UI 層做消息展示。

    3)支持缺失數據回補:在某個極端情況下客戶端群消息事件比群創建事件更早到達端上,此時端上沒有群的基本信息消息也就無法展現,所以需要客戶端主動向服務端拉取群信息同步到本地,再做消息的透出。

    4.4.2 多端數據一致性

    多端數據一致性問題一直是多端同步最核心的問題,單個用戶可以同時在 PC、Pad 以及 Mobile 登錄,消息、會話紅點等狀態需要在多端保持一致,并且用戶更換設備情況下消息可以做全量的回溯。

    基于上面的業務訴求以及系統層面面臨的諸多挑戰,釘釘自研了同步服務來解決一致性問題。

    釘釘的同步服務的設計理念和原則如下:

    • 1)統一消息模型抽象,對于 DTIM 服務產生的新消息以及已讀事件、會話增刪改、多端紅點清除等事件統一抽象為同步服務的事件;
    • 2)同步服務不感知事件的類型以及數據序列化方式。同步服務為每個用戶的事件分配一個自增的 ID(注:這里非連續遞增),確保消息可以根據 ID 做遍歷的有序查詢;
    • 3)統一同步隊列,同步服務為每個用戶分配了一個 FIFO 的隊列存儲,自增 id 和事件串行寫入隊列;當有事件推送時,服務端根據用戶當前各端在線設備狀態做增量變更,將增量事件推送到各端;
    • 4)根據設備和網絡質量的不同可以做多種分包推送策略,確保消息可以有序、可靠、高效的發送給客戶端。

    上面介紹了 DTIM 的存儲模型以及同步模型的設計與思考:

    1)在存儲優化中,存儲會基于 DTIM 消息特點進行深度優化,并會對其中原理以及實現細節做深入分析與介紹;

    2)在同步機制中,會進一步介紹多端同步機制是如何保障消息必達以及各端消息一致性。

    五、消息存儲設計

    DTIM 底層使用了表格存儲作為消息系統的核心存儲系統,表格存儲是一個典型 LSM 存儲架構,讀寫放大是此類系統的典型問題。

    LSM 系統通過 Major Compaction 來降低數據的 Level 高度,降低讀取數據放大的影響。在 DTIM 的場景中,實時消息寫入超過百萬 TPS,系統需要劃歸非常多的計算資源用于 Compaction 處理,但是在線消息讀取延遲毛刺依舊嚴重。

    在存儲的性能分析中,我們發現如下幾個特點:

    • 1)6% 的用戶貢獻了 50% 左右的消息量,20% 的用戶貢獻了 74% 的消息量。高頻用戶產生的消息遠多于低頻用戶,在 Flush MemTable 時,高頻用戶消息占據了文件的絕大部分;
    • 2)對于高頻的用戶,由于其“高頻”的原因,當消息進入 DTIM,系統發現用戶設備在線(高概率在線),會立即推送消息,因此需要推送的消息大部分在內存的 MemTable 中;
    • 3)高頻用戶產生大量的消息,Compaction 耗費了系統大量的計算和 IO 資源;
    • 4)低頻的用戶消息通常散落在多個文件當中。

    從上面的四個表現來看,我們能得出如下結論:

    • 1)絕大部分 Compaction 是無效的計算和 IO,由于大量消息寫入產生大量的文件,但是高頻的用戶消息其實已經下推給用戶的設備,Compaction 對讀加速效果大打折扣。反而會搶占計算資源,同時引起 IO 抖動;
    • 2)低頻用戶由于入庫消息頻率低,在 Flush 之后的文件中占比低;同時用戶上線頻率低,期間會累計較多的待接收的消息,那么當用戶上線時,連續的歷史消息高概率散落在多個文件當中,導致遍歷讀取消息毛刺,嚴重的有可能讀取超時。

    為了解決此類問題,我們采用分而治之方法,將高頻用戶和低頻用戶的消息區別對待。

    我們借鑒了 WiscKey KV 分離技術的思想,就是將到達一定閾值的 Value 分離出來,降低這類消息在文件中的占比進而有效的降低寫放大的問題。

    附:WiscKey KV原始論文附件下載:

     WiscKey:Separating Keys from Values in SSD-conscious Storage(52im.net).pdf (2.24 MB)

    但是 WiscKey KV 分離僅考慮單 Key 的情況,在 DITM 的場景下,Key 之間的大小差距不大,直接采用這種 KV 分離技術并不能解決以上問題。因此我們在原有 KV 分離的基礎上,改進了 KV 分離,將相同前綴的多個 Key 聚合判斷,如果多個 Key 的 Value 超過閾值,那么將這些 Key 的 Value 打包了 value-block 分離出去,寫入到 value 文件。

    數據顯示:上述方法能夠在 Minor Compaction 階段將 MemTable 中 70% 的消息放入 value 文件,大幅降低 key 文件大小,帶來更低的寫放大;同時,Major Compaction 可以更快降低 key 文件數,使讀放大更低。高頻用戶發送消息更多,因此其數據行更容易被分離到 value 文件。讀取時,高頻用戶一般把最近消息全部讀出來,考慮到 DTIM 消息 ID 是遞增生成,消息讀取的 key 是連續的,同一個 value-block 內部的數據會被順序讀取,基于此,通過 IO 預取技術提前讀取 value-block,可以進一步提高性能。對于低頻用戶,其發送消息較少,K-V 分離不生效,從而減少讀取時候 value 文件帶來的額外 IO。

    圖5:KV分離和預讀取 ▼

    六、多端同步機制設計

    6.1、概述

    DTIM 面向辦公場景,和面向普通用戶的產品在服務端到客戶端的數據同步上最大的區別是消息量巨大、變更事件復雜、對多端同步有著強烈的訴求。

    DTIM 基于同步服務構建了一套完整同步流程。同步服務是一個服務端到客戶端的數據同步服務,是一套統一的數據下行平臺,支撐釘釘多個應用服務。

    圖6:微服務架構 ▼

    同步服務是一套多端的數據同步服務,由兩部分組成:

    • 1)部署于服務端的同步服務;
    • 2)由客戶端 APP 集成的同步服務 SDK。

    工作原理類似于MQ消息隊列:

    • 1)用戶 ID 近似消息隊列中的 Topic;
    • 2)用戶設備近似消息隊列中的 Consumer Group。

    每個用戶設備作為一個消息隊列消費者能夠按需獲得這個用戶一份數據拷貝,從而實現了多端同步訴求。

    當業務需要同步一個變更數據到指定的用戶或設備時:業務調用數據同步接口,服務端會將業務需要同步的數據持久化到存儲系統中,然后當客戶端在線的時候把數據推送給客戶端。每一條數據入庫時都會原子的生成一個按用戶維度單調遞增的位點,服務端會按照位點從小到大的順序把每一條數據都推送至客戶端。

    客戶端應答接收成功后:更新推送數據最大的位點到位點管理存儲中,下次推送從這個新的位點開始推送。同步服務 SDK 內部負責接收同步服務數據,接收后寫入本地數據庫,然后再把數據異步分發到客戶端業務模塊,業務模塊處理成功后刪除本地存儲對應的內容。

    在上文章節中,已經初步介紹同步服務推送模型和多端一致性的考慮,本章主要是介紹 DTIM 是如何做存儲設計、在多端同步如何實現數據一致性、最后再介紹服務端消息數據堆積技術方案 Rebase。

    * 推薦閱讀:如果您對消息同步機制沒有概念,可以先行閱讀《淺談移動端IM的多點登陸和消息漫游原理》。

    6.2、全量消息存儲邏輯

    在同步服務中,采用以用戶為中心,將所有要推送給此用戶的消息匯聚在一起,并為每個消息分配唯一且遞增的 PTS(即位點,英文術語Point To Sequence),服務端保存每個設備推送的位點。

    通過兩個用戶 Bob 和 Alice,來實際展示消息在存儲系統中存儲的邏輯形態。例如:Bob 給 Alice 發送了一個消息”Hi! Alice“,Alice 回復了 Bob 消息”Hi! Bob“。

    當 Bob 發送第一條消息給 Alice 時,接收方分別是 Bob 和 Alice,系統會在 Bob 和 Alice 的存儲區域末尾分別添加一條消息,存儲系統在入庫成功時,會分別為這兩行分配一個唯一且遞增的位點(Bob 的位點是 10005,Alice 的位點是 23001);入庫成功之后,觸發推送。比如 Bob 的 PC 端上一次下推的位點是 10000,Alice 移動端的推送位點是 23000,在推送流程發起之后,會有兩個推送任務,第一是 Bob 的推送任務,推送任務從上一次位點(10000) + 1 開始查詢數據,將獲取到 10005 位置的”Hi“消息,將此消息推送給 Bob 的設備,推送成功之后,存儲推送位點(10005)。Alice 推送流程也是同理。Alice 收到 Bob 消息之后,Alice 回復 Bob,類似上面的流程,入庫成功并分配位點(Bob 的位點是 10009,Alice 的位點是 23003)。

    圖7:同步服務的存儲設計 ▼

    6.3、消息多端同步邏輯

    多端同步是 DTIM 的典型特點,如何保持多端的數據及時觸達和解決一致性是 DTIM 同步服務最大的挑戰。

    上文中已經介紹了同步服務的事件存儲模型,將需要推送的消息按照用戶聚合。當用戶有多個設備時,將設備的位點保存在位點管理系統之中,Key 是用戶 + 設備 ID,Value 是上一次推送的位點。如果是設備第一次登錄,位點默認為 0。

    由此可知:每個設備都有單獨的位點,數據在服務端只有一份按照用戶維度的數據,推送到客戶端的消息是服務端對應位點下的快照,從而保障了每個端的數據都是一致的。

    比如:此時 Bob 登錄了手機(該設備之前登錄過釘釘),同步服務會獲取到設備登錄的事件,事件中有此設備上次接收數據的位點(比如 10000),同步服務會從 10000 + 1(位點)開始查詢數據,獲取到五條消息(10005~10017),將消息推送給此臺手機并更新服務端位點。此時,Bob 手機和 PC 上的消息一致,當 Alice 再次發送消息時,同步服務會給 Bob 的兩臺設備推送消息,始終保持 Bob 兩個設備之間消息數據的一致性。

    6.4、大量需同步離線消息的優化邏輯

    正如上文所述:我們采用了推優先的模型下推數據以保障事件的實時性,采用位點管理實現多端同步,但是實際情況卻遠比上面的情況復雜。

    最常見的問題:就是設備離線重新登錄,期間該用戶可能會累計大量未接收的消息數據,比如幾萬條。如果按照上面的方案,服務端在短時間會給客戶端推送大量的消息,客戶端 CPU 資源極有可能耗盡導致整個設備假死。

    其實對于 IM 這種場景來說:幾天甚至幾小時之前的數據,再推送給用戶已經喪失即時消息的意義,反而會消耗客戶移動設備的電量,得不償失。又或者節假日大群中各種活動,都會有大量的消息產生。

    對于以上情況:同步服務提供 Rebase 的方案,當要推送的消息累計到一定閾值時,同步服務會向客戶端發送 Rebase 事件,客戶端收到事件之后,會從消息服務中獲取到最新的消息(Lastmsg)。這樣可以跳過中間大量的消息,當用戶需要查看歷史消息,可以基于 Lastmsg 向上回溯,即省電也能提升用戶體驗。

    還是以 Bob 為例:Bob 登錄了 Pad 設備(一臺全新的設備),同步服務收到 Pad 登錄的事件,發現登錄的位點為 0,查詢從 0 開始到當前,已經累計 1 萬條消息,累計量大于同步服務的閾值,同步服務發送 Rebase 事件給客戶端,客戶端從消息服務中獲取到最新的一條消息“Tks !!!”,同時客戶端從同步服務中獲取最新的位點為 10017,并告訴同步服務后續從 10017 這個位置之后開始推送。當 Bob 進入到和 Alice 的會話之后,客戶端只要從 Lastmsg 向上回溯幾條歷史消息填滿聊天框即可。

    七、高可用設計

    7.1、概述

    DTIM 對外提供 99.995% 的可用性 SLA,有上百萬的組織將釘釘作為自身數字化辦公的基礎設施,由于其極廣的覆蓋面,DTIM 些許抖動都會影響大量企業、機構、學校等組織,進而可能形成社會性事件。因此,DTIM 面臨著極大的穩定性挑戰。

    高可用是 DTIM 的核心能力。對于高可用,需要分兩個維度來看,首先是服務自我防護,在遇到流量洪峰、黑客攻擊、業務異常時,要有流量管控、容錯等能力,保障服務在極端流量場景下還有基本服務的能力。其次是服務擴展能力,比如常見的計算資源的擴展、存儲資源的擴展等,資源伴隨流量增長和縮減,提供優質的服務能力并與成本取得較好的平衡。

    7.2、自我防護

    7.2.1 背景

    DTIM 經常會面臨各種突發大流量,比如萬人大群紅包大戰、早高峰打卡提醒、春節除夕紅包等等都會在短時間內產生大量的聊天消息,給系統帶來很大的沖擊,基于此我們采用了多種措施。

    首先是流量管控,限流是保護系統最簡單有效的方式。DTIM 服務通過各種維度的限流來保護自身以及下游,最重要的是保護下游的存儲。在分布式系統中存儲都是分片的,最容易出現的是單個分片的熱點問題,DTIM 里面有兩個維度的數據:用戶、會話 (消息屬于會話), 分片也是這兩個維度,所以限流采用了會話、用戶維度的限流,這樣既可以保護下游存儲單個分區,又可以一定程度上限制整體的流量。要控制系統的整體流量, 前面兩個維度還不夠,DTIM 還采用了客戶端類型、應用 (服務端 IM 上游業務) 兩個維度的限流,來避免整體的流量上漲對系統帶來的沖擊。

    其次是削峰平谷。限流簡單有效,但是對用戶的影響比較大。在 DTIM 服務中有很多消息對于實時性要求不高,比如運營推送等。對于這些場景中的消息可以充分利用消息系統異步性的特點,使用異步消息隊列進行削峰平谷,這樣一方面減少了對用戶的影響,另一方面也減輕對下游系統的瞬時壓力。DTIM 可以根據業務類型 (比如運營推送)、消息類型 (比如點贊消息) 等多種維度對消息進行分級,對于低優先級的消息保證在一定時間 (比如 1 個小時) 內處理完成。

    最后是熱點優化。DTIM 服務中面臨著各種熱點問題,對于這些熱點問題僅僅靠限流是不夠的。比如通過釘釘小秘書給大量用戶推送升級提醒,由于是一個賬號與大量賬號建立會話,因此會存在 conversation_inbox 的熱點問題,如果通過限速來解決,會導致推送速度過慢、影響業務。對于這類問題需要從架構上來解決。

    總的來說,主要是兩類問題:大賬號和大群導致的熱點問題。對于大賬戶問題,由于 conversation_inbox 采用用戶維度做分區,會導致系統賬號的請求都落到某個分區,從而導致熱點。解決方案為做熱點拆分,既將 conversation_inbox 數據合并到 conversation_member 中 (按照會話做分區),將用戶維度的操作轉換為會話維度的操作,這樣就可以將系統賬號的請求打散到所有分區上, 從而實現消除熱點。對于大群問題,壓力來自大量發消息、消息已讀和貼表情互動,大量的接收者帶來極大的擴散量。所以我們針對以上三個場景,分而治之。

    7.2.2 計算延遲與按需拉取

    對于消息發送,一般的消息對于群里面所有人都是一樣的,所以可以采用讀擴散的方式,即不管多大的群,發一條消息就存儲一份。另一方面,由于每個人在每個會話上都有紅點數和 Lastmsg, 一般情況下每次發消息都需要更新紅點和 Lastmsg,但是在大群場景下會存在大量擴散,對系統帶來巨大的壓力。我們的解決方案為,對于大群的紅點和 Lastmsg,在發消息時不更新,在拉首屏時實時算,由于拉首屏是低頻操作且每個人只有一到兩個大群,實時計算壓力很小,這樣高峰期可以減少 99.99 % 的存儲操作, 從而解決了大群發消息對 DTIM 帶來的沖擊。

    7.2.3 請求合并

    在大群發消息的場景中,如果用戶都在線,瞬時就會有大量已讀請求,如果每個已讀請求都處理,則會產生 M*N(M 消息條數,N 群成員數) 的擴散,這個擴散是十分恐怖的。

    DTIM 的解決方案是客戶端將一個會話中的多次已讀進行合并,一次性發送給服務端,服務端對于每條消息的已讀請求進行合并處理,比如 1 分鐘的所有請求合并為 1 次請求。在大群中,進行消息點贊時,短時間會對消息產生大量更新,再加上需要擴散到群里面的所有人,系統整體的擴散量十分巨大。我們發現,對于消息多次更新的場景,可以將一段時間里面多次更新合并,可以大大減少擴散量,從實際優化之后的數據來看,高峰期系統的擴散量同比減少 96%。

    即使完全做到以上幾點,也很難提供當前承諾的 SLA,除了防止自身服務出現問題以外,還必須實現對依賴組件的容災。我們整體采用了冗余異構存儲和異步隊列與 RPC 相結合的方案,當任意一類 DTIM 依賴的產品出現問題時, DTIM 都能正常工作,由于篇幅問題,此處不再展開。

    7.3、水平擴展能力

    對于服務的彈性擴展能力,也需要分兩個維度來看:

    • 1)首先:服務內部的彈性擴展,比如計算資源的擴展、存儲資源的擴展等,是我們通常構建彈性擴展能力關注的重點方向;
    • 2)其次:跨地域維度的擴展,服務能根據自身需要,在其他區域擴展一個服務集群,新的服務集群承接部分流量,在跨地域層面形成一個邏輯統一的分布式服務,這種分布式服務我們稱之為單元化。

    7.3.1 彈性應用架構

    對于 DTIM 的擴展性,因為構建和生長于云上,在彈性擴展能力建設擁有了更多云的特點和選擇。

    對于計算節點:應用具備橫向擴展的能力,系統能在短時間之內感知流量突增進而進行快速擴容,對于上文提到的各種活動引起的流量上漲,能做到輕松應對。同時,系統支持定時擴容和縮容,在系統彈性能力和成本之間取得較好的平衡。

    對于存儲:DTIM 底層選擇了可以水平擴展的 Serverless 存儲服務,存儲服務內部基于讀寫流量的大小進行動態調度,應用上層完全無感知。對于服務自身的擴展性,我們還實施了不可變基礎設施、應用無狀態、去單點、松耦合、負載均衡等設計,使 DTIM 構建出了一套高效的彈性應用架構。

    7.3.2 彈性地域級擴展(單元化)

    在應用內部實現了高效彈性之后,伴隨著業務流量的增長,單個地域已經無法滿足 DTIM 億級別 DUA 的彈性擴展的需求。由于 DTIM 特點,所有用戶都可以在添加好友之后進行聊天,這就意味著不能簡單換個地域搭建一套孤島式的 DTIM。

    為了解決這種規模下的彈性能力,我們基于云上的地域架構,在一個 Geo 地域內,構建了一套異地多活、邏輯上是一體的彈性架構,我們稱之為單元化。下圖是 DTIM 的單元化架構。

    圖8:DTIM單元化架構 ▼:

    對于單元化的彈性擴展架構,其中最核心的內容是流量動態調度、數據單地域的自封閉性和單元整體降級。

    7.3.3 彈性動態調度

    流量路由決定了數據流向,我們可以依托這個能力,將大群流量調度到新的單元來承接急速增長的業務流量,同時實現流量按照企業維度匯聚,提供就近部署能力,進而提供優質的 RT 服務。

    業界現在主流的單元化調度方案主要是基于用戶維度的靜態路由分段,這種方案算法簡單可靠,但是很難實現動態路由調度,一旦用戶路由固定,無法調整服務單元。

    比如:在 DTIM 的場景中,企業(用戶)規模是隨著時間增長、用戶業務規模增長之后,單地域無法有效支撐多個大型企業用戶時,傳統靜態方案很難將企業彈性擴展到其他單元,強行遷移會付出極高的運維代價。因此傳統的路由方案不具備彈性調度能力。

    DTIM 提供一套全局一致性的高可用路由服務系統 (RoutingService)。服務中存儲了用戶會話所在單元,消息服務基于路由服務,將流量路由到不同的單元。應用更新路由數據之后,隨之路由信息也發生變化。與此同時,路由服務發起數據訂正事件,將會話的歷史消息數據進行遷移,遷移完成之后正式切換路由。路由服務底層依賴存儲的 GlobalTable 能力,路由信息更新完成之后,保障跨地域的一致性。

    7.3.4 彈性單元自封閉

    數據的單元自封閉是將 DTIM 最重要且規模最大的數據:“消息數據”的接收、處理、持久化、推送等過程封閉在當前單元中,解除了對其他單元依賴,進而能高效地擴充單元,實現跨地域級別高效彈性能力。

    要做到業務數據在單元內自封閉,最關鍵是要識別清楚要解決哪種數據的彈性擴展能力。在 DTIM 的場景下,用戶 Profile、會話數據、消息數據都是 DTIM 最核心的資產,其中消息數據的規模遠超其他數據,彈性擴展能力也是圍繞消息數據的處理在建設。怎么將消息按照單元數據合理的劃分成為單元自封閉的關鍵維度。

    在 IM 的場景中,消息數據來自于人與人之間的聊天,可以按照人去劃分數據,但是如果聊天的兩個人在不同的單元之間,消息數據必然要在兩個單元拷貝或者冗余,因此按照人劃分數據并不是很好的維度。

    DTIM 采用了會話維度劃分:因為人和會話都是元數據,數據規模有限,消息數據近乎無限,消息歸屬于會話,會話與會話之間并無交集,消息處理時并沒有跨單元的調用。因此,將不同的會話拆分到不同的單元,保障了消息數據僅在一個單元處理和持久化,不會產生跨單元的請求調用,進而實現了單元自封閉。

    7.3.5 彈性單元降級

    在單元化的架構中,為了支持服務級別的橫向擴展能力,多單元是基本形態。

    單元的異常流量亦或者是服務版本維護的影響都會放大影響面,進而影響 DTIM 整體服務。

    因此:DTIM 重點打造了單元降級的能力,單一單元失去服務能力之后,DTIM 會將業務流量切換到新的單元,新消息會從正常的單元下推,釘釘客戶端在數據渲染時也不會受到故障單元的影響,做到了單元故障切換用戶無感知。

    八、本文小結

    本文通過模型設計、存儲優化、同步機制以及高可用等維度,本文全方位地展示了當代企業級 IM 設計的核心。

    本文也是對 DTIM 過去一段時間的技術總結,隨著用戶數的持續增長,DTIM 也在與時俱進、持續迭代和優化,比如支持條件索引進而實現索引加速和成本可控、實現消息位點的連續累加、實現消息按需拉取和高效的完整性校驗、提供多種上下行通道,進一步提升弱網下的成功率和體驗等。

    九、相關文章

    [1] 企業級IM王者——釘釘在后端架構上的過人之處

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

    [3] 釘釘——基于IM技術的新一代企業OA平臺的技術挑戰(視頻+PPT)

    [5] 一套億級用戶的IM架構技術干貨(上篇):整體架構、服務拆分等

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

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

    [8] 企業微信的IM架構設計揭秘:消息模型、萬人群、已讀回執、消息撤回等

    [9] 全面揭秘億級IM消息的可靠投遞機制

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

    [11] 一套海量在線用戶的移動端IM架構設計實踐分享(含詳細圖文)

    [12] 一套原創分布式即時通訊(IM)系統理論架構方案

    本文已同步發布于:http://www.52im.net/thread-4012-1-1.html



    作者:Jack Jiang (點擊作者姓名進入Github)
    出處:http://www.52im.net/space-uid-1.html
    交流:歡迎加入即時通訊開發交流群 215891622
    討論:http://www.52im.net/
    Jack Jiang同時是【原創Java Swing外觀工程BeautyEye】【輕量級移動端即時通訊框架MobileIMSDK】的作者,可前往下載交流。
    本博文 歡迎轉載,轉載請注明出處(也可前往 我的52im.net 找到我)。


    只有注冊用戶登錄后才能發表評論。


    網站導航:
     
    Jack Jiang的 Mail: jb2011@163.com, 聯系QQ: 413980957, 微信: hellojackjiang
    主站蜘蛛池模板: 羞羞视频在线观看免费| 特级毛片爽www免费版| 一级特级aaaa毛片免费观看| 久久精品中文字幕免费| 成人毛片手机版免费看| 国产国拍精品亚洲AV片| 亚洲成人福利在线观看| 免费大片av手机看片| 久久这里只精品热免费99| 永久免费无码网站在线观看 | 亚洲电影日韩精品| 亚洲高清免费在线观看| 精品国产日韩亚洲一区在线 | www.黄色免费网站| 亚洲国产精品一区二区三区久久 | 国产免费啪嗒啪嗒视频看看| 亚洲国产AV无码专区亚洲AV| 亚洲日本成本人观看| 免费萌白酱国产一区二区三区| 成年女人免费视频播放体验区 | 亚洲成a人片77777老司机| 欧美日韩亚洲精品| 亚洲毛片免费观看| 中文字幕亚洲综合久久男男| 最新国产成人亚洲精品影院| 在线人成免费视频69国产| 免费国产人做人视频在线观看| 亚洲日产2021三区| GOGOGO高清免费看韩国| 国产高清在线免费视频| 伊人久久综在合线亚洲2019| 成在线人直播免费视频| 成人免费福利电影| 一区二区三区亚洲| 不卡视频免费在线观看| 国产一级淫片视频免费看| 亚洲午夜成激人情在线影院| 高清一区二区三区免费视频| 亚洲免费日韩无码系列| 亚洲码和欧洲码一码二码三码| 最近的中文字幕大全免费8|