阿里IM技術(shù)分享(八):深度解密釘釘即時(shí)消息服務(wù)DTIM的技術(shù)設(shè)計(jì)

本文引用自InfoQ社區(qū)“5億用戶如何高效溝通?釘釘首次對(duì)外揭秘即時(shí)消息服務(wù)DTIM”一文,作者陳萬(wàn)紅等、策劃褚杏娟,有修訂和改動(dòng)。
一、引言
本文是國(guó)內(nèi)企業(yè)IM的事實(shí)王者釘釘首次對(duì)外深度解密其即時(shí)消息服務(wù)(即DingTalk IM,簡(jiǎn)稱DTIM)的技術(shù)設(shè)計(jì)實(shí)踐。
本篇文章內(nèi)容將從模型設(shè)計(jì)原理到具體的技術(shù)架構(gòu)、最底層的存儲(chǔ)模型到跨地域的單元化等,全方位展現(xiàn)了 DTIM 在實(shí)際生產(chǎn)應(yīng)用中所遇到的各種技術(shù)挑戰(zhàn)及相應(yīng)的解決方案,希望借本文內(nèi)容的分享能為國(guó)內(nèi)企業(yè)級(jí)IM的開發(fā)帶來(lái)思考和啟發(fā)。

學(xué)習(xí)交流:
- 移動(dòng)端IM開發(fā)入門文章:《新手入門一篇就夠:從零開發(fā)移動(dòng)端IM》
- 開源IM框架源碼:https://github.com/JackJiang2011/MobileIMSDK(備用地址點(diǎn)此)
(本文已同步發(fā)布于:http://www.52im.net/thread-4012-1-1.html)
二、系列文章
本文是系列文章的第8篇,總目錄如下:
《阿里IM技術(shù)分享(一):企業(yè)級(jí)IM王者——釘釘在后端架構(gòu)上的過(guò)人之處》
《阿里IM技術(shù)分享(二):閑魚IM基于Flutter的移動(dòng)端跨端改造實(shí)踐》
《阿里IM技術(shù)分享(三):閑魚億級(jí)IM消息系統(tǒng)的架構(gòu)演進(jìn)之路》
《阿里IM技術(shù)分享(四):閑魚億級(jí)IM消息系統(tǒng)的可靠投遞優(yōu)化實(shí)踐》
《阿里IM技術(shù)分享(五):閑魚億級(jí)IM消息系統(tǒng)的及時(shí)性優(yōu)化實(shí)踐》
《阿里IM技術(shù)分享(六):閑魚億級(jí)IM消息系統(tǒng)的離線推送到達(dá)率優(yōu)化》
《阿里IM技術(shù)分享(七):閑魚IM的在線、離線聊天數(shù)據(jù)同步機(jī)制優(yōu)化實(shí)踐》
《阿里IM技術(shù)分享(八):深度解密釘釘即時(shí)消息服務(wù)DTIM的技術(shù)設(shè)計(jì)》(* 本文)
相關(guān)文章:
現(xiàn)代IM系統(tǒng)中聊天消息的同步和存儲(chǔ)方案探討
釘釘——基于IM技術(shù)的新一代企業(yè)OA平臺(tái)的技術(shù)挑戰(zhàn)(視頻+PPT)
企業(yè)微信的IM架構(gòu)設(shè)計(jì)揭秘:消息模型、萬(wàn)人群、已讀回執(zhí)、消息撤回等
三、釘釘?shù)募夹g(shù)挑戰(zhàn)

釘釘已經(jīng)有 2100 萬(wàn) + 組織、5 億 + 注冊(cè)用戶在使用。DTIM 為釘釘用戶提供即時(shí)消息服務(wù),用于組織內(nèi)外的溝通,這些組織包括公司、政府、學(xué)校等,規(guī)模從幾人到百萬(wàn)人不等。
DTIM 有著豐富的功能,單聊、各種類型的群聊、消息已讀、文字表情、多端同步、動(dòng)態(tài)卡片、專屬安全和存儲(chǔ)等等。
同時(shí):釘釘內(nèi)部很多業(yè)務(wù)模塊,比如文檔、釘閃會(huì)、Teambition、音視頻、考勤、審批等,每個(gè)業(yè)務(wù)都在使用 DTIM,用于實(shí)現(xiàn)業(yè)務(wù)流程通知、運(yùn)營(yíng)消息推送、業(yè)務(wù)信令下發(fā)等。每個(gè)業(yè)務(wù)模塊對(duì)于 DTIM 調(diào)用的流量峰值模型各有差別,對(duì)可用性要求也不盡相同。DTIM 需要能夠面對(duì)這些復(fù)雜的場(chǎng)景,保持良好的可用性和體驗(yàn),同時(shí)兼顧性能與成本。
通用的即時(shí)消息系統(tǒng)對(duì)消息發(fā)送的成功率、時(shí)延、到達(dá)率有很高的要求,企業(yè) IM 由于 ToB 的特性,在數(shù)據(jù)安全可靠、系統(tǒng)可用性、多終端體驗(yàn)、開放定制等多個(gè)方面有著極致的要求。
構(gòu)建穩(wěn)定高效的企業(yè) IM 服務(wù),DTIM 主要面臨的挑戰(zhàn)是:
1)企業(yè) IM 極致的體驗(yàn)要求對(duì)于系統(tǒng)架構(gòu)設(shè)計(jì)的挑戰(zhàn):比如數(shù)據(jù)長(zhǎng)期保存可漫游、多端數(shù)據(jù)同步、動(dòng)態(tài)消息等帶來(lái)的數(shù)據(jù)存儲(chǔ)效率和成本壓力,多端數(shù)據(jù)同步帶來(lái)的一致性問(wèn)題等;
2)極限場(chǎng)景沖擊、依賴系統(tǒng)錯(cuò)誤帶來(lái)的可用性問(wèn)題:比如超大群消息,突發(fā)疫情帶來(lái)的線上辦公和線上教學(xué)高并發(fā)流量,系統(tǒng)需要能夠應(yīng)對(duì)流量的沖擊,保障高可用;同時(shí)在中間件普遍可用性不到 99.99% 的時(shí)候,DTIM 服務(wù)需要保障核心功能的 99.995% 的可用性;
3)不斷擴(kuò)大的業(yè)務(wù)規(guī)模,對(duì)于系統(tǒng)部署架構(gòu)的挑戰(zhàn):比如持續(xù)增長(zhǎng)的用戶規(guī)模,突發(fā)事件如席卷全球的疫情,單地域架構(gòu)已經(jīng)無(wú)法滿足業(yè)務(wù)發(fā)展的要求。
DTIM 在系統(tǒng)設(shè)計(jì)上:
1)為了實(shí)現(xiàn)消息收發(fā)體驗(yàn)、性能和成本的平衡,設(shè)計(jì)了高效的讀寫擴(kuò)散模型和同步服務(wù),以及定制化的 NoSQL 存儲(chǔ);
2)通過(guò)對(duì) DTIM 服務(wù)流量的分析,對(duì)于大群消息、單賬號(hào)大量的消息熱點(diǎn)以及消息更新熱點(diǎn)的場(chǎng)景進(jìn)行了合并、削峰填谷等處理;
3)核心鏈路的應(yīng)用中間件的依賴做容災(zāi)處理,實(shí)現(xiàn)了單一中間件失敗不影響核心消息收發(fā),保障基礎(chǔ)的用戶體驗(yàn)。
在消息存儲(chǔ)過(guò)程中,一旦出現(xiàn)存儲(chǔ)系統(tǒng)寫入異常,系統(tǒng)會(huì)回旋緩沖重做,并且在服務(wù)恢復(fù)時(shí),數(shù)據(jù)能主動(dòng)向端上同步。
隨著用戶數(shù)不斷增長(zhǎng),單一地域已無(wú)法承載 DTIM 的容量和容災(zāi)需求,DTIM 實(shí)現(xiàn)了異地多單元的云原生的彈性架構(gòu)。
在分層上遵從的原則為重云輕端:業(yè)務(wù)數(shù)據(jù)計(jì)算、存儲(chǔ)、同步等復(fù)雜操作盡量后移到云端處理,客戶端只做終態(tài)數(shù)據(jù)的接收、展示,通過(guò)降低客戶端業(yè)務(wù)實(shí)現(xiàn)的復(fù)雜度,最大化地提升客戶端迭代速度,讓端上開發(fā)可以專注于提升用戶的交互體驗(yàn),所有的功能需求和系統(tǒng)架構(gòu)都圍繞著該原則做設(shè)計(jì)和擴(kuò)展。
以下章節(jié)我們將對(duì) DTIM 做更加詳細(xì)的介紹。
四、模型設(shè)計(jì)
4.1、DTIM系統(tǒng)架構(gòu)
低延遲、高觸達(dá)、高可用一直是 DTIM 設(shè)計(jì)的第一原則,依據(jù)這個(gè)原則在架構(gòu)上 DTIM 將系統(tǒng)拆分為三個(gè)服務(wù)做能力的承載。
三個(gè)服務(wù)分別是:
1)消息服務(wù):負(fù)責(zé) IM 核心消息模型和開放 API,IM 基礎(chǔ)能力包括消息發(fā)送、單聊關(guān)系維護(hù)、群組元信息管理、歷史消息拉取、已讀狀態(tài)通知、IM 數(shù)據(jù)存儲(chǔ)以及跨地域的流量轉(zhuǎn)發(fā);
2)同步服務(wù):負(fù)責(zé)用戶消息數(shù)據(jù)以及狀態(tài)數(shù)據(jù)的端到端同步,通過(guò)客戶端到服務(wù)端長(zhǎng)連接通道做實(shí)時(shí)的數(shù)據(jù)交互,當(dāng)釘釘各類設(shè)備在線時(shí) IM 及上游各業(yè)務(wù)通過(guò)同步服務(wù)做多端的數(shù)據(jù)同步,保障各端數(shù)據(jù)和體驗(yàn)一致;
3)通知服務(wù):負(fù)責(zé)用戶第三方通道維護(hù)以及通知功能,當(dāng)釘釘?shù)淖越ㄍǖ罒o(wú)法將數(shù)據(jù)同步到端上時(shí),通過(guò)三方提供的通知和透?jìng)髂芰ψ鱿⑼扑停U厢斸斚⒌募皶r(shí)性和有效性。
同步服務(wù)和通知服務(wù)除了服務(wù)于消息服務(wù),也面向其他釘釘業(yè)務(wù)比如音視頻、直播、Ding、文檔等多端 (多設(shè)備) 數(shù)據(jù)同步。
圖1:DTIM架構(gòu)圖 ▼

