如何構(gòu)建IM系統(tǒng)(摘選)

需求分析

技術(shù)需求

技術(shù)需求分為分布式架構(gòu)技術(shù)需求和IM技術(shù)需求

分布式架構(gòu)技術(shù)需求

高可用:IM server花式宕機(jī)、分布式Job

高性能:單節(jié)點(diǎn)至少10萬級長連接(c100k)和毫秒級響應(yīng)速度

擴(kuò)展性:可以自由擴(kuò)展計算和存儲能力

監(jiān)控:IM服務(wù)具備自監(jiān)控能力覆醇,降低對外監(jiān)控服務(wù)的依賴

易運(yùn)維

IM技術(shù)需求

統(tǒng)一的websocket技術(shù)選型:兼容局限于7層協(xié)議web終端永脓,7層相對4層的性能損耗也不是很大常摧。

在線用戶狀態(tài):在線終端與IM server節(jié)點(diǎn)的websocket長連接對應(yīng)關(guān)系的狀態(tài)維護(hù)

圖片壓縮技術(shù)

消息持久化存儲

消息序列化和反序列化

im管理后臺

功能需求

消息可靠性落午、即時性:不丟消息肚豺、毫秒觸達(dá)详炬。

通信加密

消息存儲加密

文件發(fā)送和存儲

“正在輸入”狀態(tài)

“消息撤回”

“忙碌”等狀態(tài)

群組

用戶關(guān)系(可見性)

消息類型(文字呛谜、音頻隐岛、視頻瓷翻、圖片齐帚、地理位置对妄、自定義類型)

消息評論(類似飛書)

消息待辦

歷史消息

聊天室

多終端同時登陸

音視頻通話

企業(yè)im組織架構(gòu)管理

集團(tuán)im多組織隔離

互聯(lián)網(wǎng)im多租戶

多級管理員(系統(tǒng)管理員剪菱、租戶管理員、組織管理員蚓哩、部門管理員)

本地賬密認(rèn)證和第三方賬密認(rèn)證

架構(gòu)分析

技術(shù)選型

語言方面依然是Java8

花式宕機(jī)

vip虛擬ip+dns輪詢岸梨,實現(xiàn)負(fù)載器集群的高可用

多NGINX/負(fù)載均衡器(我個人認(rèn)為根本沒必要花錢去使用商業(yè)版的負(fù)載均衡器盛嘿,NGINX集群真的完全完全充足夠用括袒!人傻錢多才去買F5等昂貴的負(fù)載器)

去注冊中心的raft+gossip,這樣可以降低運(yùn)維成本芥炭,因為不再需要維護(hù)一個注冊中心集群(如zookeeper园蝠、etcd等)

分布式j(luò)ob(我們計劃參考Raft+Gossip算法來實現(xiàn)一套分布式調(diào)度任務(wù)彪薛,摒棄使用任何第三方的job調(diào)度器)

chaos monkey(這個名字很有名的善延,搞應(yīng)用架構(gòu)的肯定都知道易遣,有了它豆茫,程序想不健壯都不行了)

高性能

基于Netty的websocket server

事實上netty被證明單節(jié)點(diǎn)可以穩(wěn)定hold住數(shù)十萬連接揩魂。

業(yè)務(wù)層使用非阻塞的reactive框架來開發(fā)

充分利用非阻塞的線程威力

我們準(zhǔn)備采用我們非常熟悉的rxjava2來作為開發(fā)框架

高性能netty網(wǎng)絡(luò)框架實現(xiàn)http API server

我們計劃基于netty網(wǎng)絡(luò)框架火脉,采用http1.1無狀態(tài)短連接忘分。

?servlet http server(spring mvc、spring boot重斑、單純的servlet容器)

?netty http server 他們之間的性能差異,我這里就不描述了肯骇,懂的人自然懂。

API server安全接入與授權(quán)

