Jack Jiang

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

          關于MobileIMSDK

          MobileIMSDK 是一套專門為移動端開發的開源IM即時通訊框架,超輕量級、高度提煉,一套API優雅支持UDP 、TCP 、WebSocket 三種協議,支持iOS、Android、H5、標準Java平臺,服務端基于Netty編寫。

          工程開源地址是:

          關于RainbowChat

          ► 詳細產品介紹:http://www.52im.net/thread-19-1-1.html
          ► iOS端更新記錄:http://www.52im.net/thread-2735-1-1.html
          ► 全部運行截圖:iOS端全部運行截圖 (另:Android端運行截圖 點此查看
          ► 在線體驗下載:App Store安裝地址 (另:Android端下載體驗 點此查看

           

          RainbowChat是一套基于開源IM聊天框架 MobileIMSDK 的產品級移動端IM系統。RainbowChat源于真實運營的產品,解決了大量的屏幕適配、細節優化、機器兼容問題(可自行下載體驗:專業版下載安裝)。

          RainbowChat可能是市面上提供im即時通訊聊天源碼的,唯一一款同時支持TCP、UDP兩種通信協議的IM產品(通信層基于開源IM聊天框架 MobileIMSDK 實現)。

          v6.2 版更新內容

          此版更新內容更多歷史更新日志):

          • 1)[優化] 升級核心通信層庫 MobileIMSDK 至 v6.3;
          • 2)[優化] 提供了方便的配置用于開/關長連接的SSL/TLS加密傳輸。

          此版主要功能運行截圖更多截圖點此查看):

          posted @ 2023-03-01 12:05 Jack Jiang 閱讀(57) | 評論 (0)編輯 收藏

               摘要: 1、引言對于IM聊天應用來說,為了提升安全性,對聊天消息加密是常規操作。眾所周之,Netty是高性能的Java NIO網絡通信框架,因而用Netty來寫IM是再正常不過了。網上關于為Netty生成、以及使用SSL/TLS證書的文章有很多,但由于各種原因,生成的證書要么是Netty中無法讀取和使用,要么是代碼不全或不具體導致根本配不通SSL/TLS加密。正好這段時間專門為 MobileIM...  閱讀全文

          posted @ 2023-02-23 14:18 Jack Jiang 閱讀(86) | 評論 (0)編輯 收藏

          關于MobileIMSDK

          MobileIMSDK 是一套專門為移動端開發的開源IM即時通訊框架,超輕量級、高度提煉,一套API優雅支持UDP 、TCP 、WebSocket 三種協議,支持iOS、Android、H5、標準Java平臺,服務端基于Netty編寫。

          工程開源地址是:

          關于RainbowChat

          ► 詳細產品介紹:http://www.52im.net/thread-19-1-1.html
          ► 版本更新記錄:http://www.52im.net/thread-1217-1-1.html
          ► 全部運行截圖:Android端、iOS端
          ► 在線體驗下載:專業版(TCP協議)、專業版(UDP協議)      (關于 iOS 端,請:點此查看

           

          RainbowChat是一套基于開源IM聊天框架 MobileIMSDK 的產品級移動端IM系統。RainbowChat源于真實運營的產品,解決了大量的屏幕適配、細節優化、機器兼容問題(可自行下載體驗:專業版下載安裝)。

          * RainbowChat可能是市面上提供im即時通訊聊天源碼的,唯一一款同時支持TCP、UDP兩種通信協議的IM產品(通信層基于開源IM聊天框架  MobileIMSDK 實現)。

          v8.4 版更新內容

          此版更新內容更多歷史更新日志):

          (1)Android端主要更新內容通信核心層優化!】:

          • 1)[優化] 可根據http接口的url自動判斷并啟用https加密;
          • 2)[優化] 升級核心長連接通信層庫 MobileIMSDK 至 v6.3;
          • 3)[優化] 提供了靈活的接口定制和開啟長連接的SSL/TLS加密傳輸。

          (2)服務端主要更新內容:

          • 1)[優化] 升級核心長連接通信層庫MobileIMSDK 至 v6.3;
          • 2)[優化] 開放了靈活的接口定制和開啟長連接的SSL/TLS加密傳輸。

          此版主要功能運行截圖更多截圖點此查看):

          posted @ 2023-02-16 10:42 Jack Jiang 閱讀(82) | 評論 (0)編輯 收藏

          本文作者:丁同舟,來自金蝶隨手記技術團隊。

          1、引言

          接上篇《金蝶隨手記團隊的Protobuf應用實踐(原理篇)》,本文將以iOS端的Objective-C代碼為例,圖文并茂地向您菔救綰臥趇OS工程中快速使用Protobuf,希望對你有幫助。

           

          學習交流:

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

          2、系列文章

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

          另外:如果您還打算系統地學習IM開發,建議閱讀《新手入門一篇就夠:從零開發移動端IM》。

          3、基本介紹

           

          Protobuf(全稱 Protocol buffers) 是 Google 提出的一種跨平臺、多語言支持且開源的序列化數據格式。相對于類似的 XML 和 JSON,Protobuf 更為小巧、快速和簡單。相對于傳統的 XML 和 JSON, Protobuf 的優勢主要在于:更加小、更加快,其語法目前分為proto2和proto3兩種格式。

          如果你沒不了解Protobuf是什么,建議先閱讀本系列的前幾篇《Protobuf從入門到精通,一篇就夠!》、《快速理解Protobuf的背景、原理、使用、優缺點》、《金蝶隨手記團隊的Protobuf應用實踐(原理篇)》,本篇就不再重復介紹了。

          目前 Google 官方的 Protobuf最新 release 版本為3.21.12,但本文寫作時用的是3.5.1,以下截圖都是基于此版本的環境搭建,如果你使用最新版本,差異并不大,因為只是小版本更新。

          關于 Protobuf的使用可以查閱官方文檔:https://developers.google.com/protocol-buffers/docs/overview,建議養成閱讀文檔的習慣。

          4、準備工作

          4.1環境要求

          最低開發環境要求:

          • 1)Objective-C 2.0 Runtime (32bit & 64bit iOS, 64bit OS X)
          • 2)Xcode 7.0 以上版本

          注意:Protobuf 出于性能考慮沒有使用 ARC,但在 ARC 下是可以使用的。

          4.2下載安裝

          下載 Protobuf 代碼包(https://github.com/protocolbuffers/protobuf/releases/tag/v21.12),因文章截圖時用的是v3.5.1,所以我這里的為了保持一致選擇的是 protobuf-objectivec-3.5.1.tar.gz,版本區別不大,建議依此類推。

          4.3解壓代碼包

          編譯 Protobuf,這里可能需要安裝部分工具:

          $ brew install autoconf

          $ brew install automake

          $ brew install libtool

          運行下面腳本進行編譯:

          $ ./autogen.sh

          $ ./configure

          $ make

          $ makeinstall

          檢查protobuf是否安裝成功:

          $ protoc --version

          如果成功打印版本號則安裝成功:

          libprotoc 3.5.1

          5、在 iOS 中使用 Protobuf

          5.1創建.proto文件

          這里使用官方文檔上的一份示例數據結構創建Person.proto:

          syntax = "proto3";

           

          message Person {

            string name = 1;

            int32 id = 2;

            string email = 3;

           

            enumPhoneType {

              MOBILE = 0;

              HOME = 1;

              WORK = 2;

            }

           

            message PhoneNumber {

              string number = 1;

              PhoneType type = 2;

            }

           

            repeated PhoneNumber phone = 4;

          }

          使用命令行編譯Person.proto為objective-c的文件,編譯出來的文件為Person.pbobjc.h和Person.pbobjc.m:

          protoc Person.proto --objc_out=./

          5.2引入 Protobuf 運行時資源

          Google 官方的文檔提供了兩種引入方式,但使用第一種的時候編譯不能通過,所以這里選擇了第二種。

          具體就是:復制protobuf目錄下的:objectivec/*.h, objectivec/google/protobuf/*.pbobjc.h, objectivec/google/protobuf/*.pbobjc.m, 以及除去 objectivec/GPBProtocolBuffers.m 后的objectivec/*.m。

          這里直接用命令行操作。

          首先進入protobuf下objectivec的目錄:

          $ cdprotobuf-3.5.1/objectivec

          然后復制符合規則的文件到指定的工程目錄下:

          $mkdir~/ProtobufDemo/ProtocolBuffers~/ProtobufDemo/ProtocolBuffers/google~/ProtobufDemo/ProtocolBuffers/google/protobuf

          $ cp*.h *.m ~/ProtobufDemo/ProtocolBuffers

          $ cpgoogle/protobuf/*.pbobjc.h google/protobuf/*.pbobjc.m ~/ProtobufDemo/ProtocolBuffers/google/protobuf

          注意:上面的命令并沒有排除 GPBProtocolBuffers.m 文件,引入時需要手動排除。

          現在把ProtocolBuffers目錄下所有文件以及上面編譯出來的 Person.pbobjc.h 和 Person.pbobjc.m 都引入到工程中。

          現在工程目錄結構大概是長這樣:

           

          注意:由于protobuf沒有使用 ARC,因此需要為所有.m文件加上-fno-objc-arc來關閉 ARC。

          結果如下:

          提示:需要留意工程中的 Header Search Paths 要增加 $(PROJECT_DIR)/ProtocolBuffers(具體的路徑視情況而定)。

          5.3直接引入 ProtocolBuffers 工程

          如果覺得手動引入文件的方式過于復雜,可以直接引入ProtocolBuffers工程作為依賴項。

          1)進入解壓后的protobuf目錄下,復制objective目錄下的所有文件到ProtobufDemo/ProtocolBuffers目錄下。

          2)在ProtobufDemo工程中引入ProtocolBuffers_iOS工程:

          3)在Build Phases中加入依賴關系并鏈接庫:

           

          4)引入Person.pbobjc.hPerson.pbobjc.m文件并為.m加上-fno-objc-arc。

          5)修改工程配置中部分路徑為 $(PROJECT_DIR)/ProtocolBuffers

          5.4運行測試

          首先引入頭文件:

          #import "Person.pbobjc.h"

          生成Person對象并進行編碼和解碼:

          Person *p = [[Person alloc] init];

          p.id_p = 1;

          p.name = @"person1";

          p.email = @"123@qq.com";

           

          //encode

          NSData*data = [p data];

          NSLog(@"Protocol Buffers:\n%@\nData: %@\nData Length: %lu", p, data, data.length);

           

          //decode

          Person *newP = [[Person alloc] initWithData:data error:nil];

          NSLog(@"Decoded: %@", newP);

          運行程序,打印日志如下:

          Protocol Buffers:

          <;Person 0x60c0000da2b0>: {

              name: "person1"

              id: 1

              email: "123@qq.com"

          }

          Data: <0a077065 72736f6e 3110011a 0a313233 4071712e 636f6d>

          Data Length: 23

          Decoded: <;Person 0x6040000d9c90>: {

              name: "person1"

              id: 1

              email: "123@qq.com"

          }

          6、參考資料

          [1] Protobuf 官方開發者指南(中文譯版)

          [2] Protobuf官方手冊

          [3] Protobuf從入門到精通,一篇就夠!

          [4] 如何選擇即時通訊應用的數據傳輸格式

          [5] 強列建議將Protobuf作為你的即時通訊應用數據傳輸格式

          [6] APP與后臺通信數據格式的演進:從文本協議到二進制協議

          [7] 面試必考,史上最通俗大小端字節序詳解

          [8] 移動端IM開發需要面對的技術問題(含通信協議選擇)

          [9] 簡述移動端IM開發的那些坑:架構設計、通信協議和客戶端

          [10] 理論聯系實際:一套典型的IM通信協議設計詳解

          [11] 58到家實時消息系統的協議設計等技術實踐分享

          [12] 金蝶隨手記團隊的Protobuf應用實踐(原理篇)

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

          Coffee time!

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

          posted @ 2023-02-14 12:52 Jack Jiang 閱讀(60) | 評論 (0)編輯 收藏

          本文由釘釘技術專家嘯臺、萬泓分享,為了獲得更好的閱讀效果,本文已對內容進行少修訂和重新排版。

          1、引言

          釘釘后端架構的單元化工作從2018年開始到今年,已經是第五個年頭了。五年的時間,釘釘單元化迭代了三個版本,從最初的毛頭小子,到達今年已經小有成就。

          我們在進行單元化架構建設的過程中,除了網上能找到的屈指可數的文章外,可以直接使用的系統更是乏善可陳,使我們不得不從最基礎的系統開始造輪子,極大的影響建設效率。幸運的是,近幾年云原生技術的興起,讓我們能復用很多基礎設施,進而快速提升我們的單元化建設能力,助力釘釘的發展。

          今天想借此文和大家分享我們在釘釘單元化架構實施過程中的心路歷程和一些最佳實踐。因涉及的技術和業務面太廣,本文的分享無法做到面面俱到,主要是想在同路人中形成共鳴,進而能復用一些架構或子系統的設計和實現思路。

          學習交流:

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

          2、系列文章

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

          3、術語概念

          本文內容中使用了一些專有的技術名詞,為了方便大家理解,我把關鍵的幾個術語概念的縮寫及其含義專門列出來,供大家參考。

          主要是以下幾個

          • 1)Geo:釘釘專有化部署單位,解決數據合規需求,Geo間數據按需互通,并且互通數據在Geo內部做鏡像拷貝,解決兩化問題;
          • 2)Unit: Geo內部資源物理分區隔離的最小單位,解決Geo內的容災和容量的問題;
          • 3)L0:客戶端路由,決定了用戶客戶端接入釘釘服務器的所屬單元,用戶長連接所在的邏輯單元,起到連接加速作用。用戶接入單元;
          • 4)L1:接入層路由,以用戶為維度進行調度,即用戶操作發生的單元。用戶歸屬單元;
          • 5)L2:業務層路由,以業務資源為維度進行調度,大部分的業務資源所在單元應該和用戶調度單元一致,但一些業務無法按照用戶劃分單元,如IM的會話,音視頻的會議。 業務歸屬單元;
          • 6)DMB:負責釘釘應用跨單元RPC調用的轉發,可以認為是釘釘單元化RPC路由中間件;
          • 7)DMR:負責釘釘應用跨單元MQ消息的轉發,可以認為是釘釘單元化MQ路由中間件;
          • 8)DTIM:釘釘IM系統。

          4、單元化架構1.0版:合規驅動下的部署架構

          2018年,部分大客戶出于法律政策、商業機密數據存儲的要求,要求釘釘的數據存儲、訪問接入、服務部署需要在其信任的區域內。既需要滿足其數據存儲私有化要求,同時需要滿足跨地區網絡的rt性能要求。

          于是我們結合阿里云機房部署位置、物理距離、用戶數據安全等方面出發,釘釘在客戶的阿里云機房內建設了一個單元,將通訊錄、IM信息等企業數據單獨存儲在客戶機房。

          我們通過一條專線,將兩個機房邏輯串聯到一起,內部通過DMB/DMR系統,實現了請求互通,這就是釘釘單元化架構的1.0版。

          1.0版比較簡單,純粹是業務驅動,和支付寶單元化建設的初衷——“容災驅動”有較大區別。兩個站點通過UID分段,將用戶劃分為中心用戶和專有用戶。

          上圖只是一個簡化的邏輯結構,內部實現遠比上圖復雜,但是1.0建設主要是從0到1,和大多數異地多活的系統較相似,這里就只簡單的和大家分享一下。

          5、單元化架構2.0版:逼出來的容量架構

          2020年是一個特殊的年份,由于疫情的原因,帶給大家非常多的改變,其中也包括釘釘。

          由于在線辦公與教育流量的突增,開年第一天上班就給釘釘一個下馬威,平峰的流量已經和除夕跨年的持平,但是和除夕不同的是這個流量是持續的,即使節前準備了三倍容量,也抵擋不住流量對系統的沖擊。只能借助阿里云的能力,不斷的擴容。

          但是每天將近30%的流量增幅,單純的擴容也能難保障服務的連續性,最終也遇到了擴無可擴的場景,張北機房沒有機位了,有機器資源但是沒有機位讓我們有力無處使。我們不得不不斷進行系統優化,同時借助限流、降級、雙推等措施,勉強抗住了流量的最高峰。

          疫情之前,我們一直在做高可用,但是這個高可用主要集中在容災機制上,比如搭建容災單元。如同支付寶一樣,是因為當時光纖被挖斷;又比如銀行的兩地三中心架構,是擔心某一個地域由于天災或者戰爭導致數據丟失。疫情的流量給我們上了一課,僅僅關注容災是不夠的,特別是釘釘的DAU從千萬走向億級別之后,更需要在容量上做出提前規劃。

          正因如此,我們認為“容量架構不是設計出來而是真真切切被逼出來的”,所以容量架構就成為我們單元化核心要素之一。

          容量架構是將流量劃分到不同單元,每個單元承載各自的流量。容災架構是單元異常時,能保障核心的能力可用,也可以將流量動態調度到別的單元,實現服務的快速恢復。

          因此釘釘單元化進入了2.0時代,專注于容量和容災的建設。

          6、2.0版是基于什么維度進行流量劃分的?

          要實現流量的劃分,必然要基于一個維度進行劃分,一部分到A單元,一部分到B單元。

          釘釘單元化架構也是參考了淘系和支付寶的單元化架構,前兩者都是基于UID劃分,釘釘單元化的第一個版本其實也是一樣的,基于UID做拆分。

          但是當我們設計容量架構時,發現基于UID劃分無法解決我們的容量問題。

          以IM為例:一條消息其實屬于聊天雙方的,群聊亦是如此。用戶能和任意一個人聊天,這樣我們根本無法找到一個切入點來劃分流量,強行按照UID拆分,必然導致一個用戶的消息出現在N個單元,單元的自封閉就無法做了。

          也有同學會說:為什么消息不按照每個人存儲,這不就能按照UID劃分了嗎?結論是不行。首先這個消息變成了寫擴散,持久化的時候會變成多單元寫,其次是成本翻倍,在DTIM這種過億規模的場景這條路走不通。這里可以多說一點,因為這個觀點來之不易,大家都知道,人是有慣性的,既然淘寶、支付寶甚至是微信都是UID劃分,為什么釘釘要特立獨行?當時我們團隊受到了絕大部分釘釘技術團隊的挑戰,持續長達將近一個月的技術選型的“爭吵”,最終還是達成了一致意見。

          DTIM主要有3個維度,分別是UID、會話(CID)、消息。其中會話和消息是綁定的,而系統中最大量的是消息,按照第一性原則來看,一定要將消息劃分開來,才能做到將容量劃分開來的效果。

          我們再來看看音視頻,是按照房間維度組織流量和數據的,和IM又完全不同。

          同樣,文檔其實更適合按照企業維度來劃分。

          不同的業務擁有不同的維度,因此我們認為:單元化最重要的找到自身“最大”的業務維度,將這個維護拆分,才能實現單元的橫向擴展,我們稱之為“業務路由”。

          回頭來看:我們之前其實是進入了思考誤區,以為淘系和支付寶都是UID維度,我們也要這個維度,其實UID正是前者的業務維度,比如訂單,也是圍繞用戶,并不會有交集的情況,會話就是IM的劃分維度,因此做單元化之前要先找到屬于自己的業務維度。

          7、2.0版是如何實現IM消息的全局路由能力的?

          7.1概述

          UID路由有個最大的好處,就是可以按照UID分段,能實現高效的靜態路由,也不用擔心多單元之間的一致性問題。但是這種分段路由局限性也比較明顯,需要預先分配,單元之間動態調度流量和數據成本極高,而且只能支持這種數值+順序的場景。

          在釘釘的場景中,有會話維度、房間維度、企業維度等等,想簡單采用這種預分段機制難以滿足業務需求。因此我們需要構建一個業務路由系統(RoutingService),實現消息流量的精確路由。

           

           以IM為例:每次消息的發送,在單元化框架層面,會通過消息的會話(CID),查詢路由信息,如果是本單元,流量下行并持久化;如果是非本單元,路由到對應的單元中。

          下圖是三個會話:分別是cid:1001、cid:1002、cid:1003,三個會話隸屬不同單元,不管用戶從哪個單元發送消息,都會路由到會話所在的單元。比如:用戶在Unit B的cid:1001 中發送消息,當消息進入Receiver之后,會先查詢此cid:1001所在的單元,發現是Unit A,路由框架將請求轉到A單元,消息在A單元持久化并通過A單元的同步協議,將數據推送到客戶端。

           

           

           從上圖可知:每次消息發送,都要查詢路由服務,DTIM百萬的峰值,對路由必然會帶來超大的壓力,同時我們能發現,路由數據在多單元實現一致性是一個巨大的挑戰。

          7.2邊緣計算:端到端路由

          在DTIM的場景中,會話的路由信息幾乎不會變更,只有當我們決定將某些超大的會話或者企業騰挪到新單元時,才會發起路由的變更,因此會話的路由信息幾乎可以認為是恒定不變的。那么每次查詢路由服務端,效費比太低,是極大的浪費。

          既然路由信息幾乎不可變,是否將路由信息緩存呢?最常見的是使用一個集中式的Cache系統,緩存Hot的會話,我們也是這么做的,但是這么做還是不夠,一旦Cache系統失效,DTIM還是會出現大面積故障,而且這個百萬級的請求對Cache也是一個極大的壓力。

          考慮到釘釘有強大的客戶端,借用邊緣計算的思路,我們將用戶的會話數據緩存到客戶端。對于客戶端來說,也只用緩存用戶自身最熱的N會話路由數據,消息發送時,通過Header將路由數據攜帶到服務端,服務端路由SDK只要做合法性和續約即可,這樣就將路由流量降低了95%以上。當路由服務出現異常時,還可以繼續使用客戶端路由,將路由的可用性提升到一個新的高度。

          SDK本地會依據上行請求的返回中是否有新的路由信息,進而更新客戶端路由。同時可以借助釘釘有主動下推的能力,通過同步協議將新的路由信息主動推送給客戶端,使會話遷移做到更平順。

          7.3計算下沉:多單元一致性


          對于新會話:比如小明要創建一個群聊,是應該創建在那個單元呢?

          如果在A單元創建了,當會話消息來到B單元,系統怎么能第一時間知道會話已經在被綁定到A單元。

          這里一般的方式有兩種

          • 1)單元之間的存儲系統采用類似DTS的機制進行異步同步,這種機制有秒級延遲;
          • 2)在應用層主動同步,比如接入消息隊列。


          這兩種方式由于都是異步的原因,都會出現不一致的問題,如果會話同時被綁定在兩個單元,邏輯上會導致用戶的歷史消息丟失,這個是不能接受的。

          多地域(Region)數據同步其實是通用的技術挑戰,我們認為存儲系統提供是最好的方式,正如Google的Spanner一樣,這樣對我們上層才是最友好的方式。

          因此我們找到了存儲的OTS、Nuwa團隊一起共建了GlobalTable。GlobalTable的核心原理還是借助Nuwa的一致性組,組分布在多個地域,采用多數派寫入成功即返回的原理,做到20ms以內的一致性寫。

          8、2.0版的容災能力

          釘釘單元化的容災能力是深度結合釘釘的業務層場景落地的,和淘系支付寶等有明確的區別。

          以DTIM為例,最大的特點是當服務單元異常時,服務側仍能提供最核心的服務,保障最基本的能力。本質上是由于DTIM是最終一致性系統,可以短暫允許部分環節失敗。

          可以看一下DTIM發送消息的容災場景。當某個單元完全不可用的情況下,用戶消息發送鏈路通過降級為local模式,在本地校驗非本單元會話數據通過之后直接做消息發送,processor遇到非本單元的會話消息數據可以做單元間投遞做數據回放,本地是否落庫可選,同步協議推送不必區分是否為本單元會話消息數據直接通過本單元的topic推送給客戶端,配合用戶無狀態快速遷移能力,單元間可以實現真正的分鐘級別容災切換能力。

          9、2.0版的成果與突破

          以上是釘釘單元化2.0提供給應用的核心能力,在滿足容災和容量設計需求之后,釘釘單元化給應用帶來了更多的能力和想象空間。

          比如:

          • 1)快速遷移:當某一地域資源不足時,釘釘單元化可以將業務快速的從A單元遷移到B單元;
          • 2)常態化切流:比如新建的教育會話,可以放到獨立的單元;
          • 3)熱點治理:當前某一個會話過熱,特殊時期可以遷移到獨立集群;
          • 4)SLA:滿足不同的VIP客戶需求,基于不同的SLA和售賣價格,將VIP客戶放到對應地單元。

          核心還是我們擁有單元化能力之后,實現了多單元流量的快速調度,為業務解決了后顧之憂。

          10、2.0版在新時代面臨的新挑戰

          10.1魚和熊掌不可兼得

          2022年對釘釘來說是成本之年,成本的壓力不光落到了團隊,還落到了每個人身上。

          正如存儲的CAP理論是一樣的,我們同時只能滿足兩個維度,對于流量(性能P)、成本(C)、體驗(E)也是一樣,在流量不可預知和干預的情況下,選擇成本必然導致體驗受損,反之選擇體驗,必然導致成本升高。進入下半年,疫情反復帶來流量的反復,為了實現可控的教育成本,只能在高峰期降級部分能力,這又導致體驗受損,這段時間的工單量可以窺見一斑。

          流量是用戶側觸發的,我們無法干預,只能在成本和體驗之間尋求平衡。和前面提及的一樣,為了減小成本的消耗這就導致我們在擴容和縮容之間疲于奔命,反應不及時甚至有故障的危險,這種機制不可取也不可持續。到底是要流量與成本,還是要流量與體驗,給我們技術團隊帶來了巨大的挑戰和矛盾。

          10.2商業化路在何方

          當前釘釘為支持大客戶提供了多種解決方案,專業釘釘、專屬存儲與打包、專有釘釘。

          專屬釘釘通過APP專屬化以及部分專屬功能,比如為一個企業定制一個擁有獨立Logo的APP,能滿足一般的中大型客戶的業務訴求。

          對于大型以及超大型客戶,我們提供專有釘釘,提供專有化輸出,完全隔離的方案,比如浙政釘。

          伴隨著釘釘的商業化進入深水區,客戶對釘釘提出了新的訴求,特別是數據安全與歸屬、互聯互通、完整的能力棧等訴求,當前釘釘輸出產品形態都無法同時地滿足以上需求。

          前幾年互聯網上出現的幾起數據安全事件,數據丟失與泄露,未經客戶授權私自訪問客戶數據,讓大多數客戶不信任服務提供商,即使服務商的安全能力已經是業界一線能力。其實這個是可以理解的,數據即客戶的生命線,數據無法在自身可控范圍內,特別是對于很多特殊行業,這是無法接受的,自身性命豈能假手于人。專屬釘釘在面臨這種客戶時,前線售賣同學是無能為力。

          那么很多同學肯定會提“如果專屬釘釘滿足不了需求,我們專有釘釘不是能解決這些問題嗎?”,其實單單從訴求來看,專有釘釘場景是切合客戶的業務訴求,提供完全獨立運行環境、可控的數據安全。但是專有釘釘由于其獨特的架構帶來高昂的售價以及后期的運維代價,對于超大型的客戶來說也難以承擔如此高的成本。對于釘釘自身來說,從研發到后續運維,維護一套獨立體系也難以在客戶側大面積推廣。

          11、單元化架構3.0版:混合云架構

          11.1概述

          釘釘單元化經過四年的發展,在容災和容量上做出一定的積淀,同時完成了一些核心技術的積累。

          當整體架構成熟之后,我們也在思考,單元化能否從技術架構升級為業務架構,比如搭建獨立的高可用單元,按照售賣的SLA提供給VIP客戶,支持釘釘商業化的發展。

          同時我們在云原生逐步發力,將部分核心應用放到云上,經過這一年多的運行,遇到了新的挑戰,但更獲得云下無法獲得的計算彈性能力,云上的彈性對云下是一個降維打擊,從一個新的方向解決計算問題。

          如上文提到的兩個核心挑戰,釘釘單元化同樣面臨這個問題,在持續的發展中找到了一個合適的架構方向。

          基本思路是:

          • 1)云下作為基本盤,保障核心流量的問題,畢竟云下經過集團多年的打磨,不管是穩定性還是流程的合理性都有保障;
          • 2)云上應對高漲異常的流量,比如和疫情正相關的教育流量,既保證了服務的穩定性,又能充分利用云上彈性能力,在提供完整能力的前提下做到一個相對較低的成本。

          其次是升級Geo概念:

          • 1)將Geo作為一個獨立的業務域,實現Geo級別完全獨立部署,分布式云模式;
          • 2)同時Geo之間按需互通,從研發體系上能做到一套代碼。

          因此,釘釘單元化來到了3.0版本,我們稱之為釘釘單元化混合云架構。

          混合云主要是從兩個維度來看:

          • 第一:是云上云下,我們認為云上云下并不是取代的關系,而是相互補充的關系,是一個長期的狀態,正如很多大客戶隨著規模的持續擴張,最終依賴的部分核心能力必然走向自研道理一樣,這能做成本的進一步降低,所以架構是一個混合云架構;
          • 第二:業務架構上也是混合云架構,通過不同的Geo,將不同的業務邏輯上聚合到一起,構建起一張釘釘的大網,不同Geo按需互通,實現了業務架構的混合。

          3.0從系統架構上相對于2.0,最大的區別就是云原生技術的運用和互通網關的建立。

          11.2云原生技術 :抵抗系統架構熵增的有效手段

          近幾年,互聯網圈最火的技術莫過于以Docker為代表的云原生技術最為火熱,各大云廠商也都在不遺余力的推廣云原生技術以及對應的產品。同時釘釘服務過億DAU的客戶,面對各種可靠性、服務連續性、并發、容災等技術挑戰,也都走到了現有技術的邊界。

          所以我們也在不斷吸收新的技術和架構,希望從體系與架構上降低我們的技術復雜度,以抵抗熵增。

          我們在2021年底啟動了云原生升級戰略,升級云原生技術并不是為了技術而升級,而是切實面臨巨大的技術挑戰。

          1)首先我們面臨多語言的挑戰:

          我們以IM為例,IM的核心邏輯都是使用C++構建,但是我們常用的中間件三大件:存儲、緩存、異步隊列,其中緩存和異步隊列在C++客戶端上長期建設不足,導致IM長期在使用低版本。

          低版本由于長時間缺乏維護,經常會出現異常,比如隊列假死、消費不均等,導致我們自己不得不親自上陣修改SDK的代碼,以致最后難以使用到產品的新能力,阻礙IM服務能力的提升。

          2)其次是多產品多云的挑戰:

          我們以阿里云為例,數據庫類目下的產品,從類別上就有關系數據庫、NoSQL數據庫、數倉等等,還有存儲也是一樣。

          對于我們上層業務,其實絕大部分服務都只依賴了底層的CURD,這么多產品,每次對接一個產品都要開發一輪。

          配置系統也是一樣,彈內有Diamond,云上有Nacos、Mse,K8s有自己的Configmap等,而且這些配置系統不像數據庫有標準,而是百花齊放,但是這樣卻苦了我們使用者。

          這些內容不是我們的核心路徑,浪費大把時間在各種產品接口的適配上,明顯拖累了釘釘的發展。

          3)最后就是通用的流量治理挑戰:

          釘釘很多系統都是最終一致的系統,IM就是典型的最終一致系統,這類系統和強同步系統在架構設計有一個明顯的區別,強一致系統如果遇到失敗,必須要持續重試直到成功,所以一般編程上都是重試+退避。

          但是最終一致系統不是,這類系統允許部分節點失敗,不要阻礙其他流程,失敗的流量通過一個異步回旋的隊列,將數據逐步回放回來即可。這種回旋需要借助異步隊列,而且要設計各種消費機制,比如限速、比如丟棄等等,這是一個通用的邏輯,但是每個業務方或多或少都在實現自己的回旋系統,重復的造輪子。又比如各種故障注入,單元化路由流量等等,要想擁有這個能力,團隊不得不投入人力研發。

          在對付架構復雜度上,我們主要從兩個維度來屏蔽復雜度。

          首先代碼層面我們選擇了DDD模式,我們使用DDD分層核心是把對外系統的依賴全部收攏到Infrastructure這一層,全部采用純虛函數(Interface)對外提供接口。屏蔽底層中間件差異和細節。

          在架構上采用Sidecar的模式,類似于Dapr的思想,通過標準的GRPC和PB實現應用與中間件解耦。Sidecar中集成了各種中間件、配置系統、灰度系統等,等價實現了應用和中間件的解耦。上文中提到的不管是多語言挑戰、多云多產品的挑戰、重復造輪子等問題,都能很好的解決。

          11.3互通網關 :混合架構的基石

          云上云下互通,或者說多個云賬戶VPC之間的互通,我們常見的有兩種方案:

          • 1)其一是VPC直接打通,讓多個VPC之間形成一個大的局域網,RealServer實現點對點互通;
          • 2)其一是中間搭建一個負載均衡器,通過暴露EIP實現互通。

          兩個方案都有自己的優缺點。

          對于方案一:打通的VPC涉及到IP規劃,如果前期沒有合理規劃,后續很難打通;還有這種方案有水桶短板安全問題,一旦一個VPC被攻破,這張網也被攻破;但是對于內部的應用來說架構就比較簡單,可以僅僅借助K8s DNS service就能做到服務發現。

          對于方案二:最大的缺點就是中間有一個集中式的負載均衡,需要申請獨立的LB才可訪問;但是這種方案隔離性好。

          對于釘釘單元化來說,涉及N個業務方,N * M個應用,對應X個VPC,要想VPC之間打通,幾乎沒有可能性,而且VPC打通,還面臨應用之間的安全性問題。要實現Geo之間互通,環境之間的隔離性是基本要求,與此同時,我們也要考慮到系統的可擴展性,所以我們必須要構建一套獨立的流量網關,實現流量加密、尋址、轉發等通用能力。

          釘釘互通網關是構建在Envoy之上的系統,雙向Ingress和Egress,支持GRPC和釘釘自研協議。具備流量管理、傳輸加密、單元尋址等能力。釘釘單元化借助互通網關的能力,再配合全局流控系統,我們可以在多單元之間實現精確的流量控制和調度。

          12、寫在最后

          伴隨著專屬集群的持續輸出,客戶對專屬的場景需求會越來越多,需要我們投入更多的人力持續的建設。

          比如:

          • 1)在架構側:首先是Sidecar持續強化,支持更多的中間件和環境,提供不同維度的安全能力,滿足客戶和應用的安全需求;
          • 2)在運維側:我們需要構建多Geo管理能力,完善Geo和單元之間流量快速調度能力,提供自動化的自檢系統等;
          • 3)在交付側:如果實現快速交付,比如是否能做到新應用一周完成單元化改造,新Geo一天部署完成。這些挑戰都是接下來我們要重點投入的方向。

          對于標準釘釘來說,這個是我們的基本盤,一個穩定可靠且低成本的釘釘是我們持之以恒的目標,接下來我們會加大云上流量的占比,充分的借助云上彈性能力,實現可控的成本。

          今天我們只是站在釘釘的角度上拋了一個“磚”,希望在異地多活這個領域激起一層浪花,歡迎大家一起討論。

          13、相關資料

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

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

          [3] 深度解密釘釘即時消息服務DTIM的技術設計

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

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

          [6] IM系統的MQ消息中間件選型:Kafka還是RabbitMQ?

          [7] 深度揭密RocketMQ在釘釘IM系統中的應用實踐

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

          posted @ 2023-02-13 10:50 Jack Jiang 閱讀(130) | 評論 (0)編輯 收藏

          一、更新內容簡介

          本次更新為次要版本更新,進行了若干優化(更新歷史詳見:碼云 Release Nodes)。可能是市面上唯一同時支持 UDP+TCP+WebSocket 三種協議的同類開源IM框架。

          二、MobileIMSDK簡介

          MobileIMSDK 是一套專為移動端開發的原創IM通信層框架:

          • 歷經8年、久經考驗;
          • 超輕量級、高度提煉,lib包50KB以內;
          • 精心封裝,一套API同時支持UDP、TCP、WebSocket三種協議(可能是全網唯一開源的);
          • 客戶端支持 iOS、Android標準Java、H5、小程序(開發中..)、Uniapp(開發中..);
          • 服務端基于Netty,性能卓越、易于擴展;??
          • 可與姊妹工程 MobileIMSDK-Web 無縫互通實現網頁端聊天或推送等;??
          • 可應用于跨設備、跨網絡的聊天APP、企業OA、消息推送等各種場景。

          MobileIMSDK工程始于2013年10月,起初用作某產品的即時通訊底層實現,完全從零開發,技術自主可控!

          您可能需要:查看關于MobileIMSDK的詳細介紹。

          三、代碼托管同步更新

          OsChina.net

          GitHub.com

          四、MobileIMSDK設計目標

          讓開發者專注于應用邏輯的開發,底層復雜的即時通訊算法交由SDK開發人員,從而解偶即時通訊應用開發的復雜性。

          五、MobileIMSDK框架組成

          整套MobileIMSDK框架由以下5部分組成:

          1. Android客戶端SDK:用于Android版即時通訊客戶端,支持Android 2.3及以上,查看API文檔
          2. iOS客戶端SDK:用于開發iOS版即時通訊客戶端,支持iOS 8.0及以上,查看API文檔;
          3. Java客戶端SDK:用于開發跨平臺的PC端即時通訊客戶端,支持Java 1.6及以上,查看API文檔
          4. H5客戶端SDK:暫無開源版,查看精編注釋版;
          5. 服務端SDK:用于開發即時通訊服務端,支持Java 1.7及以上版本,查看API文檔。

          整套MobileIMSDK框架的架構組成:

           另外:MobileIMSDK可與姊妹工程 MobileIMSDK-Web 無縫互通,從而實現Web網頁端聊天或推送等。

          六、MobileIMSDK v6.3更新內容 

          【重要說明】:

          MobileIMSDK v6.3 為次要版本,進行了若干優化! 查看詳情

          【新增的特性】:

          • 1. [所有端] 提供了靈活的接口供開發者定制和開啟SSL/TLS加密傳輸;

          【其它優化和提升】:

          • 1. [iOS] 解決了iOS端Demo在iOS16下的適配問題;
          • 2. [iOS] 解決了iOS端Demo在黑暗模式下背景和標題欄是黑色的問題;
          • 3. [Android] 優化了Android端Demo在最新Android系統下的適配等;
          • 4. [Android/Java] 對全局單例增加線程安全處理,防止在高版本JDK中出現并發調用而導致單例被重復實例化。

          【版本地址】:

          https://gitee.com/jackjiang/MobileIMSDK/releases/tag/6.3

          posted @ 2023-02-07 10:27 Jack Jiang 閱讀(69) | 評論 (0)編輯 收藏

               摘要: 本文引用了“鮮棗課堂”的《史上最強5G科普》文章內容。為了更好的內容呈現,在引用和收錄時內容有改動,轉載時請注明原文來源。1、內容概述? 5G技術的關注度越來越高:在此之前,5G技術對于普通老百姓來說,似乎還很遙遠,關注度并不高。但從去年開始,美帝赤裸裸打壓中興和華為的國際事件,讓5G技術在國內有了很高的關注度。美帝打壓中興、華為固然是壞事,但因為這個事情,相當于反過來為5...  閱讀全文

          posted @ 2023-02-04 16:21 Jack Jiang 閱讀(79) | 評論 (0)編輯 收藏

               摘要: 本文由金蝶隨手記技術團隊丁同舟分享。1、引言跟移動端IM中追求數據傳輸效率、網絡流量消耗等需求一樣,隨手記客戶端與服務端交互的過程中,對部分數據的傳輸大小和效率也有較高的要求,普通的數據格式如 JSON 或者 XML 已經不能滿足,因此決定采用 Google 推出的 Protocol Buffers 以達到數據高效傳輸。本文將基于隨手記團隊的Protobuf應用實踐,分享了Protobuf的技術原...  閱讀全文

          posted @ 2023-01-28 16:57 Jack Jiang 閱讀(126) | 評論 (0)編輯 收藏

               摘要: 1、前言Protobuf是Google開源的一種混合語言數據標準,已被各種互聯網項目大量使用。Protobuf最大的特點是數據格式擁有極高的壓縮比,這在移動互聯時代是極具價值的(因為移動網絡流量到目前為止仍然昂貴的),如果你的APP能比競品更省流量,無疑這也將成為您產品的亮點之一?,F在,尤其IM、消息推送這類應用中,Protobuf的應用更是非常廣泛,基于它的優秀表現,微信和手機QQ這樣的主流IM...  閱讀全文

          posted @ 2023-01-05 16:14 Jack Jiang 閱讀(153) | 評論 (0)編輯 收藏

          本文由釘釘技術專家尹啟繡分享,有修訂和重新排版。

          1、引言

          短短的幾年時間,釘釘便迅速成為一款國民級應用,發展速度堪稱迅猛。

          IM作為釘釘最核心的功能,每天需要支持海量企業用戶的溝通,同時還通過 PaaS 形式為淘寶、高德等 App 提供基礎的即時通訊能力,是日均千億級消息量的 IM 平臺。

          在釘釘的IM中,我們通過 RocketMQ實現了系統解耦、異步削峰填谷,還通過定時消息實現分布式定時任務等高級特性。同時與 RocketMQ 深入共創,不斷優化解決了很多RocketMQ本身的問題,并且孵化出 POP 消費模式等新特性,使 RocketMQ 能夠完美支持對性能穩定性和時延要求非常高的 IM 系統。本文將為你分享這些內容。

          學習交流:

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

          2、系列文章

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

          3、釘釘IM面臨的巨大技術挑戰

          3.1 概述

          釘釘作為企業級 IM 領先者,面臨著巨大的技術挑戰。市面上DAU過億的App里,只有釘釘是2B產品,我們不僅需要和其他 2C 產品一樣,支持海量用戶的低時延、高并發、高性能、高可用,還需保證企業級用戶在使用釘釘時能夠提升溝通協同效率。

          下圖是概括的是釘釘的主要能力:

          3.2 技術挑戰1:ToB與ToC的差異

          作為企業級應用,需要保證幫助用戶提升溝通體驗。

          ToB 的工作溝通和 ToC 的場景生活溝通存在較大差異, ToC的IM產品比如微信,在有完整的關系鏈后,只需滿足大部分用戶需求即可。

          然而微信的很多體驗其實并不友好:比如聊天消息中的視頻圖片在固定時間內沒有打開則會無法下載,卸載重裝之后聊天記錄全部丟失。

          而 ToB 場景下:聊天記錄是非常重要的內容,釘釘為保證用戶消息不丟失,提供了多端同步和消息云端存儲的能力,用戶任意換端都能查看完整的聊天記錄。

          在工作過程中,大量會議是工作效率殺手,釘釘還提供了已讀、Ding 等效率套件,為工作溝通提供新選項。

          3.3 技術挑戰2:安全要求高

          在ToB 的工作場景下,用戶對信息安全要求非常高,信息安全是企業的生命線。

          釘釘提供了人和組織架構打通的工作群,用戶離開組織后自動退出企業工作群,這樣就很好地保障了企業信息的安全。

          同時,在已經支持的全鏈路加密能力上提供了三方加密能力,可以最大程度保障企業用戶的信息安全性。

          3.4 技術挑戰3:穩定性要求高

          企業用戶對穩定性的要求也非常高,如果釘釘出現故障,深度使用釘釘的企業都會受到巨大影響。

          因此,釘釘 IM 系統在穩定性上也做了非常深入的建設,架構上對依賴和流量做了深入治理,核心能力所有依賴都為雙倍。

          比如雖然 RocketMQ 已經非常穩定,也沒有發生過故障,但是對 RocketMQ 可能出現故障的產品依然做了很好的保護,使用 RocketMQ 定時消息和堆積能力做熱點治理和流量防護,讓系統面對大規模流量時能從容應對,并且建設了異地多活和可彈性擴縮容能力,疫情期間很好地保證了學生們的在線課堂。

          在穩定性機制上,常態化容災演練、突襲演練、自動化健康巡檢等也能很好地保證線上穩定性。比如波浪式流量就是在做斷網演練時發現。

          3.5 技術挑戰4:業務多樣性

          針對不同行業的業務多樣性,還要盡可能地滿足用戶的通用性需求,比如萬人群、全員群等,目前釘釘已經做到能夠支持 10 萬人級別的群。

          更多的業務需求將依賴于我們抽象出的通用開放能力,將 IM 能力盡可能地開放給企業和三方 ISV,使得不同形態的業務都能在釘釘平臺上得到滿足 。

          4、消息隊列在釘釘IM系統中的重要作用

          4.1 概述

          在如此豐富的企業級能力下,釘釘IM要與微信等 ToC 產品一樣,支持億級用戶低時延溝通,系統架構需要具備高并發、高性能、高可用的能力,挑戰非常之大。

          IM 本身是異步化溝通系統,與開會或者電話溝通相比,讓溝通雙方異步處理消息能夠減少打斷次數,提升溝通效率。這種異步的特性和消息隊列的能力很契合,消息隊列可以很好地幫助 IM 完成異步化解耦、失敗重試、削峰填谷等能力。

          這里,我們以釘釘IM系統最核心的發消息和已讀鏈路簡化流程(如下圖所示),來詳細說明消息隊列在系統里的重要作用。

           

          4.2 發消息鏈路

          釘釘IM系統的發消息鏈路流程如下:

          • 1)處于登錄狀態的釘釘用戶發送一條消息時,首先會將請求發送到 receiver 應用;
          • 2)為保證發消息體驗和成功率,receiver 應用只做這條消息能否發送的校驗,其他如消息入庫、接收者推送等都交由下游應用完成;
          • 3)校驗完成之后將消息投遞給消息隊列,成功后即可返回給用戶;
          • 4)消息發送成功,processor 會從消息隊列里訂閱到這條消息,并對消息進行入庫處理,再通過消息隊列將消息交給同步服務 syncserver 做處理,將消息同步給在線接收者。

          上述過程中,對于不在線的用戶:可以通過消息隊列將消息推給離線 push 系統。離線 push 系統可以對接接蘋果、華為、小米等推送系統進行離線推送。

          用戶發消息過程中的每一步,失敗后都可通過消息隊列進行重試處理。如 processor 入庫失敗,可將消息打回消息隊列,繼續回旋處理,達到最終一致。同時,可以在訂閱的過程中對消費限速,避免線上突發峰值給系統帶來災難性的后果。

          4.3 消息已讀鏈路

          釘釘IM系統的消息已讀鏈路流程如下:

          • 1)用戶對一條消息做讀操作后,會發送請求到已讀服務;
          • 2)已讀服務收到請求后,直接將請求放到消息隊列進行異步處理,同時可以達到削峰填谷的目的;
          • 3)已讀服務處理完之后,將已讀事件推給同步服務,讓同步服務將已讀事件推送給消息發送者。

          從上面兩個鏈路可以看出,消息隊列是 IM 系統里非常重要的組成部分。

          5、釘釘IM選擇RocketMQ的原因

          阿里內部曾有 notify、RocketMQ 兩套應用消息中間件,也有其他基于 MQTT 協議實現的消息隊列,最終都被 RocketMQ 統一。

          IM 系統對消息隊列有如下幾個基本要求:

          • 1)解耦和削峰填谷(這是消息隊列的基礎能力);
          • 2)高性能、低時延;
          • 3)高可用性。

          對于第 3)點:要求消息隊列的高可用性方面不僅包括系統可用性,也包括數據可用性,要求寫入消息隊列時消息不丟失(釘釘 IM 對消息的保證級別是一條都不丟)。

          RocketMQ 經過多次雙 11 考驗,其堆積性能、低時延、高可用已成為業屆標桿,完全符合對消息隊列的要求。

          同時它的其他特性也非常豐富,如定時消息、事務消息,能夠以極低的成本實現分布式定時任務,消息可重放和死信隊列提供了后悔藥的能力,比如線上系統出現 bug ,很多消息沒有正確處理,可以通過重置位點、重新消費的方式,訂正之前的錯誤處理。

          另外:消息隊列的使用場景非常豐富,RocketMQ 的擴展能力可以在消息發送和消費上做切面處理,實現通用性的擴展封裝,大大降低開發工作量。 Tag & SQL 過濾能讓下游針對性地訂閱定業務需要的消息,無需訂閱整個 topic 里的所有消息,大幅降低下游系統的訂閱壓力。

          RocketMQ 至今從未發生故障,集群峰值 TPS 可達 300w/s,從生產到消費時延能夠保證在 10 ms 以內,支持 30 億條消息堆積,核心指標數據表現搶眼,性能異常優秀。

          6、RocketMQ的消息必達3重保險

          如上圖所示,發消息流程中,很重要的一步是 receiver 應用做完消息能否發送的校驗之后,通過 RocketMQ 將消息投遞給 processor做消息入庫處理。

          投遞過程中,將提供三重保險,以保證消息發送萬無一失。

          第一重保險:receiver 將消息寫進 RocketMQ 時, RocketMQ SDK 默認會重試五次(每次嘗試不同的 broker ,保障了消息寫失敗的概率非常?。?。

          第二重保險:寫入 RocketMQ 失敗的情況下,會嘗試以 RPC 形式將消息投遞給 processor 。

          第三重保險:如果 RPC 形式也失敗,會嘗試將本地 redoLog 通過 Crontab 任務定時將消息回放到 RocketMQ 里面。

          此外,如何在系統異常的情況下做到消息最終一致?

          Processor 收到上游投遞的消息時,會嘗試對消息做入庫處理。即使入庫失敗,依然會將消息投給同步服務,將消息下發,保證實時消息收發正常。異常情況時會將消息重新投遞到異常 topic 進行重試,投遞過程中通過設置RocketMQ 定時消息做退避處理,對異常 topic 做限速消費。

          重試寫不同的 topic 是為了與正常流量隔離,優先處理正常流量,防止因為異常流量消費而導致真正的線上消息處理被延遲。

          另外:Rocket MQ 的一個 broker 默認只有一個 Retry 消息隊列,如果消費失敗量特別大的情況下,會導致下游負載不均,某些機器打死。

          此外:如果系統持續發生異常,則會不斷地進行回旋重試,如果不做限速處理,線上容易出現流量疊加,導致整個系統雪崩。

          7、RocketMQ的獨門絕技——分布式定時任務

          在幾千人的群里發一條消息,假設有 1/4 的成員同時開著聊天窗口,如果不對服務端已讀服務和客戶端需要更新的已讀數做合并處理,更新的 QPS 會高達到 1000/s。釘釘能夠支持十幾萬人的超大群,超大群的活躍對服務端和客戶端都會帶來很大沖擊,而實際上用戶的需求只需實現秒級更新。

          針對以上場景:可以利用 RocketMQ 的定時消息能力實現分布式定時任務。

          以已讀流程為例(如下圖所示),用戶發起請求時,會將請求放入集中式請求隊列,再通過 RocketMQ 定時消息生成定時任務,比如 5 秒后批量處理。5秒之后,RocketMQ 訂閱到任務觸發消息,將隊列里面所有請求都取出處理。

          ▲ 用 RocketMQ 實現分布式定時任務的流程原理

          我們抽象了一個分布式定時任務的組件,提供了很多其他實時性可達秒級的功能,如萬人群的群狀態更新、消息擴展更新都接入了此組件。通過組件的定時合并處理,大幅降低系統壓力。

          如上圖(右邊部分),在一些大群活躍的時間點成功地讓流量下降并保持平穩狀態。

          8、釘釘IM使用RocketMQ遇到的技術問題

          8.1 概述

          RocketMQ 的生產端策略如下:

          • 1)生產者獲取到對應 topic 所有 broker 和 Queue 列表,然后輪詢寫入消息;
          • 2)消費者端也會獲取到 topic 所有 broker 和Queue列表;
          • 3)還需要要從 broker 中獲取所有消費者 IP 列表進行排序(按照配置負載均衡,如哈希、一次性哈希等策略計算出自己應該訂閱哪些 Queue)。

          上圖中:ConsumerGroupA的Consumer1被分配到MessageQueue0和MessageQueue1,則它訂閱MessageQueue0和MessageQueue1。

          在RocketMQ的使用過程中,我們面臨了諸多問題,下面我們來逐一分享。

          8.2 問題1:波浪式流量

          我們發現訂閱消息集群滾動時,CPU 呈現波浪式飆升。

          經過深入排查發現,斷網演練后進行網絡恢復時,大量 producer 同時恢復工作,同時從第一個 broker 的第一個 Queue 開始寫入消息,生產消息波浪式寫入 RocketMQ ,進而導致消費者端出現波浪式流量。

          最終,我們聯系 RocketMQ 開發人員,調整了生產策略,每次生產者發現 broker 數量或狀態發生變化時,都會隨機選取一個初始Queue寫入消息,以此解決問題。

          另一個導致波浪式流量的問題是配置問題。

          排查線上問題時,從 broker 視角看,每個 broker 的消息量都是平均的,但 consumer 之間流量相差特別大。最終通過在 producer 側嘗試抓包得以定位到問題,是由于 producer 寫入消息時超時率偏高。

          梳理配置后發現,是由于 producer 寫入消息時配置超時太短,Rocket MQ 在寫消息時會嘗試多次,比如第一個 broker 寫入失敗后,將直接跳到下一個 broker 的第一個 Queue ,導致每個 broker 的第一個 Queue 消息量特別大,而靠后的 partition 幾乎沒有消息。

          8.3 問題2:負載均衡維度太粗

          負載均衡只能到Queue維度,導致需要不時地關注 Queue 數量。

          比如線上流量增長過快,需要進行擴容,而擴容后發現機器數大于 Queue 數量,導致無論怎么擴容都無法分擔線上流量,最終只能聯系 RocketMQ 運維人員調高 Queue 數量來解決。

          雖然調高 Queue 數量能解決機器無法訂閱的問題,但因為負載均衡策略只到 Queue 維度,負載始終無法均衡。從下圖可以看到, consumer 1 訂閱了兩個 Queue 而 consumer 2 只訂閱了一個 Queue。

          8.4 問題3:單機夯死導致消息堆積

          單機夯死導致消息堆積,這也是負載均衡只能到 Queue 維度帶來的副作用。

          比如 Broker A 的 Queue 由 consumer 1 訂閱,出現宿主機磁盤 IO 夯死但與 broker 之間的心跳依然正常,導致 Queue 消息長時間無法訂閱進而影響用戶接收消息。最終只能通過手動介入將對應機器下線來解決。

          8.5 問題4:rebalance

          Rocket MQ 的負載均衡由 client 自己計算,導致有機器異常或發布時,整個集群狀態不穩定,時常會出現某些 Queue 有多個 consumer 訂閱,而某些 Queue 在幾十秒內沒有 consumer 訂閱的情況。

          因而導致線上發布的時候,出現消息亂序或對方已回消息但顯示未讀的情況。

          8.6 問題5:C++ SDK 能力缺失

          釘釘IM的核心處理模塊Receiver、processor 等應用都是通過 C++ 實現,而RocketMQ 的 C++ SDK 相比于 Java 存在較大缺失。經常出現內存泄漏或 CPU 飆高的情況,嚴重影響線上服務的穩定。

          9、釘釘IM與RocketMQ的相互促進

          面對以上困擾,在經過過多次討論和共創后,最終孵化出 RocketMQ 5.0 POP 消費模式。

          這是 RocketMQ 在實時系統里程碑式的升級,解決了大量實時系統使用 RocketMQ 過程中遇到的問題(如下圖所示)。

          1)Pop消費模式下,每一個 consumer 都會與所有 broker 建立長連接并具備消費能力,以 broker 維護整個消息訂閱的負載均衡和位點。重云輕端的模式下,負載均衡、訂閱消息、位點維護都在客戶端完成,而新客戶端只需做長鏈接管理、消息接收,并且通用 gRPC 協議,使得多語言比如 C++、Go、 Python 等語言客戶端都能輕松實現,無需持續投入力去升級維護 SDK 。

          2)broker能力升級更簡單。重云輕端很好地解決了客戶端版本升級問題,客戶端改動的可能性和頻率大大降低。以往升級新特性或能力只能推動所有相關 SDK 應用進行升級發布,升級過程中還需考慮新老兼容等問題,工作量極大。而新模式只需升級 broker 即可完成工作。

          3)單機夯死消息能繼續被消費。新模式下 consumer 和 broker 進行網狀連接和消息訂閱,由 broker 通過負載均衡策略平均分配消息給 consumer 進行消費,以往宕機夯死導致的 Queue 消息堆積問題也迎刃而解。如果 broker 發現 consumer 長時間沒有進行消息 ACK ,則將不再對其投遞消息,徹底解決單機夯死問題。

          4)無需關注partition數量。

          5)徹底解決rebalance。

          6)負載更均衡。通過新的訂閱模式,不管上游流量如何偏移,只要不超過單個 broker 的容量上限,消費端都能實現真正意義上的負載均衡。

          POP 模式消費模式已經在釘釘 IM 場景磨合得非常成熟,在對可用性、性能、時延方面要求非常高的釘釘 IM 系統證明了自己,也證明了不斷升級的 RocketMQ 是即時通訊場景消息隊列的不二選擇。

          10、相關資料

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

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

          [3] 深度解密釘釘即時消息服務DTIM的技術設計

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

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

          [6] IM系統的MQ消息中間件選型:Kafka還是RabbitMQ?

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

          posted @ 2022-12-30 12:05 Jack Jiang 閱讀(114) | 評論 (0)編輯 收藏

          僅列出標題
          共50頁: First 上一頁 14 15 16 17 18 19 20 21 22 下一頁 Last 
          Jack Jiang的 Mail: jb2011@163.com, 聯系QQ: 413980957, 微信: hellojackjiang
          主站蜘蛛池模板: 晴隆县| 措美县| 宝山区| 苗栗县| 枣强县| 莒南县| 卢湾区| 泸州市| 怀化市| 水富县| 广灵县| 嘉祥县| 日喀则市| 郯城县| 华池县| 晋中市| 莱阳市| 夏津县| 宜宾市| 东兰县| 怀化市| 和林格尔县| 淄博市| 大洼县| 定兴县| 酒泉市| 长葛市| 吉林市| 巴青县| 临邑县| 区。| 广饶县| 仁布县| 民和| 蕉岭县| 金川县| 秦皇岛市| 仁怀市| 永靖县| 彰武县| 延津县|