上圖展示了 DTIM 系統(tǒng)架構(gòu),接下來(lái)詳細(xì)介紹消息收發(fā)鏈路。
4.2、消息收發(fā)鏈路
圖2:DTIM消息處理架構(gòu) ▼

1)消息發(fā)送:消息發(fā)送接口由 Receiver 提供,釘釘統(tǒng)一接入層將用戶從客戶端發(fā)送的消息請(qǐng)求轉(zhuǎn)發(fā)到 Receiver 模塊,Receiver 校驗(yàn)消息的合法性(文字圖片等安全審核、群禁言功能是否開啟或者是否觸發(fā)會(huì)話消息限流規(guī)則等)以及成員關(guān)系的有效性(單聊校驗(yàn)二者聊天、群聊校驗(yàn)發(fā)送者在群聊成員列表中),校驗(yàn)通過(guò)后為該消息生成一個(gè)全局唯一的 MessageId 隨消息體以及接收者列表打包成消息數(shù)據(jù)包投遞給異步隊(duì)列,由下游 Processor 處理。消息投遞成功之后,Receiver 返回消息發(fā)送成功的回執(zhí)給客戶端。
2)消息處理 :Processor 消費(fèi)到 IM 發(fā)送事件首先做按接收者的地域分布(DTIM 支持跨域部署, Geography,Geo)做消息事件分流,將本域用戶的消息做本地存儲(chǔ)入庫(kù)(消息體、接收者維度、已讀狀態(tài)、個(gè)人會(huì)話列表紅點(diǎn)更新),最后將消息體以及本域接收者列表打包為 IM 同步事件通過(guò)異步隊(duì)列轉(zhuǎn)發(fā)給同步服務(wù)。
3)消息接收 :同步服務(wù)按接收者維度寫入各自的同步隊(duì)列,同時(shí)查取當(dāng)前用戶設(shè)備在線狀態(tài),當(dāng)用戶在線時(shí)撈取隊(duì)列中未同步的消息,通過(guò)接入層長(zhǎng)連接推送到各端。當(dāng)用戶離線時(shí),打包消息數(shù)據(jù)以及離線用戶狀態(tài)列表為 IM 通知事件,轉(zhuǎn)發(fā)給通知服務(wù)的 PNS 模塊,PNS 查詢離線設(shè)備做三方廠商通道推送,至此一條消息的推送流程結(jié)束。
4.3、存儲(chǔ)模型設(shè)計(jì)
了解 IM 服務(wù)最快的途徑就是掌握它的存儲(chǔ)模型。
業(yè)界主流 IM 服務(wù)對(duì)于消息、會(huì)話、會(huì)話與消息的組織關(guān)系雖然不盡相同,但是歸納起來(lái)主要是兩種形式:寫擴(kuò)散讀聚合、讀擴(kuò)散寫聚合。
所謂讀寫擴(kuò)散其實(shí)是定義消息在群組會(huì)話中的存儲(chǔ)形式。如下圖所示。
圖3:讀模式和寫模式 ▼

如上圖所示:
1)讀擴(kuò)散的場(chǎng)景:消息歸屬于會(huì)話,對(duì)應(yīng)到存儲(chǔ)中相當(dāng)于有張 conversation_message 的表存儲(chǔ)著該會(huì)話產(chǎn)生的所有消息 (cid->msgid->message,cid 會(huì)話 ID、msgid 消息 ID、message 消息),這樣實(shí)現(xiàn)的好處是消息入庫(kù)效率高,只存儲(chǔ)會(huì)話與消息的綁定關(guān)系即可;
2)寫擴(kuò)散的場(chǎng)景:會(huì)話產(chǎn)生的消息投遞到類似于個(gè)人郵件的收件箱,即 message_inbox 表,存儲(chǔ)個(gè)人的所有消息(uid->msgid->message, uid 用戶 ID、msgid 消息 ID、message 消息),基于這種實(shí)現(xiàn),會(huì)話中的每條消息面向不同的接收者可以呈現(xiàn)出不同狀態(tài)。
DTIM 對(duì) IM 消息的及時(shí)性、前后端存儲(chǔ)狀態(tài)一致性要求異常嚴(yán)格,特別對(duì)于歷史消息漫游的訴求十分強(qiáng)烈,當(dāng)前業(yè)界 IM 產(chǎn)品對(duì)于消息長(zhǎng)時(shí)間存儲(chǔ)和客戶端歷史消息多端漫游都做得不盡如人意,主要是存儲(chǔ)成本過(guò)高。因此在產(chǎn)品體驗(yàn)與投入成本之間需要找到一個(gè)平衡點(diǎn)。
采用讀擴(kuò)散:在個(gè)性化的消息擴(kuò)展及實(shí)現(xiàn)層面有很大的約束。
采用寫擴(kuò)散帶來(lái)的問(wèn)題也很明顯:一個(gè)群成員為 N 的會(huì)話一旦產(chǎn)生消息就會(huì)擴(kuò)散 N 條消息記錄,如果在消息發(fā)送和擴(kuò)散量較少的場(chǎng)景,這樣的實(shí)現(xiàn)相比于讀擴(kuò)散落地更為簡(jiǎn)單,存儲(chǔ)成本也不是問(wèn)題。但是 DTIM 會(huì)話活躍度超高,一條消息的平均擴(kuò)散比可以達(dá)到 1:30,超大群又是企業(yè) IM 最核心的溝通場(chǎng)景,如果采用完全寫擴(kuò)散所帶來(lái)存儲(chǔ)成本問(wèn)題勢(shì)必制約釘釘業(yè)務(wù)發(fā)展。
所以,在 DTIM 的存儲(chǔ)實(shí)現(xiàn)上,釘釘采取了混合的方案,將讀擴(kuò)散和寫擴(kuò)散針對(duì)不同場(chǎng)景做了適配,最終在用戶視角系統(tǒng)會(huì)做統(tǒng)一合并,如下圖所示。
圖4:DTIM 讀寫混合模式 ▼