?OAuth2.0?授權(quán)碼模式(Authorization Code)笛丙、密碼模式(password)漾脂、?Client模式(Client Credentials)(OAuth2.0需要redis集中存儲狀態(tài)數(shù)據(jù))

?JWT 一次性授權(quán)票據(jù)、輕應(yīng)用/小程序用完即走場景坦冠,服務(wù)端不需要集中存儲session,是完全無狀態(tài)的判呕。

dao層不再使用基于jdbc的框架

dao層我們選型使用reactive postgresql

不再使用jdbc這種阻塞業(yè)務(wù)線程直到數(shù)據(jù)庫返回結(jié)果的線程模型,什么mybatis梦抢、hibernate哼蛆、jooq一律不在考慮范圍內(nèi)腮介! 如果不基于jdbc,那么還需要提供一套自有的orm了,我們這邊也有一定的技術(shù)積累可以復(fù)用抵代。

高性能IM通信協(xié)議

字符串?dāng)?shù)據(jù)的序列化和反序列化,我們暫時使用熟悉的fastjson

即時通訊網(wǎng)絡(luò)協(xié)議,如上文統(tǒng)一使用websocket應(yīng)用層長連接

消息體payload的格式規(guī)范詳見接口規(guī)范文檔

websocket長連接中的消息ack協(xié)議:

我們不是單純地把消息payload丟到長連接上去就完了

我們還需要一套ack機(jī)制來保證消息是已經(jīng)被終端收到晦嵌。

我們還需要一套ack機(jī)制來保終端消息是已經(jīng)被服務(wù)器持久化到數(shù)據(jù)庫中了。

消息去重機(jī)制/協(xié)議:message id協(xié)議。

在確定的IM通信協(xié)議前提下伴挚,我們要提供一套完善的全方位的壓測客戶端,以確定各種規(guī)模的服務(wù)端配置能給出多高的極限并發(fā)量田弥。

擴(kuò)展性

無狀態(tài)的im應(yīng)用實例

上文提到我們基于raft+gossip去中心化的服務(wù)治理方案,我們的服務(wù)端im-server應(yīng)用擴(kuò)展直接通過增加服務(wù)器增加應(yīng)用實例數(shù)量來實現(xiàn)計算能力擴(kuò)展。

如何克服關(guān)系型數(shù)據(jù)庫存儲的瓶頸問題

使用數(shù)據(jù)庫中件(這個方案由于維護(hù)成本太高而放棄)

使用云廠商的數(shù)據(jù)庫云服務(wù)postgresql(這個視情況而定)

使用分布式關(guān)系型數(shù)據(jù)庫

基于上文提到的reactive-dao的選型,我們選型的是postgresql型數(shù)據(jù)庫

決定采用開源的cockroachdb十绑,是由于它是完整的postgresql數(shù)據(jù)庫協(xié)議的實現(xiàn)晚岭,而且支持完美的寫擴(kuò)展能力,開源和高可用的支持片择,基于postgresql生態(tài),云原生的支持,不依賴某個特定的云廠商硫戈。

自監(jiān)控

這里為什么要提自監(jiān)控呢?主要是因為我們想要實現(xiàn)一款解耦對特定監(jiān)控系統(tǒng)的依賴關(guān)系。 設(shè)計思路是:

實現(xiàn)一套通用監(jiān)控數(shù)據(jù)的metric接口,將IM服務(wù)器的狀態(tài)全部提供出來

任何外部的監(jiān)控系統(tǒng)如果要與之對接琢感,則再實現(xiàn)一個適配器對接二方數(shù)據(jù)接口即可。

易維護(hù)性

如果要降低im集群的維護(hù)難度,原則就是:

盡量避免或少使用外部中間件和多重數(shù)據(jù)庫種類。

我們見過很多系統(tǒng)撤防,為了性能和為了功能豐富无牵,MySQL主從集群克懊、redis集群、MongoDB集群扮念、NGINX輪詢谈为、zookeeper集群粘茄、fastdfs集群吠架、kafka/rabbitMQ集群磺平,等等,一堆一堆地用赊舶。這運(yùn)維的酸爽感舔痪,你們不搞運(yùn)維的體會不了。

