好消息:IM1.0.0版本已經上線啦,支持特性:mysql
首先講講IM(即時通信)技術能夠用來作什麼:
聊天:qq、微信
直播:鬥魚直播、抖音
實時位置共享、遊戲多人互動等等
能夠說幾乎全部高實時性的應用場景都須要用到IM技術。git
本篇將帶你們從零開始搭建一個輕量級的IM服務端,麻雀雖小,五臟俱全,咱們搭建的IM服務端實現如下功能:github
這個項目涵蓋了不少後端必備知識:redis
咱們先從最簡單的特性開始實現:一個普通消息的發送
消息格式以下:sql
message ChatMsg{ id = 1; //消息id fromId = Alice //發送者userId destId = Bob //接收者userId msgBody = hello //消息體 }
如上圖,咱們如今有兩個用戶:Alice和Bob鏈接到了服務器,當Alice發送消息message(hello)
給Bob,服務端接收到消息,根據消息的destId進行轉發,轉發給Bob。docker
那咱們要怎麼來實現回執的發送呢?
咱們定義一種回執數據格式ACK,MsgType有三種,分別是sent
(已發送),delivered
(已送達), read
(已讀):數據庫
message AckMsg { id; //消息id fromId; //發送者id destId; //接收者id msgType; //消息類型 ackMsgId; //確認的消息id } enum MsgType { DELIVERED; READ; }
當服務端接受到Alice發來的消息時:後端
sent(hello)
表示消息已經被髮送到服務器。message AckMsg { id = 2; fromId = Bob; destId = Alice; msgType = SENT; ackMsgId = 1; }
hello
轉發給Bob後,馬上向Alice發送delivered(hello)
表示消息已經發送給Bob。message AckMsg { id = 3; fromId = Bob; destId = Alice; msgType = DELIVERED; ackMsgId = 1; }
read(hello)
表示消息已讀message AckMsg { id = 4; fromId = Bob; destId = Alice; msgType = READ; ackMsgId = 1; }
這個消息會像一個普通聊天消息同樣被服務器處理,最終發送給Alice。
緩存
在服務器這裏不區分ChatMsg
和AckMsg
,處理過程都是同樣的:解析消息的destId
並進行轉發。安全
當用戶量愈來愈大,必然須要增長服務器的數量,用戶的鏈接被分散在不一樣的機器上。此時,就須要存儲用戶鏈接在哪臺機器上。
咱們引入一個新的模塊來管理用戶的鏈接信息。
模塊叫作user status
,共有三個接口:
public interface UserStatusService { /** * 用戶上線,存儲userId與機器id的關係 * * @param userId * @param connectorId * @return 若是當前用戶在線,則返回他鏈接的機器id,不然返回null */ String online(String userId, String connectorId); /** * 用戶下線 * * @param userId */ void offline(String userId); /** * 經過用戶id查找他當前鏈接的機器id * * @param userId * @return */ String getConnectorId(String userId); }
這樣咱們就可以對用戶鏈接狀態進行管理了,具體的實現應考慮服務的用戶量、指望性能等進行實現。
此處咱們使用redis來實現,將userId和connectorId的關係以key-value的形式存儲。
除此以外,還須要一個模塊在不一樣的機器上轉發消息,以下結構:
此時咱們的服務被拆分紅了connector
和transfer
兩個模塊,connector
模塊用於維持用戶的長連接,而transfer
的做用是將消息在多個connector
之間轉發。
如今Alice和Bob鏈接到了兩臺connector上,那麼消息要如何傳遞呢?
機器[1]
上時
user status
的online
方法記錄Alice上線。機器[1]
收到消息後,解析destId,在內存中查找是否有Bob。transfer
。transfer
調用user status
的getConnectorId(Bob)
方法找到Bob所鏈接的connector,返回機器[2]
,則轉發給機器[2]
。流程圖:
user status
模塊管理用戶鏈接,transfer
模塊在不一樣的機器之間轉發,使服務能夠水平擴展。transfer
須要和每臺connector
機器都保持長連接。若是用戶當前不在線,就必須把消息持久化下來,等待用戶下次上線再推送,這裏使用mysql存儲離線消息。
爲了方便地水平擴展,咱們使用消息隊列進行解耦。
transfer
接收到消息後若是發現用戶不在線,就發送給消息隊列入庫。用戶的註冊登陸、帳戶管理、好友關係鏈等功能更適合使用http協議,所以咱們將這個模塊作成一個restful服務,對外暴露http接口供客戶端調用。
至此服務端的基本架構就完成了:
以上就是這篇博客的全部內容,本篇幫你們構建了IM服務端的架構,但還有不少細節須要咱們去思考,例如:
更多細節實現就留到下一篇啦~
IM1.0.0版本已上線,github連接:
https://github.com/yuanrw/IM
以爲對你有幫助請點個star吧~!