作為讀擴(kuò)散、寫擴(kuò)散方案的混合形式存在,用戶維度的消息分別從?conversation_message?和?message_inbox?表中獲取,在應(yīng)用側(cè)按消息發(fā)送時(shí)間做排序合并,conversation_message?表中記錄了該會(huì)話面向所有群成員接收的普通消息 N(Normal),而?message_inbox?表在以下兩種場(chǎng)景下被寫入:
1)第一種是:定向消息 P(Private,私有消息),群會(huì)話中發(fā)送的消息指定了接收者范圍,那么會(huì)直接寫入到接收者的 message_inbox 表中,比如紅包的領(lǐng)取狀態(tài)消息只能被紅包發(fā)送者可見,那么這種消息即被定義為定向消息。
2)第二種是:歸屬于會(huì)話消息的狀態(tài)轉(zhuǎn)換 NP(Normal to Private,普通消息轉(zhuǎn)私有消息),當(dāng)會(huì)話消息通過(guò)某種行為使得某些消息接收者的消息狀態(tài)發(fā)生轉(zhuǎn)換時(shí),該狀態(tài)會(huì)寫入到?message_inbox?表中,比如用戶在接收側(cè)刪除了消息,那么消息的刪除狀態(tài)會(huì)寫入到?message_inbox?中,在用戶拉取時(shí)會(huì)通過(guò)?message_inbox?的刪除狀態(tài)將?conversation_message?中的消息做覆蓋,最終用戶拉取不到自己已刪除的消息。
當(dāng)用戶在客戶端發(fā)起某個(gè)會(huì)話的歷史消息漫游請(qǐng)求時(shí),服務(wù)端根據(jù)用戶上傳的消息截止時(shí)間(message_create_time)分別從?conversation_message?表和?message_inbox?表拉取消息列表,在應(yīng)用層做狀態(tài)的合并,最終返回給用戶合并之后的數(shù)據(jù),N、P、NP 三種類型消息在消息個(gè)性化處理和存儲(chǔ)成本之間取得了很好的平衡。
4.4、同步模型設(shè)計(jì)
4.4.1 推送模型
用戶在會(huì)話中發(fā)出的消息和消息狀態(tài)變更等事件是如何同步到端上呢?
業(yè)界關(guān)于消息的同步模型的實(shí)現(xiàn)方案大致有三種:
1)客戶端拉取方案;
2)服務(wù)端推送方案;
3)服務(wù)端推送位點(diǎn)之后客戶端拉取的推拉結(jié)合方案。
三種方案各有優(yōu)劣,在此簡(jiǎn)短總結(jié):
1)首先:客戶端拉取方案的優(yōu)點(diǎn)是該方案實(shí)施簡(jiǎn)單、研發(fā)成本低,是傳統(tǒng)的 B/S 架構(gòu)。劣勢(shì)是效率低下,拉取間隔控制權(quán)在客戶端,對(duì)于 IM 這種實(shí)時(shí)的場(chǎng)景,很難設(shè)置一個(gè)有效的拉取間隔,間隔太短對(duì)服務(wù)端壓力大,間隔太長(zhǎng)時(shí)效性差;
2)其次:服務(wù)端主動(dòng)推送方案的優(yōu)點(diǎn)是低延遲、能做到實(shí)時(shí),最重要的主動(dòng)權(quán)在服務(wù)端。劣勢(shì)相對(duì)拉取方案,如何協(xié)調(diào)服務(wù)端和客戶端的處理能力存在問(wèn)題;
3)最后:推拉結(jié)合這個(gè)方案整合了拉和推的優(yōu)點(diǎn),但是方案更復(fù)雜,同時(shí)會(huì)比推的方案多一次 RTT,特別是在移動(dòng)網(wǎng)絡(luò)的場(chǎng)景下,不得不面臨功耗和推送成功率的問(wèn)題。
DTIM 相對(duì)傳統(tǒng) toC 的場(chǎng)景,有較明顯的區(qū)別:
1)第一是對(duì)實(shí)時(shí)性的要求:在企業(yè)服務(wù)中,比如員工聊天消息、各種系統(tǒng)報(bào)警,又比如音視頻中的共享畫板,無(wú)不要求實(shí)時(shí)事件同步,因此需要一種低延時(shí)的同步方案。
2)第二是弱網(wǎng)接入的能力:在 DTIM 服務(wù)的對(duì)象中,上千萬(wàn)的企業(yè)組織涉及各行各業(yè),從大城市 5G 的高速到偏遠(yuǎn)的山區(qū)弱網(wǎng),都需要 DTIM 的消息能發(fā)送、能觸達(dá)。對(duì)于復(fù)雜的網(wǎng)絡(luò)環(huán)境,需要服務(wù)端能判斷接入環(huán)境,并依據(jù)不同的環(huán)境條件調(diào)整同步數(shù)據(jù)的策略。
3)第三是功耗可控成本可控:在 DTIM 的企業(yè)場(chǎng)景中,消息收發(fā)頻率比傳統(tǒng)的 IM 多出一個(gè)數(shù)量級(jí),在這么大的消息收發(fā)場(chǎng)景怎么保障 DTIM 的功耗可控,特別是移動(dòng)端的功耗可控,是 DTIM 必須面對(duì)的問(wèn)題。在這種要求下,就需要 DTIM 盡量降低 IO 次數(shù),并基于不同的消息優(yōu)先級(jí)進(jìn)行合并同步,既能要保障實(shí)時(shí)性不被破壞,又要做到低功耗。
從以上三點(diǎn)可知,服務(wù)端主動(dòng)推送的模型更適合 DTIM 場(chǎng)景:
1)首先可以做到極低的延時(shí),保障推送耗時(shí)在毫秒級(jí)別;
2)其次是服務(wù)端能通過(guò)用戶接入信息判斷用戶接入環(huán)境好壞,進(jìn)行對(duì)應(yīng)的分包優(yōu)化,保障弱網(wǎng)鏈路下的成功率;
3)最后是主動(dòng)推送相對(duì)于推拉結(jié)合來(lái)說(shuō),可以降低一次 IO,對(duì) DTIM 這種每分鐘過(guò)億消息服務(wù)來(lái)說(shuō),能極大的降低設(shè)備功耗,同時(shí)配合消息優(yōu)先級(jí)合并包的優(yōu)化,進(jìn)一步降低端的功耗。
雖說(shuō)主動(dòng)推送有諸多優(yōu)勢(shì),但是客戶端會(huì)離線,甚至客戶端處理速度無(wú)法跟上服務(wù)端的速度,必然導(dǎo)致消息堆積。
DTIM 為了協(xié)調(diào)服務(wù)端和客戶端處理能力不一致的問(wèn)題,支持 Rebase 的能力,當(dāng)服務(wù)端消息堆積的條數(shù)達(dá)到一定閾值時(shí)觸發(fā) Rebase,客戶端會(huì)從 DTIM 拉取最新的消息,同時(shí)服務(wù)端跳過(guò)這部分消息從最新的位點(diǎn)開始推送消息。DTIM 稱這個(gè)同步模型為推優(yōu)先模型(Preferentially-Push Model,PPM)。
在基于 PPM 的推送方案下,為了保障消息的可靠達(dá)到,DTIM 還做一系列優(yōu)化。
這些優(yōu)化具體是:
1)支持消息可重入:服務(wù)端可能會(huì)針對(duì)某條消息做重復(fù)推送,客戶端需要根據(jù) msgId 做去重處理,避免端上消息重復(fù)展示。
2)支持消息排序:服務(wù)端推送消息特別是群比較活躍的場(chǎng)景,某條消息由于推送鏈路或者端側(cè)網(wǎng)絡(luò)抖動(dòng),推送失敗,而新的消息正常推送到端側(cè),如果端上不做消息排序的話,消息列表就會(huì)發(fā)生亂序,所以服務(wù)端會(huì)為每條消息分配一個(gè)時(shí)間戳,客戶端每次進(jìn)入消息列表就是根據(jù)時(shí)間戳做排序再投遞給 UI 層做消息展示。
3)支持缺失數(shù)據(jù)回補(bǔ):在某個(gè)極端情況下客戶端群消息事件比群創(chuàng)建事件更早到達(dá)端上,此時(shí)端上沒(méi)有群的基本信息消息也就無(wú)法展現(xiàn),所以需要客戶端主動(dòng)向服務(wù)端拉取群信息同步到本地,再做消息的透出。
4.4.2 多端數(shù)據(jù)一致性
多端數(shù)據(jù)一致性問(wèn)題一直是多端同步最核心的問(wèn)題,單個(gè)用戶可以同時(shí)在 PC、Pad 以及 Mobile 登錄,消息、會(huì)話紅點(diǎn)等狀態(tài)需要在多端保持一致,并且用戶更換設(shè)備情況下消息可以做全量的回溯。
基于上面的業(yè)務(wù)訴求以及系統(tǒng)層面面臨的諸多挑戰(zhàn),釘釘自研了同步服務(wù)來(lái)解決一致性問(wèn)題。
釘釘?shù)耐椒?wù)的設(shè)計(jì)理念和原則如下:
1)統(tǒng)一消息模型抽象,對(duì)于 DTIM 服務(wù)產(chǎn)生的新消息以及已讀事件、會(huì)話增刪改、多端紅點(diǎn)清除等事件統(tǒng)一抽象為同步服務(wù)的事件;
2)同步服務(wù)不感知事件的類型以及數(shù)據(jù)序列化方式。同步服務(wù)為每個(gè)用戶的事件分配一個(gè)自增的 ID(注:這里非連續(xù)遞增),確保消息可以根據(jù) ID 做遍歷的有序查詢;
3)統(tǒng)一同步隊(duì)列,同步服務(wù)為每個(gè)用戶分配了一個(gè) FIFO 的隊(duì)列存儲(chǔ),自增 id 和事件串行寫入隊(duì)列;當(dāng)有事件推送時(shí),服務(wù)端根據(jù)用戶當(dāng)前各端在線設(shè)備狀態(tài)做增量變更,將增量事件推送到各端;
4)根據(jù)設(shè)備和網(wǎng)絡(luò)質(zhì)量的不同可以做多種分包推送策略,確保消息可以有序、可靠、高效的發(fā)送給客戶端。
上面介紹了 DTIM 的存儲(chǔ)模型以及同步模型的設(shè)計(jì)與思考:
1)在存儲(chǔ)優(yōu)化中,存儲(chǔ)會(huì)基于 DTIM 消息特點(diǎn)進(jìn)行深度優(yōu)化,并會(huì)對(duì)其中原理以及實(shí)現(xiàn)細(xì)節(jié)做深入分析與介紹;
2)在同步機(jī)制中,會(huì)進(jìn)一步介紹多端同步機(jī)制是如何保障消息必達(dá)以及各端消息一致性。
五、消息存儲(chǔ)設(shè)計(jì)
DTIM 底層使用了表格存儲(chǔ)作為消息系統(tǒng)的核心存儲(chǔ)系統(tǒng),表格存儲(chǔ)是一個(gè)典型 LSM 存儲(chǔ)架構(gòu),讀寫放大是此類系統(tǒng)的典型問(wèn)題。
LSM 系統(tǒng)通過(guò) Major Compaction 來(lái)降低數(shù)據(jù)的 Level 高度,降低讀取數(shù)據(jù)放大的影響。在 DTIM 的場(chǎng)景中,實(shí)時(shí)消息寫入超過(guò)百萬(wàn) TPS,系統(tǒng)需要?jiǎng)潥w非常多的計(jì)算資源用于 Compaction 處理,但是在線消息讀取延遲毛刺依舊嚴(yán)重。
在存儲(chǔ)的性能分析中,我們發(fā)現(xiàn)如下幾個(gè)特點(diǎn):
1)6% 的用戶貢獻(xiàn)了 50% 左右的消息量,20% 的用戶貢獻(xiàn)了 74% 的消息量。高頻用戶產(chǎn)生的消息遠(yuǎn)多于低頻用戶,在 Flush MemTable 時(shí),高頻用戶消息占據(jù)了文件的絕大部分;
2)對(duì)于高頻的用戶,由于其“高頻”的原因,當(dāng)消息進(jìn)入 DTIM,系統(tǒng)發(fā)現(xiàn)用戶設(shè)備在線(高概率在線),會(huì)立即推送消息,因此需要推送的消息大部分在內(nèi)存的 MemTable 中;
3)高頻用戶產(chǎn)生大量的消息,Compaction 耗費(fèi)了系統(tǒng)大量的計(jì)算和 IO 資源;
4)低頻的用戶消息通常散落在多個(gè)文件當(dāng)中。
從上面的四個(gè)表現(xiàn)來(lái)看,我們能得出如下結(jié)論:
1)絕大部分 Compaction 是無(wú)效的計(jì)算和 IO,由于大量消息寫入產(chǎn)生大量的文件,但是高頻的用戶消息其實(shí)已經(jīng)下推給用戶的設(shè)備,Compaction 對(duì)讀加速效果大打折扣。反而會(huì)搶占計(jì)算資源,同時(shí)引起 IO 抖動(dòng);
2)低頻用戶由于入庫(kù)消息頻率低,在 Flush 之后的文件中占比低;同時(shí)用戶上線頻率低,期間會(huì)累計(jì)較多的待接收的消息,那么當(dāng)用戶上線時(shí),連續(xù)的歷史消息高概率散落在多個(gè)文件當(dāng)中,導(dǎo)致遍歷讀取消息毛刺,嚴(yán)重的有可能讀取超時(shí)。
為了解決此類問(wèn)題,我們采用分而治之方法,將高頻用戶和低頻用戶的消息區(qū)別對(duì)待。
我們借鑒了?WiscKey KV?分離技術(shù)的思想,就是將到達(dá)一定閾值的 Value 分離出來(lái),降低這類消息在文件中的占比進(jìn)而有效的降低寫放大的問(wèn)題。
附:WiscKey KV原始論文附件下載:
?WiscKey:Separating Keys from Values in SSD-conscious Storage(52im.net).pdf?(2.24 MB)
但是 WiscKey KV 分離僅考慮單 Key 的情況,在 DITM 的場(chǎng)景下,Key 之間的大小差距不大,直接采用這種 KV 分離技術(shù)并不能解決以上問(wèn)題。因此我們?cè)谠?KV 分離的基礎(chǔ)上,改進(jìn)了 KV 分離,將相同前綴的多個(gè) Key 聚合判斷,如果多個(gè) Key 的 Value 超過(guò)閾值,那么將這些 Key 的 Value 打包了 value-block 分離出去,寫入到 value 文件。
數(shù)據(jù)顯示:上述方法能夠在 Minor Compaction 階段將 MemTable 中 70% 的消息放入 value 文件,大幅降低 key 文件大小,帶來(lái)更低的寫放大;同時(shí),Major Compaction 可以更快降低 key 文件數(shù),使讀放大更低。高頻用戶發(fā)送消息更多,因此其數(shù)據(jù)行更容易被分離到 value 文件。讀取時(shí),高頻用戶一般把最近消息全部讀出來(lái),考慮到 DTIM 消息 ID 是遞增生成,消息讀取的 key 是連續(xù)的,同一個(gè) value-block 內(nèi)部的數(shù)據(jù)會(huì)被順序讀取,基于此,通過(guò) IO 預(yù)取技術(shù)提前讀取 value-block,可以進(jìn)一步提高性能。對(duì)于低頻用戶,其發(fā)送消息較少,K-V 分離不生效,從而減少讀取時(shí)候 value 文件帶來(lái)的額外 IO。
圖5:KV分離和預(yù)讀取 ▼

