本文由作者jhon_11分享,有大量修訂和改動。
1、引言
如何設計一款高效能、高併發、高可用的im綜合訊息平臺是很多公司發展過程中會碰到且必須要解決的問題。比如一家公司內部的通訊系統、各個網際網路平臺的客服諮詢系統,都是離不開一款好用且維護的方便im綜合訊息系統。
那麼,我們應該怎麼樣來設計一款三高特性的im系統,並能同時支援各個業務線的接入(比如:內部OA通訊、客服諮詢、訊息推送等等功能)有呢?
下面就由我來介紹一下我所負責的公司IM綜合訊息系統所經歷的架構設計歷程,以及架構設計過程中的一些思路和總結,希望能給你帶來啟發。
學習交流:
- 移動端IM開發入門文章:《新手入門一篇就夠:從零開發移動端IM》
- 開源IM框架原始碼:https://github.com/JackJiang2...(備用地址點此)
(本文已同步釋出於:http://www.52im.net/thread-39...)
2、初版IM架構
2.1 概述
im第一版設計的初衷是公司需要一款im訊息中介軟體用於支撐客服諮詢業務。
但是,考慮到為了方便日後其他業務線也能接入訊息溝通平臺,所以一開始就將整個訊息中心的能力需求給到中介軟體團隊進行開發,以便除客服外的各業務線接入綜合訊息中心,從而實現多元的訊息實時觸達能力。
2.2 初版架構介紹
初版架構圖如下圖所示:
針對上面的架構圖,我們逐個解釋一下各模組的作用。
1)儲存端:
在初版的架構下,儲存端我們使用tidb、redis作為主要儲存:
[1] redis用於儲存訊息已讀未讀,快取連線資訊等功能;
[2] tidb作為開源的分散式資料庫,選擇它是為了方便訊息的儲存。
2)mq訊息匯流排:
我們使用rocketmq來實現訊息匯流排(PS:即分散式情況下,不同im例項間通過MQ進行訊息互動)。
訊息匯流排是整個im的核心,使用rocketmq能支援十萬級別的tps。基本所有服務都要從訊息匯流排中消費訊息進行業務處理。
3)zookeeper註冊中心:各個服務會註冊到zk中,方便服務之間內部進行呼叫,同樣也可以暴露服務給外部進行呼叫。
4)link服務:
link服務主要用於接收客戶端的ws(WebSocket協議)、tcp、udp等協議的連線。
同時呼叫使用者服務進行認證,並投遞連線成功的訊息給位置服務進行消費,儲存連線資訊。
ws(WebSocket協議)過來的訊息先到link再投遞到訊息匯流排。
5)訊息分發服務:
訊息分發服務主要用於接收訊息匯流排推過來的訊息進行處理,按照im內部訊息協議構造好訊息體後,又推送到訊息匯流排中(比如會推給會話服務、訊息盒子、link服務)。
6)位置服務:
儲存link的(WebSocket協議)連線、tcp連線等資訊,並使用redis進行快取(key為userId),方便根據UserId查詢到該使用者所登入的客戶端連線在哪個link上。
一個使用者在相同裝置只能登入一個,但可以支援多端登入。
7)使用者服務:用於儲存所有使用者,提供認證查詢介面。
8)訊息盒子:儲存所有訊息,提供訊息查詢、訊息已讀未讀、訊息未讀數、訊息檢索等功能。
9)會話服務:管理會話、群聊會話、單聊會話等功能。
2.3 整體時序圖
整體架構的時序圖如下:
3、初版IM架構存在的問題及思考
在上節的架構設計介紹中,我們詳細分享了初版IM系統架構的設計思路以及具體流程。
那麼在初版IM架構設計中還存在什麼樣的問題,又該如何優化呢?我們一條條來看看。
3.1 使用MQ訊息匯流排的問題
正如上節所分享的那樣,我們初版IM架構中,link服務到訊息分發服務的訊息使用的MQ訊息匯流排。
初版架構設計中,link服務將訊息下推給訊息分發服務進行處理時,使用的是mq訊息匯流排(通俗了說,IM叢集內不同IM例項間的通訊是依賴於MQ進行的訊息傳遞),而mq訊息匯流排必然做對有一定的時延(而且時延受制於MQ本身的系統實現和技術策略)。
舉個例子:
當兩個處於不同IM例項的客戶端A和B聊天時,A使用者傳送訊息到link --> 訊息匯流排 --> 訊息分發服務 --> 訊息匯流排 --> link --> B使用者。
正如上面這個例子,im訊息投遞流程太長了,並且這樣也會大大降低系統的吞吐量。
3.2 訊息落庫為寫擴散的問題
其實現階段我們使用的是跟微信一樣的寫擴散策略(詳見《企業微信的IM架構設計揭祕:訊息模型、萬人群、已讀回執、訊息撤回等》)。
那麼為啥微信使用寫擴散不是缺陷,而對於我們的IM架構來說確是缺陷呢?
微信的技術特性:
1)微訊號稱沒有儲存使用者的聊天記錄,全是實時推送;
2)微信聊天記錄全部會在我們手機端儲存一份,兩臺手機終端上的聊天記錄並不互通,並且互不可見。
我們的IM綜合訊息中心技術特性:
1)綜合訊息中心是會有拉取歷史聊天記錄(服務端拉取)的功能,儲存了全量訊息;
2)綜合訊息中心的客戶端,需要支援網頁版本。
綜上所述:
1)寫擴散對微信這樣有移動端的富客戶端版本的即時通訊產品十分友好,每個訊息在訊息分發的時候給處於這個會話(單聊,群聊)下的所有使用者所在客戶端先推送訊息,沒找到連線就針對這個使用者寫一個離線快取訊息,那麼下次該使用者登入進來,可以從快取中拉取到該訊息,並且清掉快取;
2)寫擴散對於我們這類通用綜合訊息平臺並不友好,由於接入方大部分是網頁版的客戶端,所以沒有快取訊息的能力,瀏覽器重新整理就沒有了任何訊息,所以需要實時去服務端拉取歷史訊息。假設我是寫擴散,在一個群聊中有五百個使用者,針對這五百個使用者在這個會話,我需要去寫五百條訊息,大大的增加了寫io,並且還不能寫快取(得寫資料庫)。
3.3 tidb存在不穩定性和事務併發的問題
tidb是目前主流的開源分散式資料庫,查詢效率高、無需分庫分表。
但同樣的,tidb存在一些隱藏的問題:
1)tidb在高併發情況下,併發事務會導致事務失敗,具體原因不知;
2)tidb排錯成本高,公司很少有tidb專業運維,經常遇到不走索引的情況。
3.4 群聊、單聊冗餘在同一個服務的問題
在我們初版的IM架構設計中,單聊和群聊是冗餘在會話服務中的,並且冗餘在同一張表的。
其實單聊、群聊從資料角度來說,還是會有些不同(比如業務屬性)雖然都是會話,我們還是需要將這兩個服務拆分開,細粒度的服務拆分能更好的把控整體的邏輯。
4、升級版IM架構
4.1 初始架構問題
正如前面兩節分享的那樣,漸漸的我們發現初版im架構有很大的不足之處。
在生產上暴露出了以下問題:
1)tps沒達到預期,吞吐量不能滿足公司業務的發展;
2)使用的儲存中介軟體難以維護(主要是tidb),試錯成本高,經常在生產暴露問題,並且速度越來越慢;
3)訊息寫擴散沒有太大必要,並大大增加了系統io次數(原因見上一節);
4)一些特性無法支援,比如訊息圖文檢索,訊息已讀未讀。
4.2 升級版im架構介紹
本次升級後的im架構如下圖所示:
如上圖所示,改版後的各模組情況如下:
1)儲存端:儲存端我們改用了mysql,針對訊息服務單獨使用了主從mysql叢集(主節點用於寫訊息、從節點用於訊息檢索)——;
2)mq訊息匯流排:與第一版相比沒有改動;
3)link服務:與第一版相比,改動了link服務到訊息分發服務的訊息推送方式(由MQ匯流排方式變更為tcp實時推送);
4)訊息分發服務:整合了訊息處理能力、路由能力,每臺訊息分發服務擁有所有link服務的tcp連線;
5)單聊服務:負責單聊會話的管理能力;
6)群聊服務:負責群聊會話的管理能力;
7)使用者服務:提供使用者認證,登入\註冊能力。
5、詳細對比針對初版IM架構的改動
升級版的IM架構,對比初始初始,具體主要是下面這些改動。
5.1 改進了不同im例項間的訊息分發方式
針對初版MQ訊息總結的問題,升級版架構中,我們將link到訊息分發服務改為tcp實時連線,百萬客戶端連線同一臺link機器,訊息實時觸達能力tps達到16萬。
link到訊息分發服務的改版是本次設計的亮點之一,完全消除了mq推送的時延性,並且路由簡單,幾乎實時觸達。
舉個例子:(當兩個處於不同IM例項的客戶端A和B聊天時)
1)初版架構中是:A使用者傳送訊息到link --> 訊息匯流排 --> 訊息分發服務 --> 訊息匯流排 --> link --> B使用者;
2)升級版架構是:使用者A --> link --> 訊息分發 --> link --> 使用者B。
而且:link服務到訊息分發服務叢集的訊息推送使用輪詢負載均衡的方式,保證公平,不會導致個別機器負載過高。
5.2 取消了位置服務
取消了位置服務(這裡的位置不是指的IM訊息裡的地理位置訊息哦),訊息分發服務整合位置服務的能力。
訊息分發服務本身業務簡單,不需要再單獨劃分位置服務,因為會增加網路io,並且訊息分發服務直連link,而讓它負責路由則更加方便。
5.3 儲存由tidb改成了mysql
儲存端由tidb改成了mysql,增強了可維護性,訊息服務使用mysql主從讀寫分離方式,提高了訊息落庫速度與檢索速度的同時,也減輕資料庫壓力。
前面有提到過使用tidb這樣維護成本高,排查問題難的分散式資料庫是一件很痛苦的事情。
而我們使用mysql更加穩定,大家對mysql的學習成本相對較低。針對訊息服務使用讀寫分離的方式,能大大提高訊息的吞吐量。
5.4 實現了初版無法實現的特性功能
升級版架構中,我們實現了初版無法實現的特性功能,比如訊息已讀未讀、紅包推送、商品連結推送等功能。
新版綜合訊息中心加入了訊息已讀未讀、傳送紅包、連結推送等功能,但這些功能帶有一定的業務特性,畢竟不是所有Im都需要,可通過配置取消這些功能。
5.5 訊息由寫擴散改為讀擴散
升級版IM架構中,訊息儲存由寫擴散改為了讀擴散。
前面我們有提到寫擴散和讀擴散的利弊,對於網頁端IM我們更適合使用讀擴散,只需要落一條訊息,大大提高訊息服務的吞吐量.
5.6 增加了門面服務
升級版IM架構中,我們增加門面服務 im-logic,用於暴露給第三方業務線介面呼叫。
初版架構中,都是im的各個服務各自暴露介面給到外部進行呼叫, 而升級版架中我們統一使用logic服務暴露給外部呼叫。
在logic服務針對呼叫可以做一些處理,這樣不會影響到整體im的通用,不會增加im底層程式碼的複雜度,從而將業務邏輯與底層進行解耦。
6、優化後的效果對比
針對升級版和初版IM架構,我們也做了一些對比測試,具體的測試過程就是詳細展開了。
以下是測試結果:
7、業務線接入im綜合訊息系統的業務劃分思考
7.1 到底該如何設計高效能通用im綜合訊息系統
關於業務線接入im綜合訊息系統的業務劃分,我也做了一些總結和思考,為了更形象和易於理解,我這裡以客服系統以及企業微信為例來進行分析。
假如我開發了一款通用的im綜合訊息系統,現在有很多業務方需要接入我們,我們該如何進行業務域的清晰劃分就顯得尤為重要,需要在妥協與不妥協中進行平衡。
就像當前市面上開源的im訊息平臺來說,存在的問題主要是:要麼是整合了很多的業務邏輯,要麼就只是一款單純的客服系統,再或者就是一款IM好友聊天系統,中間的業務劃分並不明確。當然,這也有好處,拿來就能用,並不需要進行二次業務封裝。
那麼,到底如何將im設計為一款真正的高效能通用im綜合訊息系統呢?
通用的綜合訊息訊息平臺只需要有通用的底層能力:
以下案例假設在我已經按照上述架構設計了一版im綜合訊息中心。
7.2 以客服系統為例
客服系統:
客服系統不光需要實現自身業務,還需要整合im的訊息能力(消費im的訊息),來進行場景分析,實現會話變更、信令訊息推送等邏輯。
客服系統內部需要根據im的底層支援能力進行相應的業務封裝以及客服系統的客服使用者池,c端使用者池如何初始化到im的使用者中心這些問題都是需要考慮進去的。
7.3 內部OA通訊為例
內部OA通訊:
員工內部OA通訊系統需要整合IM好友功能,需要根據im的使用者中心封裝組織架構,使用者許可權等功能。
同時,內部通訊系統需要根據im實現訊息已讀未讀,群聊列表,會話列表拉取等功能。
8、本文小結
im的綜合訊息平臺是一款需要高度結合業務的中介軟體系統,它直接與業務打交道,跟普通的中介軟體有根本的區別。
一款好用的im綜合訊息平臺,直接取決於你的通用性,可擴充套件性以及系統吞吐能力。
希望這篇文章所分享的內容,能對大家開發im時候的思路有所啟迪。
9、參考資料
[1] 從零到卓越:京東客服即時通訊系統的技術架構演進歷程
[2] 從游擊隊到正規軍(一):馬蜂窩旅遊網的IM系統架構演進之路
[3] 瓜子IM智慧客服系統的資料架構設計(整理自現場演講,有配套PPT)
[4] 阿里釘釘技術分享:企業級IM王者——釘釘在後端架構上的過人之處
[5] 新手入門一篇就夠:從零開發移動端IM
[6] 零基礎IM開發入門(一):什麼是IM系統?
[7] 基於實踐:一套百萬訊息量小規模IM系統技術要點總結
[8] 一套億級使用者的IM架構技術乾貨(上篇):整體架構、服務拆分等
[9] 一套億級使用者的IM架構技術乾貨(下篇):可靠性、有序性、弱網優化等
[10] 從新手到專家:如何設計一套億級訊息量的分散式IM系統
[11] 企業微信的IM架構設計揭祕:訊息模型、萬人群、已讀回執、訊息撤回等
[12] 阿里IM技術分享(三):閒魚億級IM訊息系統的架構演進之路
[13] 一套高可用、易伸縮、高併發的IM群聊、單聊架構方案設計實踐
(本文已同步釋出於:http://www.52im.net/thread-39...)