傻瓜式部署

你的軟件,它如果是可以一鍵傻瓜運(yùn)行、可實施性特別簡單傻瓜是不是接受度會更高?特別是是在小型企業(yè)內(nèi)部斋枢,以及學(xué)術(shù)技術(shù)研究方面涩赢。

多環(huán)境支持

Linux、windows澄步、Mac OS

docker或者更進(jìn)一步kubernetes

IM相關(guān)技術(shù)實現(xiàn)

session長連接

統(tǒng)一的websocket技術(shù)選型:兼容局限于7層協(xié)議web終端,7層相對4層的性能損耗也不是很大。

技術(shù)選型如上文,我們使用netty來實現(xiàn)websocket server

在線用戶狀態(tài)

海量在線用戶終端是與多個IM server建立著長連接的,我們向終端推送消息必須定位到是哪個im server的哪個websocket連接。如果有一個簡單而搞笑的定位算法那就是極好的。一般我們首先想到的是使用redis等緩存技術(shù)去集中存儲這些狀態(tài)。但是,我們是否有更優(yōu)秀的方案呢嗜浮?

hash slot算法

raft一致性算法 步驟如下:

我們通過raft一致性算法得到IM server節(jié)點(diǎn)列表

我們通過hash slot算法可以快速高效地得到用戶對應(yīng)的IM server節(jié)點(diǎn),由于一致性哈希算法的一致性保證,我們可以保證每次得到的都是一致的結(jié)果,即我們可以保證每次定位到100%相同的長連接所在的節(jié)點(diǎn)。

hash slot結(jié)合raft一致性算法撮胧,支持花式宕機(jī)后session長連接動態(tài)調(diào)整到一致性哈希對應(yīng)的落點(diǎn)锻离,節(jié)點(diǎn)越多需要新建的session長連接越少疏虫;同理啤呼,我們擴(kuò)展im計算能力卧秘,新增session節(jié)點(diǎn)后,部分存量session長連接需要斷開并與新節(jié)點(diǎn)建立的長連接數(shù)量越少官扣。

問題來了:raft腦裂問題

腦裂問題一直是一個經(jīng)典問題翅敌,我只能說你們把機(jī)房基礎(chǔ)網(wǎng)絡(luò)做穩(wěn)了,這是前提惕蹄。

跨機(jī)房集群蚯涮,機(jī)房間網(wǎng)絡(luò)斷了,我們又該怎么整呢卖陵? 0. 首先國內(nèi)就沒有幾家企業(yè)有機(jī)會讓他們的IM達(dá)到跨機(jī)房部署應(yīng)用集群的條件遭顶。如果達(dá)到了,我們繼續(xù)往下看泪蔫。

不知道大家有沒有玩過elasticsearch棒旗?

elasticsearch有一個minimum_master_nodes參數(shù),用來規(guī)避腦裂時被隔離的小集群內(nèi)某個節(jié)點(diǎn)被提升為leader撩荣。所以你們的懂的铣揉,這里不再多說了饶深。

圖片壓縮技術(shù)

先空著

消息持久化存儲

上文已經(jīng)提到了,我們基于最單純數(shù)據(jù)庫和中間件選型原則逛拱,選用最少的數(shù)量類型的數(shù)據(jù)庫技術(shù): 目前市面上最合適的產(chǎn)品經(jīng)過實踐評估即是cockroachdb敌厘,即小強(qiáng)db,云原生的異地多活分布式關(guān)系型數(shù)據(jù)庫解決方案朽合。 之所以是cockroachdb:

上文已經(jīng)講到過postgresql的reactive驅(qū)動的生態(tài)俱两。