六、多端同步機(jī)制設(shè)計(jì)
6.1、概述
DTIM 面向辦公場(chǎng)景,和面向普通用戶的產(chǎn)品在服務(wù)端到客戶端的數(shù)據(jù)同步上最大的區(qū)別是消息量巨大、變更事件復(fù)雜、對(duì)多端同步有著強(qiáng)烈的訴求。
DTIM 基于同步服務(wù)構(gòu)建了一套完整同步流程。同步服務(wù)是一個(gè)服務(wù)端到客戶端的數(shù)據(jù)同步服務(wù),是一套統(tǒng)一的數(shù)據(jù)下行平臺(tái),支撐釘釘多個(gè)應(yīng)用服務(wù)。
圖6:微服務(wù)架構(gòu) ▼

同步服務(wù)是一套多端的數(shù)據(jù)同步服務(wù),由兩部分組成:
1)部署于服務(wù)端的同步服務(wù);
2)由客戶端 APP 集成的同步服務(wù) SDK。
工作原理類似于MQ消息隊(duì)列:
1)用戶 ID 近似消息隊(duì)列中的 Topic;
2)用戶設(shè)備近似消息隊(duì)列中的 Consumer Group。
每個(gè)用戶設(shè)備作為一個(gè)消息隊(duì)列消費(fèi)者能夠按需獲得這個(gè)用戶一份數(shù)據(jù)拷貝,從而實(shí)現(xiàn)了多端同步訴求。
當(dāng)業(yè)務(wù)需要同步一個(gè)變更數(shù)據(jù)到指定的用戶或設(shè)備時(shí):業(yè)務(wù)調(diào)用數(shù)據(jù)同步接口,服務(wù)端會(huì)將業(yè)務(wù)需要同步的數(shù)據(jù)持久化到存儲(chǔ)系統(tǒng)中,然后當(dāng)客戶端在線的時(shí)候把數(shù)據(jù)推送給客戶端。每一條數(shù)據(jù)入庫(kù)時(shí)都會(huì)原子的生成一個(gè)按用戶維度單調(diào)遞增的位點(diǎn),服務(wù)端會(huì)按照位點(diǎn)從小到大的順序把每一條數(shù)據(jù)都推送至客戶端。
客戶端應(yīng)答接收成功后:更新推送數(shù)據(jù)最大的位點(diǎn)到位點(diǎn)管理存儲(chǔ)中,下次推送從這個(gè)新的位點(diǎn)開始推送。同步服務(wù) SDK 內(nèi)部負(fù)責(zé)接收同步服務(wù)數(shù)據(jù),接收后寫入本地?cái)?shù)據(jù)庫(kù),然后再把數(shù)據(jù)異步分發(fā)到客戶端業(yè)務(wù)模塊,業(yè)務(wù)模塊處理成功后刪除本地存儲(chǔ)對(duì)應(yīng)的內(nèi)容。
在上文章節(jié)中,已經(jīng)初步介紹同步服務(wù)推送模型和多端一致性的考慮,本章主要是介紹 DTIM 是如何做存儲(chǔ)設(shè)計(jì)、在多端同步如何實(shí)現(xiàn)數(shù)據(jù)一致性、最后再介紹服務(wù)端消息數(shù)據(jù)堆積技術(shù)方案 Rebase。
* 推薦閱讀:如果您對(duì)消息同步機(jī)制沒(méi)有概念,可以先行閱讀《淺談移動(dòng)端IM的多點(diǎn)登陸和消息漫游原理》。
6.2、全量消息存儲(chǔ)邏輯
在同步服務(wù)中,采用以用戶為中心,將所有要推送給此用戶的消息匯聚在一起,并為每個(gè)消息分配唯一且遞增的 PTS(即位點(diǎn),英文術(shù)語(yǔ)Point To Sequence),服務(wù)端保存每個(gè)設(shè)備推送的位點(diǎn)。
通過(guò)兩個(gè)用戶 Bob 和 Alice,來(lái)實(shí)際展示消息在存儲(chǔ)系統(tǒng)中存儲(chǔ)的邏輯形態(tài)。例如:Bob 給 Alice 發(fā)送了一個(gè)消息”Hi! Alice“,Alice 回復(fù)了 Bob 消息”Hi! Bob“。
當(dāng) Bob 發(fā)送第一條消息給 Alice 時(shí),接收方分別是 Bob 和 Alice,系統(tǒng)會(huì)在 Bob 和 Alice 的存儲(chǔ)區(qū)域末尾分別添加一條消息,存儲(chǔ)系統(tǒng)在入庫(kù)成功時(shí),會(huì)分別為這兩行分配一個(gè)唯一且遞增的位點(diǎn)(Bob 的位點(diǎn)是 10005,Alice 的位點(diǎn)是 23001);入庫(kù)成功之后,觸發(fā)推送。比如 Bob 的 PC 端上一次下推的位點(diǎn)是 10000,Alice 移動(dòng)端的推送位點(diǎn)是 23000,在推送流程發(fā)起之后,會(huì)有兩個(gè)推送任務(wù),第一是 Bob 的推送任務(wù),推送任務(wù)從上一次位點(diǎn)(10000) + 1 開始查詢數(shù)據(jù),將獲取到 10005 位置的”Hi“消息,將此消息推送給 Bob 的設(shè)備,推送成功之后,存儲(chǔ)推送位點(diǎn)(10005)。Alice 推送流程也是同理。Alice 收到 Bob 消息之后,Alice 回復(fù) Bob,類似上面的流程,入庫(kù)成功并分配位點(diǎn)(Bob 的位點(diǎn)是 10009,Alice 的位點(diǎn)是 23003)。
圖7:同步服務(wù)的存儲(chǔ)設(shè)計(jì) ▼

