<ul id="qxxfc"><fieldset id="qxxfc"><tr id="qxxfc"></tr></fieldset></ul>


      好消息: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吧~!


      友情鏈接
      ioDraw流程圖
      API參考文檔
      OK工具箱
      云服務(wù)器優(yōu)惠
      阿里云優(yōu)惠券
      騰訊云優(yōu)惠券
      京東云優(yōu)惠券
      站點(diǎn)信息
      問(wèn)題反饋
      郵箱:[email protected]
      QQ群:637538335
      關(guān)注微信

        <ul id="qxxfc"><fieldset id="qxxfc"><tr id="qxxfc"></tr></fieldset></ul>
          99久热re在线精品视频大全 | 狠狠狠狠狠狠狠狠 | 色噜噜狠狠永久免费 | 国产黄色视频在线播放 | 国产免费久久久久 | 天天日天天添 | 国产精品18禁 | www.婷婷五月天 | 日本午夜视频 | 亚洲影视大全 |