好消息:IM1.0.0版本已經(jīng)上線啦,支持特性:
* 私聊發(fā)送文本/文件
* 已發(fā)送/已送達(dá)/已讀回執(zhí)
* 支持使用ldap登錄
* 支持接入外部的登錄認(rèn)證系統(tǒng)
* 提供客戶端jar包,方便客戶端開(kāi)發(fā)
github鏈接: https://github.com/yuanrw/IM <https://github.com/yuanrw/IM>
前言
首先講講IM(即時(shí)通訊)技術(shù)可以用來(lái)做什么:
聊天:qq、微信
直播:斗魚直播、抖音
實(shí)時(shí)位置共享、游戲多人互動(dòng)等等
可以說(shuō)幾乎所有高實(shí)時(shí)性的應(yīng)用場(chǎng)景都需要用到IM技術(shù)。
本篇將帶大家從零開(kāi)始搭建一個(gè)輕量級(jí)的IM服務(wù)端,麻雀雖小,五臟俱全,我們搭建的IM服務(wù)端實(shí)現(xiàn)以下功能:
* 一對(duì)一的文本消息、文件消息通信
* 每個(gè)消息有“已發(fā)送”/“已送達(dá)”/“已讀”回執(zhí)
* 存儲(chǔ)離線消息
* 支持用戶登錄,好友關(guān)系等基本功能。
* 能夠方便地水平擴(kuò)展
通過(guò)這個(gè)項(xiàng)目能學(xué)到什么?
這個(gè)項(xiàng)目涵蓋了很多后端必備知識(shí):
* rpc通信
* 數(shù)據(jù)庫(kù)
* 緩存
* 消息隊(duì)列
* 分布式、高并發(fā)的架構(gòu)設(shè)計(jì)
* docker部署
消息通信
文本消息
我們先從最簡(jiǎn)單的特性開(kāi)始實(shí)現(xiàn):一個(gè)普通消息的發(fā)送
消息格式如下:
message ChatMsg{ id = 1; //消息id fromId = Alice //發(fā)送者userId destId = Bob
//接收者userId msgBody = hello //消息體 }
如上圖,我們現(xiàn)在有兩個(gè)用戶:Alice和Bob連接到了服務(wù)器,當(dāng)Alice發(fā)送消息message(hello)
給Bob,服務(wù)端接收到消息,根據(jù)消息的destId進(jìn)行轉(zhuǎn)發(fā),轉(zhuǎn)發(fā)給Bob。
發(fā)送回執(zhí)
那我們要怎么來(lái)實(shí)現(xiàn)回執(zhí)的發(fā)送呢?
我們定義一種回執(zhí)數(shù)據(jù)格式ACK,MsgType有三種,分別是sent(已發(fā)送),delivered(已送達(dá)), read(已讀):
message AckMsg { id; //消息id fromId; //發(fā)送者id destId; //接收者id msgType; //消息類型
ackMsgId; //確認(rèn)的消息id } enum MsgType { DELIVERED; READ; }
當(dāng)服務(wù)端接受到Alice發(fā)來(lái)的消息時(shí):
* 向Alice發(fā)送一個(gè)sent(hello)表示消息已經(jīng)被發(fā)送到服務(wù)器。 message AckMsg { id = 2; fromId =
Alice; destId = Bob; msgType = SENT; ackMsgId = 1; }
* 服務(wù)器把hello轉(zhuǎn)發(fā)給Bob后,立刻向Alice發(fā)送delivered(hello)表示消息已經(jīng)發(fā)送給Bob。 message AckMsg {
id = 3; fromId = Bob; destId = Alice; msgType = DELIVERED; ackMsgId = 1; }
* Bob閱讀消息后,客戶端向服務(wù)器發(fā)送read(hello)表示消息已讀 message AckMsg { id = 4; fromId = Bob;
destId = Alice; msgType = READ; ackMsgId = 1; }
這個(gè)消息會(huì)像一個(gè)普通聊天消息一樣被服務(wù)器處理,最終發(fā)送給Alice。
在服務(wù)器這里不區(qū)分ChatMsg和AckMsg,處理過(guò)程都是一樣的:解析消息的destId并進(jìn)行轉(zhuǎn)發(fā)。
水平擴(kuò)展
當(dāng)用戶量越來(lái)越大,必然需要增加服務(wù)器的數(shù)量,用戶的連接被分散在不同的機(jī)器上。此時(shí),就需要存儲(chǔ)用戶連接在哪臺(tái)機(jī)器上。
我們引入一個(gè)新的模塊來(lái)管理用戶的連接信息。
管理用戶狀態(tài)
模塊叫做user status,共有三個(gè)接口:
public interface UserStatusService { /** * 用戶上線,存儲(chǔ)userId與機(jī)器id的關(guān)系 * * @param
userId * @param connectorId * @return 如果當(dāng)前用戶在線,則返回他連接的機(jī)器id,否則返回null */ String
online(String userId, String connectorId); /** * 用戶下線 * * @param userId */ void
offline(String userId); /** * 通過(guò)用戶id查找他當(dāng)前連接的機(jī)器id * * @param userId * @return */
String getConnectorId(String userId); }
這樣我們就能夠?qū)τ脩暨B接狀態(tài)進(jìn)行管理了,具體的實(shí)現(xiàn)應(yīng)考慮服務(wù)的用戶量、期望性能等進(jìn)行實(shí)現(xiàn)。
此處我們使用redis來(lái)實(shí)現(xiàn),將userId和connectorId的關(guān)系以key-value的形式存儲(chǔ)。
消息轉(zhuǎn)發(fā)
除此之外,還需要一個(gè)模塊在不同的機(jī)器上轉(zhuǎn)發(fā)消息,如下結(jié)構(gòu):
此時(shí)我們的服務(wù)被拆分成了connector和transfer兩個(gè)模塊,connector模塊用于維持用戶的長(zhǎng)鏈接,而transfer的作用是將消息在多個(gè)
connector之間轉(zhuǎn)發(fā)。
現(xiàn)在Alice和Bob連接到了兩臺(tái)connector上,那么消息要如何傳遞呢?
* Alice上線,連接到機(jī)器[1]上時(shí)
* 將Alice和它的連接存入內(nèi)存中。
* 調(diào)用user status的online方法記錄Alice上線。
* Alice發(fā)送了一條消息給Bob
* 機(jī)器[1]收到消息后,解析destId,在內(nèi)存中查找是否有Bob。
* 如果沒(méi)有,代表Bob未連接到這臺(tái)機(jī)器,則轉(zhuǎn)發(fā)給transfer。
* transfer調(diào)用user status的getConnectorId(Bob)方法找到Bob所連接的connector,返回機(jī)器[2],則轉(zhuǎn)發(fā)給
機(jī)器[2]。
流程圖:
總結(jié):
* 引入user status模塊管理用戶連接,transfer模塊在不同的機(jī)器之間轉(zhuǎn)發(fā),使服務(wù)可以水平擴(kuò)展。
* 為了滿足實(shí)時(shí)轉(zhuǎn)發(fā),transfer需要和每臺(tái)connector機(jī)器都保持長(zhǎng)鏈接。
離線消息
如果用戶當(dāng)前不在線,就必須把消息持久化下來(lái),等待用戶下次上線再推送,這里使用mysql存儲(chǔ)離線消息。
為了方便地水平擴(kuò)展,我們使用消息隊(duì)列進(jìn)行解耦。
* transfer接收到消息后如果發(fā)現(xiàn)用戶不在線,就發(fā)送給消息隊(duì)列入庫(kù)。
* 用戶登錄時(shí),服務(wù)器從庫(kù)里拉取離線消息進(jìn)行推送。
用戶登錄、好友關(guān)系
用戶的注冊(cè)登錄、賬戶管理、好友關(guān)系鏈等功能更適合使用http協(xié)議,因此我們將這個(gè)模塊做成一個(gè)restful服務(wù),對(duì)外暴露http接口供客戶端調(diào)用。
至此服務(wù)端的基本架構(gòu)就完成了:
總結(jié)
以上就是這篇博客的所有內(nèi)容,本篇幫大家構(gòu)建了IM服務(wù)端的架構(gòu),但還有很多細(xì)節(jié)需要我們?nèi)ニ伎?,例如?br>
* 如何保證消息的順序和唯一
* 多個(gè)設(shè)備在線如何保證消息一致性
* 如何處理消息發(fā)送失敗
* 消息的安全性
* 如果要存儲(chǔ)聊天記錄要怎么做
* 數(shù)據(jù)庫(kù)分表分庫(kù)
* 服務(wù)高可用
……
更多細(xì)節(jié)實(shí)現(xiàn)就留到下一篇啦~
IM1.0.0版本已上線,github鏈接:
https://github.com/yuanrw/IM <https://github.com/yuanrw/IM>
覺(jué)得對(duì)你有幫助請(qǐng)點(diǎn)個(gè)star吧~!
熱門工具 換一換