6.3、消息多端同步邏輯
多端同步是 DTIM 的典型特點(diǎn),如何保持多端的數(shù)據(jù)及時(shí)觸達(dá)和解決一致性是 DTIM 同步服務(wù)最大的挑戰(zhàn)。
上文中已經(jīng)介紹了同步服務(wù)的事件存儲(chǔ)模型,將需要推送的消息按照用戶聚合。當(dāng)用戶有多個(gè)設(shè)備時(shí),將設(shè)備的位點(diǎn)保存在位點(diǎn)管理系統(tǒng)之中,Key 是用戶 + 設(shè)備 ID,Value 是上一次推送的位點(diǎn)。如果是設(shè)備第一次登錄,位點(diǎn)默認(rèn)為 0。
由此可知:每個(gè)設(shè)備都有單獨(dú)的位點(diǎn),數(shù)據(jù)在服務(wù)端只有一份按照用戶維度的數(shù)據(jù),推送到客戶端的消息是服務(wù)端對(duì)應(yīng)位點(diǎn)下的快照,從而保障了每個(gè)端的數(shù)據(jù)都是一致的。
比如:此時(shí) Bob 登錄了手機(jī)(該設(shè)備之前登錄過(guò)釘釘),同步服務(wù)會(huì)獲取到設(shè)備登錄的事件,事件中有此設(shè)備上次接收數(shù)據(jù)的位點(diǎn)(比如 10000),同步服務(wù)會(huì)從 10000 + 1(位點(diǎn))開始查詢數(shù)據(jù),獲取到五條消息(10005~10017),將消息推送給此臺(tái)手機(jī)并更新服務(wù)端位點(diǎn)。此時(shí),Bob 手機(jī)和 PC 上的消息一致,當(dāng) Alice 再次發(fā)送消息時(shí),同步服務(wù)會(huì)給 Bob 的兩臺(tái)設(shè)備推送消息,始終保持 Bob 兩個(gè)設(shè)備之間消息數(shù)據(jù)的一致性。
6.4、大量需同步離線消息的優(yōu)化邏輯
正如上文所述:我們采用了推優(yōu)先的模型下推數(shù)據(jù)以保障事件的實(shí)時(shí)性,采用位點(diǎn)管理實(shí)現(xiàn)多端同步,但是實(shí)際情況卻遠(yuǎn)比上面的情況復(fù)雜。
最常見的問(wèn)題:就是設(shè)備離線重新登錄,期間該用戶可能會(huì)累計(jì)大量未接收的消息數(shù)據(jù),比如幾萬(wàn)條。如果按照上面的方案,服務(wù)端在短時(shí)間會(huì)給客戶端推送大量的消息,客戶端 CPU 資源極有可能耗盡導(dǎo)致整個(gè)設(shè)備假死。
其實(shí)對(duì)于 IM 這種場(chǎng)景來(lái)說(shuō):幾天甚至幾小時(shí)之前的數(shù)據(jù),再推送給用戶已經(jīng)喪失即時(shí)消息的意義,反而會(huì)消耗客戶移動(dòng)設(shè)備的電量,得不償失。又或者節(jié)假日大群中各種活動(dòng),都會(huì)有大量的消息產(chǎn)生。
對(duì)于以上情況:同步服務(wù)提供 Rebase 的方案,當(dāng)要推送的消息累計(jì)到一定閾值時(shí),同步服務(wù)會(huì)向客戶端發(fā)送 Rebase 事件,客戶端收到事件之后,會(huì)從消息服務(wù)中獲取到最新的消息(Lastmsg)。這樣可以跳過(guò)中間大量的消息,當(dāng)用戶需要查看歷史消息,可以基于 Lastmsg 向上回溯,即省電也能提升用戶體驗(yàn)。
還是以 Bob 為例:Bob 登錄了 Pad 設(shè)備(一臺(tái)全新的設(shè)備),同步服務(wù)收到 Pad 登錄的事件,發(fā)現(xiàn)登錄的位點(diǎn)為 0,查詢從 0 開始到當(dāng)前,已經(jīng)累計(jì) 1 萬(wàn)條消息,累計(jì)量大于同步服務(wù)的閾值,同步服務(wù)發(fā)送 Rebase 事件給客戶端,客戶端從消息服務(wù)中獲取到最新的一條消息“Tks !!!”,同時(shí)客戶端從同步服務(wù)中獲取最新的位點(diǎn)為 10017,并告訴同步服務(wù)后續(xù)從 10017 這個(gè)位置之后開始推送。當(dāng) Bob 進(jìn)入到和 Alice 的會(huì)話之后,客戶端只要從 Lastmsg 向上回溯幾條歷史消息填滿聊天框即可。
七、高可用設(shè)計(jì)
7.1、概述
DTIM 對(duì)外提供 99.995% 的可用性 SLA,有上百萬(wàn)的組織將釘釘作為自身數(shù)字化辦公的基礎(chǔ)設(shè)施,由于其極廣的覆蓋面,DTIM 些許抖動(dòng)都會(huì)影響大量企業(yè)、機(jī)構(gòu)、學(xué)校等組織,進(jìn)而可能形成社會(huì)性事件。因此,DTIM 面臨著極大的穩(wěn)定性挑戰(zhàn)。
高可用是 DTIM 的核心能力。對(duì)于高可用,需要分兩個(gè)維度來(lái)看,首先是服務(wù)自我防護(hù),在遇到流量洪峰、黑客攻擊、業(yè)務(wù)異常時(shí),要有流量管控、容錯(cuò)等能力,保障服務(wù)在極端流量場(chǎng)景下還有基本服務(wù)的能力。其次是服務(wù)擴(kuò)展能力,比如常見的計(jì)算資源的擴(kuò)展、存儲(chǔ)資源的擴(kuò)展等,資源伴隨流量增長(zhǎng)和縮減,提供優(yōu)質(zhì)的服務(wù)能力并與成本取得較好的平衡。
7.2、自我防護(hù)
7.2.1 背景
DTIM 經(jīng)常會(huì)面臨各種突發(fā)大流量,比如萬(wàn)人大群紅包大戰(zhàn)、早高峰打卡提醒、春節(jié)除夕紅包等等都會(huì)在短時(shí)間內(nèi)產(chǎn)生大量的聊天消息,給系統(tǒng)帶來(lái)很大的沖擊,基于此我們采用了多種措施。
首先是流量管控,限流是保護(hù)系統(tǒng)最簡(jiǎn)單有效的方式。DTIM 服務(wù)通過(guò)各種維度的限流來(lái)保護(hù)自身以及下游,最重要的是保護(hù)下游的存儲(chǔ)。在分布式系統(tǒng)中存儲(chǔ)都是分片的,最容易出現(xiàn)的是單個(gè)分片的熱點(diǎn)問(wèn)題,DTIM 里面有兩個(gè)維度的數(shù)據(jù):用戶、會(huì)話 (消息屬于會(huì)話), 分片也是這兩個(gè)維度,所以限流采用了會(huì)話、用戶維度的限流,這樣既可以保護(hù)下游存儲(chǔ)單個(gè)分區(qū),又可以一定程度上限制整體的流量。要控制系統(tǒng)的整體流量, 前面兩個(gè)維度還不夠,DTIM 還采用了客戶端類型、應(yīng)用 (服務(wù)端 IM 上游業(yè)務(wù)) 兩個(gè)維度的限流,來(lái)避免整體的流量上漲對(duì)系統(tǒng)帶來(lái)的沖擊。
其次是削峰平谷。限流簡(jiǎn)單有效,但是對(duì)用戶的影響比較大。在 DTIM 服務(wù)中有很多消息對(duì)于實(shí)時(shí)性要求不高,比如運(yùn)營(yíng)推送等。對(duì)于這些場(chǎng)景中的消息可以充分利用消息系統(tǒng)異步性的特點(diǎn),使用異步消息隊(duì)列進(jìn)行削峰平谷,這樣一方面減少了對(duì)用戶的影響,另一方面也減輕對(duì)下游系統(tǒng)的瞬時(shí)壓力。DTIM 可以根據(jù)業(yè)務(wù)類型 (比如運(yùn)營(yíng)推送)、消息類型 (比如點(diǎn)贊消息) 等多種維度對(duì)消息進(jìn)行分級(jí),對(duì)于低優(yōu)先級(jí)的消息保證在一定時(shí)間 (比如 1 個(gè)小時(shí)) 內(nèi)處理完成。
最后是熱點(diǎn)優(yōu)化。DTIM 服務(wù)中面臨著各種熱點(diǎn)問(wèn)題,對(duì)于這些熱點(diǎn)問(wèn)題僅僅靠限流是不夠的。比如通過(guò)釘釘小秘書給大量用戶推送升級(jí)提醒,由于是一個(gè)賬號(hào)與大量賬號(hào)建立會(huì)話,因此會(huì)存在 conversation_inbox 的熱點(diǎn)問(wèn)題,如果通過(guò)限速來(lái)解決,會(huì)導(dǎo)致推送速度過(guò)慢、影響業(yè)務(wù)。對(duì)于這類問(wèn)題需要從架構(gòu)上來(lái)解決。
總的來(lái)說(shuō),主要是兩類問(wèn)題:大賬號(hào)和大群導(dǎo)致的熱點(diǎn)問(wèn)題。對(duì)于大賬戶問(wèn)題,由于 conversation_inbox 采用用戶維度做分區(qū),會(huì)導(dǎo)致系統(tǒng)賬號(hào)的請(qǐng)求都落到某個(gè)分區(qū),從而導(dǎo)致熱點(diǎn)。解決方案為做熱點(diǎn)拆分,既將 conversation_inbox 數(shù)據(jù)合并到 conversation_member 中 (按照會(huì)話做分區(qū)),將用戶維度的操作轉(zhuǎn)換為會(huì)話維度的操作,這樣就可以將系統(tǒng)賬號(hào)的請(qǐng)求打散到所有分區(qū)上, 從而實(shí)現(xiàn)消除熱點(diǎn)。對(duì)于大群?jiǎn)栴},壓力來(lái)自大量發(fā)消息、消息已讀和貼表情互動(dòng),大量的接收者帶來(lái)極大的擴(kuò)散量。所以我們針對(duì)以上三個(gè)場(chǎng)景,分而治之。
7.2.2 計(jì)算延遲與按需拉取
對(duì)于消息發(fā)送,一般的消息對(duì)于群里面所有人都是一樣的,所以可以采用讀擴(kuò)散的方式,即不管多大的群,發(fā)一條消息就存儲(chǔ)一份。另一方面,由于每個(gè)人在每個(gè)會(huì)話上都有紅點(diǎn)數(shù)和 Lastmsg, 一般情況下每次發(fā)消息都需要更新紅點(diǎn)和 Lastmsg,但是在大群場(chǎng)景下會(huì)存在大量擴(kuò)散,對(duì)系統(tǒng)帶來(lái)巨大的壓力。我們的解決方案為,對(duì)于大群的紅點(diǎn)和 Lastmsg,在發(fā)消息時(shí)不更新,在拉首屏?xí)r實(shí)時(shí)算,由于拉首屏是低頻操作且每個(gè)人只有一到兩個(gè)大群,實(shí)時(shí)計(jì)算壓力很小,這樣高峰期可以減少 99.99 % 的存儲(chǔ)操作, 從而解決了大群發(fā)消息對(duì) DTIM 帶來(lái)的沖擊。
7.2.3 請(qǐng)求合并
在大群發(fā)消息的場(chǎng)景中,如果用戶都在線,瞬時(shí)就會(huì)有大量已讀請(qǐng)求,如果每個(gè)已讀請(qǐng)求都處理,則會(huì)產(chǎn)生 M*N(M 消息條數(shù),N 群成員數(shù)) 的擴(kuò)散,這個(gè)擴(kuò)散是十分恐怖的。
DTIM 的解決方案是客戶端將一個(gè)會(huì)話中的多次已讀進(jìn)行合并,一次性發(fā)送給服務(wù)端,服務(wù)端對(duì)于每條消息的已讀請(qǐng)求進(jìn)行合并處理,比如 1 分鐘的所有請(qǐng)求合并為 1 次請(qǐng)求。在大群中,進(jìn)行消息點(diǎn)贊時(shí),短時(shí)間會(huì)對(duì)消息產(chǎn)生大量更新,再加上需要擴(kuò)散到群里面的所有人,系統(tǒng)整體的擴(kuò)散量十分巨大。我們發(fā)現(xiàn),對(duì)于消息多次更新的場(chǎng)景,可以將一段時(shí)間里面多次更新合并,可以大大減少擴(kuò)散量,從實(shí)際優(yōu)化之后的數(shù)據(jù)來(lái)看,高峰期系統(tǒng)的擴(kuò)散量同比減少 96%。
即使完全做到以上幾點(diǎn),也很難提供當(dāng)前承諾的 SLA,除了防止自身服務(wù)出現(xiàn)問(wèn)題以外,還必須實(shí)現(xiàn)對(duì)依賴組件的容災(zāi)。我們整體采用了冗余異構(gòu)存儲(chǔ)和異步隊(duì)列與 RPC 相結(jié)合的方案,當(dāng)任意一類 DTIM 依賴的產(chǎn)品出現(xiàn)問(wèn)題時(shí), DTIM 都能正常工作,由于篇幅問(wèn)題,此處不再展開。
7.3、水平擴(kuò)展能力
對(duì)于服務(wù)的彈性擴(kuò)展能力,也需要分兩個(gè)維度來(lái)看:
1)首先:服務(wù)內(nèi)部的彈性擴(kuò)展,比如計(jì)算資源的擴(kuò)展、存儲(chǔ)資源的擴(kuò)展等,是我們通常構(gòu)建彈性擴(kuò)展能力關(guān)注的重點(diǎn)方向;
2)其次:跨地域維度的擴(kuò)展,服務(wù)能根據(jù)自身需要,在其他區(qū)域擴(kuò)展一個(gè)服務(wù)集群,新的服務(wù)集群承接部分流量,在跨地域?qū)用嫘纬梢粋€(gè)邏輯統(tǒng)一的分布式服務(wù),這種分布式服務(wù)我們稱之為單元化。
7.3.1 彈性應(yīng)用架構(gòu)
對(duì)于 DTIM 的擴(kuò)展性,因?yàn)闃?gòu)建和生長(zhǎng)于云上,在彈性擴(kuò)展能力建設(shè)擁有了更多云的特點(diǎn)和選擇。
對(duì)于計(jì)算節(jié)點(diǎn):應(yīng)用具備橫向擴(kuò)展的能力,系統(tǒng)能在短時(shí)間之內(nèi)感知流量突增進(jìn)而進(jìn)行快速擴(kuò)容,對(duì)于上文提到的各種活動(dòng)引起的流量上漲,能做到輕松應(yīng)對(duì)。同時(shí),系統(tǒng)支持定時(shí)擴(kuò)容和縮容,在系統(tǒng)彈性能力和成本之間取得較好的平衡。
對(duì)于存儲(chǔ):DTIM 底層選擇了可以水平擴(kuò)展的 Serverless 存儲(chǔ)服務(wù),存儲(chǔ)服務(wù)內(nèi)部基于讀寫流量的大小進(jìn)行動(dòng)態(tài)調(diào)度,應(yīng)用上層完全無(wú)感知。對(duì)于服務(wù)自身的擴(kuò)展性,我們還實(shí)施了不可變基礎(chǔ)設(shè)施、應(yīng)用無(wú)狀態(tài)、去單點(diǎn)、松耦合、負(fù)載均衡等設(shè)計(jì),使 DTIM 構(gòu)建出了一套高效的彈性應(yīng)用架構(gòu)。
7.3.2 彈性地域級(jí)擴(kuò)展(單元化)
在應(yīng)用內(nèi)部實(shí)現(xiàn)了高效彈性之后,伴隨著業(yè)務(wù)流量的增長(zhǎng),單個(gè)地域已經(jīng)無(wú)法滿足 DTIM 億級(jí)別 DUA 的彈性擴(kuò)展的需求。由于 DTIM 特點(diǎn),所有用戶都可以在添加好友之后進(jìn)行聊天,這就意味著不能簡(jiǎn)單換個(gè)地域搭建一套孤島式的 DTIM。
為了解決這種規(guī)模下的彈性能力,我們基于云上的地域架構(gòu),在一個(gè) Geo 地域內(nèi),構(gòu)建了一套異地多活、邏輯上是一體的彈性架構(gòu),我們稱之為單元化。下圖是 DTIM 的單元化架構(gòu)。
圖8:DTIM單元化架構(gòu) ▼:

