gobelieveio代碼拆分和分析

最近做im調研,記錄一下筆記。
拆分了代碼,使其可以在windows下運行
(圖是掛了,可以移步github查看) im_servers


特色

  • 自帶 android 、 ios 、 web 的 sdk
  • 單臺50w併發,3000條/s消息發送量(32g,16核),支持服務器的超大集羣。
  • 支持點對點消息, 羣組消息, 聊天室消息
  • 支持超大羣組(3000人)
  • 與終端交互靠自定義協議,傳輸體小
  • 敏感詞過濾
  • 支持持久化和非持久化消息。(目前僅客服消息支持非持久化)
  • 可調用api查看im系統使用情況

github地址

項目介紹

分爲三個服務:

im 終端服務(可分佈式部署,暫無負載均衡模塊)

imr 路由服務(主要解決 im 分佈式部署的問題)

ims 存儲服務(主從部署)

  1. im 與終端相連,將鏈接、路由信息、用戶、組、聊天室等存儲在內存中。

     accept 收到一個連接
     開啓寫線程和讀線程
     寫線程:監聽 client.wt 阻塞隊列,一有數據就寫入 conn
     讀線程:按照數據包協議從 conn 讀出數據包,由 client.HandleMessage 處理 分發給 imr 處理
    
  2. imr 將消息交付給對應的 im 處理, 解決 im 分佈式部署

  3. ims 存儲服務 (主從服務),存儲介質爲文件、同時創建消息索引

其中對超級羣做了單獨處理,如果配置了超級羣存儲節點、信息單獨存儲

客戶端消息發到 IM 服務器之後,IM服務器會給接收客戶端發送 MSG_SYNC_NOTIFY ,然後讓客戶端再主動去拉,
這樣做的目的是嚴格控制消息順序的,客戶端接受到的消息順序和服務端存儲的順序會一致。
只有不持久化的消息纔會即時發送到接收客戶端。

  • 使用 mysql、redis:

    redis 做隊列和緩存、
    數據先存儲在內存中,過後會將數據刷新到 mysql

  • 使用glog作爲系統日誌

協議

使用兩種通信協議:

custom:自定義協議

gorpc:https://github.com/valyala/gorpc

終端 <-> IM:custom

IM <-> IMR:custom

IM <-> IMS:gorpc

  1. 自定義協議:
    包:header(12)|body
    
    header:len(4),seq(4),cmd(1),version(1),(2)
    
  2. gorpc
     github.com/valyala/gorpc
    

存儲文件

分爲索引文件、消息文件

  1. 索引文件

    索引文件兩種類型:
        peer:單聊消息通訊
        group:羣消息通訊
    
    索引文件位置:
        peer:%root%/peer_index
        group:%root%/group_index
    

    如果索引文件不存在,那麼在 ims 啓動的時候會根據消息內容文件進行重建;

    如果索引文件讀取出現問題,則會直接退出,說明文件已經破壞,需要人工干預。

    在取離線消息時,可以對羣組消息和單聊消息分別獲取,
    這樣可以做到分別控制單聊消息和羣組消息讀取量,避免單次讀取超量的離線消息

    消息索引全部放在內存中,在程序退出時,再全部保存到文件中,
    如果索引文件不存在或上次保存失敗,則在程序啓動的時候,從消息 DB 中重建索引,這需要遍歷每一條消息

  2. 消息文件

    都存儲在一個目錄裏,這個目錄由參數 storage_root 指定。
    消息存儲被劃分到不同的塊,每個消息塊大小爲 128M,一個消息塊對應一個文件。
    消息存儲文件的文件名爲:   message_0   ... message_N 數字0、1、2、...N爲消息塊 ID
    
    • 消息 ID

      每條消息都會對應一個消息 ID,這個消息 ID 是全局唯一且逐漸遞增的。

      消息 ID 是該條消息在全局消息文件中的起始位置。
      
      msgid = BLOCK_SIZE * block_NO + block_offset
      
      BLOCK_SIZE:消息塊大小(128M)
      block_NO:消息所在消息塊號,從0開始
      block_offset:消息在對應消息塊的起始位置
      
      如何根據消息 ID 來定位消息所在的塊文件以及在塊文件中的起始位置?
      
      block_NO = int(msgid/BLOCK_SIZE)
      block_offset = int(msgid%BLOCK_SIZE) 
      
    • 文件格式

      文件格式

      消息存儲文件由兩部分組成,文件頭+記錄列表。

    • 文件頭

      2

      每個消息塊文件都有文件頭,文件頭大小爲32字節。

      前面兩個4字節分別爲:MAGIC、VERSION。

      後面24字節作爲保留區域,暫未使用,用0進行填充。

    • 消息記錄

      3

      消息並非固定長度,消息的前後分別用來存放 MAGIC 數字。

      cmd、version、flag 各佔一個字節,最後用0補齊4個字節。

      len:存放消息記錄 body 部分的長度

      seq:TODO

      cmd:

      version:

      flag:

      body:TODO