跟tidb一樣,參考Google f1論文理論基礎(chǔ)旁舰,對標(biāo)Google spanner數(shù)據(jù)庫的產(chǎn)品锋华,但是比tidb更簡潔的運(yùn)維+云原生嗡官;(我憑什么說比tidb運(yùn)維更簡單呢箭窜?你們自己玩一下這兩個數(shù)據(jù)庫就知道了。)

我心目中的明星協(xié)議:一致性協(xié)議raft

完全開源

消息序列化和反序列化

有人說Protobuf通信協(xié)議是最快的序列化和傳輸方案衍腥。但是protobuf不是最通用的序列化方案磺樱。考慮到我們對json的熟悉程度更高于protobuf婆咸,而且基于字符串的序列化性能竹捉,json不比protobuf慢。而IM傳輸?shù)膒ayload不就是文本嘛尚骄?有興趣可以參考這篇關(guān)于protobuf和json性能對比的文章

所以块差,我們固執(zhí)地選擇fastjson作為序列化和反序列化方案

im管理后臺

管理功能微服務(wù)化

即后臺界面與管理接口配套的微服務(wù)化方案。說白了就是復(fù)用倔丈,就是將來誰想要復(fù)用我們的局部服務(wù)能力憨闰,我可以把它摳出去給他復(fù)用。

考慮到微服務(wù)化需五,我們需要將im核心進(jìn)行細(xì)粒度的拆分實現(xiàn)鹉动。im核心對應(yīng)的各個功能模塊必然對應(yīng)各個管理接口,管理接口對應(yīng)著不同的管理后臺頁面宏邮。我們的設(shè)計會將這些頁面也進(jìn)行微服務(wù)化分離管理泽示。聰明的您理解得肯定沒錯,我們將會使用多站點(diǎn)方案:

單點(diǎn)登錄蜜氨,身份認(rèn)證

集中權(quán)限管理機(jī)制:權(quán)限注冊中心+權(quán)限單元自治械筛。有興趣的你可以參考阿里云RAM和騰訊云ACM。

一沉不變的前后端分離方案

有些老牌的后端開發(fā)工程師喜歡用模板框架飒炎,比如很有名的freemarker埋哟,Velocity。但是這些東東真的被證明是拖慢前后端開發(fā)者進(jìn)行協(xié)作開發(fā)的“利器”厌丑。當(dāng)然定欧,你們團(tuán)隊都是后端工程師出身的“全椨婧牵”小伙子的情況除外。

前端技術(shù)選型呢砍鸠,交給我們的前端小伙子去決定吧扩氢,后端模板技術(shù),我們是不會選擇的爷辱。

后端選型

既然是多站點(diǎn)录豺,那么是需要提供單點(diǎn)登錄能力的,這里我們將抽象一層單點(diǎn)登錄客戶端饭弓,去對接統(tǒng)一單點(diǎn)登錄服務(wù)SSO/內(nèi)置的簡單的SSO服務(wù)

將來考慮支持移動B端管理能力双饥,同樣也是需要集成SSO的。

后端API服務(wù)能力弟断,上文已經(jīng)提到咏花,我們使用netty實現(xiàn)API server,什么spring mvc阀趴、springboot都一邊站吧昏翰。

api doc:因為是前后端分離所以簡潔清晰的API文檔是必不可少的,不然你讓你的前端基友讀你的Java源碼來理解API接口該如何調(diào)用嘛刘急?而且API文檔最好是“半自動化”的棚菊,“文檔代碼一致性”的。

IM功能性設(shè)計

消息發(fā)送簡要流程

發(fā)送方終端 --> 服務(wù)端 --> 數(shù)據(jù)庫 --入庫成功ack--> 發(fā)送方終端?

--> 收方終端 --成功接收ack--> 服務(wù)端 --更新狀態(tài)已發(fā)送--> 數(shù)據(jù)庫?

(是不是感覺這個不清晰叔汁,我們可以)

消息可靠性统求,不丟消息

我們采用經(jīng)典的ack和message id去重機(jī)制