對(duì)于單元化的彈性擴(kuò)展架構(gòu),其中最核心的內(nèi)容是流量動(dòng)態(tài)調(diào)度、數(shù)據(jù)單地域的自封閉性和單元整體降級(jí)。
7.3.3 彈性動(dòng)態(tài)調(diào)度
流量路由決定了數(shù)據(jù)流向,我們可以依托這個(gè)能力,將大群流量調(diào)度到新的單元來(lái)承接急速增長(zhǎng)的業(yè)務(wù)流量,同時(shí)實(shí)現(xiàn)流量按照企業(yè)維度匯聚,提供就近部署能力,進(jìn)而提供優(yōu)質(zhì)的 RT 服務(wù)。
業(yè)界現(xiàn)在主流的單元化調(diào)度方案主要是基于用戶維度的靜態(tài)路由分段,這種方案算法簡(jiǎn)單可靠,但是很難實(shí)現(xiàn)動(dòng)態(tài)路由調(diào)度,一旦用戶路由固定,無(wú)法調(diào)整服務(wù)單元。
比如:在 DTIM 的場(chǎng)景中,企業(yè)(用戶)規(guī)模是隨著時(shí)間增長(zhǎng)、用戶業(yè)務(wù)規(guī)模增長(zhǎng)之后,單地域無(wú)法有效支撐多個(gè)大型企業(yè)用戶時(shí),傳統(tǒng)靜態(tài)方案很難將企業(yè)彈性擴(kuò)展到其他單元,強(qiáng)行遷移會(huì)付出極高的運(yùn)維代價(jià)。因此傳統(tǒng)的路由方案不具備彈性調(diào)度能力。
DTIM 提供一套全局一致性的高可用路由服務(wù)系統(tǒng) (RoutingService)。服務(wù)中存儲(chǔ)了用戶會(huì)話所在單元,消息服務(wù)基于路由服務(wù),將流量路由到不同的單元。應(yīng)用更新路由數(shù)據(jù)之后,隨之路由信息也發(fā)生變化。與此同時(shí),路由服務(wù)發(fā)起數(shù)據(jù)訂正事件,將會(huì)話的歷史消息數(shù)據(jù)進(jìn)行遷移,遷移完成之后正式切換路由。路由服務(wù)底層依賴存儲(chǔ)的 GlobalTable 能力,路由信息更新完成之后,保障跨地域的一致性。
7.3.4 彈性單元自封閉
數(shù)據(jù)的單元自封閉是將 DTIM 最重要且規(guī)模最大的數(shù)據(jù):“消息數(shù)據(jù)”的接收、處理、持久化、推送等過(guò)程封閉在當(dāng)前單元中,解除了對(duì)其他單元依賴,進(jìn)而能高效地?cái)U(kuò)充單元,實(shí)現(xiàn)跨地域級(jí)別高效彈性能力。
要做到業(yè)務(wù)數(shù)據(jù)在單元內(nèi)自封閉,最關(guān)鍵是要識(shí)別清楚要解決哪種數(shù)據(jù)的彈性擴(kuò)展能力。在 DTIM 的場(chǎng)景下,用戶 Profile、會(huì)話數(shù)據(jù)、消息數(shù)據(jù)都是 DTIM 最核心的資產(chǎn),其中消息數(shù)據(jù)的規(guī)模遠(yuǎn)超其他數(shù)據(jù),彈性擴(kuò)展能力也是圍繞消息數(shù)據(jù)的處理在建設(shè)。怎么將消息按照單元數(shù)據(jù)合理的劃分成為單元自封閉的關(guān)鍵維度。
在 IM 的場(chǎng)景中,消息數(shù)據(jù)來(lái)自于人與人之間的聊天,可以按照人去劃分?jǐn)?shù)據(jù),但是如果聊天的兩個(gè)人在不同的單元之間,消息數(shù)據(jù)必然要在兩個(gè)單元拷貝或者冗余,因此按照人劃分?jǐn)?shù)據(jù)并不是很好的維度。
DTIM 采用了會(huì)話維度劃分:因?yàn)槿撕蜁?huì)話都是元數(shù)據(jù),數(shù)據(jù)規(guī)模有限,消息數(shù)據(jù)近乎無(wú)限,消息歸屬于會(huì)話,會(huì)話與會(huì)話之間并無(wú)交集,消息處理時(shí)并沒(méi)有跨單元的調(diào)用。因此,將不同的會(huì)話拆分到不同的單元,保障了消息數(shù)據(jù)僅在一個(gè)單元處理和持久化,不會(huì)產(chǎn)生跨單元的請(qǐng)求調(diào)用,進(jìn)而實(shí)現(xiàn)了單元自封閉。
7.3.5 彈性單元降級(jí)
在單元化的架構(gòu)中,為了支持服務(wù)級(jí)別的橫向擴(kuò)展能力,多單元是基本形態(tài)。
單元的異常流量亦或者是服務(wù)版本維護(hù)的影響都會(huì)放大影響面,進(jìn)而影響 DTIM 整體服務(wù)。
因此:DTIM 重點(diǎn)打造了單元降級(jí)的能力,單一單元失去服務(wù)能力之后,DTIM 會(huì)將業(yè)務(wù)流量切換到新的單元,新消息會(huì)從正常的單元下推,釘釘客戶端在數(shù)據(jù)渲染時(shí)也不會(huì)受到故障單元的影響,做到了單元故障切換用戶無(wú)感知。
八、本文小結(jié)
本文通過(guò)模型設(shè)計(jì)、存儲(chǔ)優(yōu)化、同步機(jī)制以及高可用等維度,本文全方位地展示了當(dāng)代企業(yè)級(jí) IM 設(shè)計(jì)的核心。
本文也是對(duì) DTIM 過(guò)去一段時(shí)間的技術(shù)總結(jié),隨著用戶數(shù)的持續(xù)增長(zhǎng),DTIM 也在與時(shí)俱進(jìn)、持續(xù)迭代和優(yōu)化,比如支持條件索引進(jìn)而實(shí)現(xiàn)索引加速和成本可控、實(shí)現(xiàn)消息位點(diǎn)的連續(xù)累加、實(shí)現(xiàn)消息按需拉取和高效的完整性校驗(yàn)、提供多種上下行通道,進(jìn)一步提升弱網(wǎng)下的成功率和體驗(yàn)等。
九、相關(guān)文章
[1]?企業(yè)級(jí)IM王者——釘釘在后端架構(gòu)上的過(guò)人之處
[2]?現(xiàn)代IM系統(tǒng)中聊天消息的同步和存儲(chǔ)方案探討
[3]?釘釘——基于IM技術(shù)的新一代企業(yè)OA平臺(tái)的技術(shù)挑戰(zhàn)(視頻+PPT)》
[5]?一套億級(jí)用戶的IM架構(gòu)技術(shù)干貨(上篇):整體架構(gòu)、服務(wù)拆分等
[6]?一套億級(jí)用戶的IM架構(gòu)技術(shù)干貨(下篇):可靠性、有序性、弱網(wǎng)優(yōu)化等
[7]?從新手到專家:如何設(shè)計(jì)一套億級(jí)消息量的分布式IM系統(tǒng)
[8]?企業(yè)微信的IM架構(gòu)設(shè)計(jì)揭秘:消息模型、萬(wàn)人群、已讀回執(zhí)、消息撤回等
[9]?全面揭秘億級(jí)IM消息的可靠投遞機(jī)制
[10]?一套高可用、易伸縮、高并發(fā)的IM群聊、單聊架構(gòu)方案設(shè)計(jì)實(shí)踐
[11]?一套海量在線用戶的移動(dòng)端IM架構(gòu)設(shè)計(jì)實(shí)踐分享(含詳細(xì)圖文)
[12]?一套原創(chuàng)分布式即時(shí)通訊(IM)系統(tǒng)理論架構(gòu)方案
(本文已同步發(fā)布于:http://www.52im.net/thread-4012-1-1.html)