消息同步

  1. 消息同步過程

    1、當終端發送 MSG_IM 消息時,服務端會通知接收終端 MSG_SYNC_NOTIFY ,其中消息內容 SyncKey 爲當前最新 msgid(由 IMS 服務器返回)
    
    2、終端收到這個 SyncKey 時,會向 IM 服務器發送 MSG_SYNC 消息,消息內容即爲 SyncKey
    
    3、IM服務器調用 IMS ,獲取從 sync_key 位置的最新消息
    
    4、IM服務器發送 MSG_SYNC_BEGIN + 歷史消息列表 + MSG_SYNC_END
    
    5、終端收到 MSG_SYNC_END 消息時,保存最新 SyncKey
    
    6、如果終端設置了 SyncKeyHandler ,那麼將會向服務器發送 MSG_SYNC_KEY 消息,消息內容爲最新 SyncKey
    
    7、服務器收到 MSG_SYNC_KEY 後,會比較當前 Redis 裏面的 SyncKey 和終端發送的 SyncKey
    
    8、如果終端發送 SyncKey 較大,那麼將其存入 Redis ;否則,do nothing
    
  2. 終端初始化消息同步

    1、終端連接後,進行 Token 認證
    
    2、發送 MSG_SYNC ,此時 SyncKey 爲0
    
    3、服務器收到 SyncKey 爲0的消息同步請求,從 Redis 中獲取對應的 SyncKey
    
        users_#{APPID}_#{UID}
        用戶狀態數據,類型爲 Hash,hashKey 如下:
        * sync_key
        * group_sync_key_#{GROUPID}
        * forbidden
        * unread
    
    4、後面的流程保持一致
    
    5、IM 服務器調用 IMS,獲取從 sync_key 位置的最新消息
    
    6、IM 服務器發送 MSG_SYNC_BEGIN+歷史消息列表+MSG_SYNC_END
    
    7、終端收到 MSG_SYNC_END 消息時,保存最新 SyncKey
    

流程圖

  1. 消息傳輸流程

    4

  2. imr

    5

  3. ims

    6

性能

單臺50w併發,3000條/s消息發送量(32g,16核)

來自官方說明,未做性能測試

二次開發相關

  • 消息體主要通過cmd區分操作,增加新的操作增加cmd類型和後續處理
  • 目前重複代碼較多,二次開發會有重複性修改
  • 消息存儲是文件加索引,這部分內容想要修改的話 工作量極大(二進制文件)
  • 因官方代碼不支持在windows下運行編譯所以做了結構調整,跟官方代碼更新做同步難度加大

消息類型在 message.go

存在問題:

  1. 終端的登陸認證
  2. 終端的配置信息、聊天的系統配置
  3. im可直接擴容,imr、ims 擴容必須重啓
  4. ims主從,目前只做備份使用、沒有被im直接訪問減少ims壓力
  5. 消息推送 APNs 實現未找到
  6. 沒有Token鑑權
  7. 記錄終端 ip 時,只支持 ipv4
  8. 有很多重複代碼、有些 bug ,看不懂邏輯、需要在梳理
  9. 沒有用戶體系
  10. 沒有知名使用的線上案例
  11. 社區不活躍
  12. 不支持語音、視頻聊天,圖片服務需要自己現行搭建
  13. 如果服務器掛掉導致存儲文件不完整、可能需要人工介入處理
  14. 數據庫表結構需要重新設計以增加業務邏輯

備註

參考地址:

  1. 流程解析
  2. 代碼簡介
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章