ack保證消息不丟失,關(guān)于ack我們在上文中已經(jīng)簡單地提到過据块,它屬于websocket session會話通信協(xié)議的一部分:

發(fā)送方終端-->服務(wù)端-->持久化成功--ack-->發(fā)送方終端

服務(wù)端--推送-->接收端終端--接收并ack-->服務(wù)端

message id去重機(jī)制保證

服務(wù)端消息僅入庫存儲唯一一條码邻,我們將message id在數(shù)據(jù)庫設(shè)置唯一性約束。

終端接收消息后推送瑰钮,都會根據(jù)id去重冒滩,保證UI只呈現(xiàn)那唯一一條消息。

message id由im-sdk生成(消息推送restful接口除外)

messageId結(jié)構(gòu)規(guī)范定義:版本號 + 發(fā)送方id(比如userId)+ 終端id + 時間戳 + 隨機(jī)數(shù)

消息即時性

消息即時性浪谴,其實就是IM服務(wù)性能可靠保證前提和網(wǎng)絡(luò)通暢的前提

性能保證:上文已經(jīng)提到开睡,監(jiān)控和彈性是保證IM服務(wù)可靠性的關(guān)鍵。

網(wǎng)絡(luò)可靠性:網(wǎng)絡(luò)可靠性還是相對依賴網(wǎng)絡(luò)基礎(chǔ)設(shè)施了苟耻,應(yīng)用層會輔助對應(yīng)用流量進(jìn)行監(jiān)控篇恒,以降低對網(wǎng)絡(luò)監(jiān)控的強(qiáng)依賴。

消息發(fā)送流程圖

消息發(fā)送

消息推送

終端消息拉取

通信加密

什么樣的IM才能讓領(lǐng)導(dǎo)用著放心凶杖?

聊天消息在老板自己的機(jī)房里面存著胁艰,別人無權(quán)獲取,也無權(quán)進(jìn)行監(jiān)控,更不談對你的隱私進(jìn)行窺探和大數(shù)據(jù)分析了

聊天的內(nèi)容通過互聯(lián)網(wǎng)傳輸時腾么,別人難以截聽奈梳,即使截聽也難以破解

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市解虱,隨后出現(xiàn)的幾起案子攘须,更是在濱河造成了極大的恐慌,老刑警劉巖殴泰,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件于宙,死亡現(xiàn)場離奇詭異,居然都是意外死亡悍汛,警方通過查閱死者的電腦和手機(jī)捞魁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來离咐,“玉大人谱俭,你說我怎么就攤上這事〗∨” “怎么了旺上?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長糖埋。 經(jīng)常有香客問我,道長窃这,這世上最難降的妖魔是什么瞳别? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮杭攻,結(jié)果婚禮上祟敛,老公的妹妹穿的比我還像新娘。我一直安慰自己兆解,他們只是感情好馆铁,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著锅睛,像睡著了一般埠巨。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上现拒,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天辣垒,我揣著相機(jī)與錄音,去河邊找鬼印蔬。 笑死勋桶,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播例驹,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼捐韩,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了鹃锈?” 一聲冷哼從身側(cè)響起奥帘,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎仪召,沒想到半個月后寨蹋,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡扔茅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年已旧,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片召娜。...
    茶點(diǎn)故事閱讀 39,953評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡运褪,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出玖瘸,到底是詐尸還是另有隱情秸讹,我是刑警寧澤,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布雅倒,位于F島的核電站璃诀,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏蔑匣。R本人自食惡果不足惜劣欢,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望裁良。 院中可真熱鬧凿将,春花似錦、人聲如沸价脾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽侨把。三九已至犀变,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間座硕,已是汗流浹背弛作。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工伊约, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留专酗,地道東北人。 一個月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓纽谒,卻偏偏與公主長得像,于是被迫代替她去往敵國和親萨西。 傳聞我的和親對象是個殘疾皇子有鹿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評論 2 355

推薦閱讀更多精彩內(nèi)容