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) 
      
    • 文件格式

      消息存储文件由两部分组成,文件头+记录列表。

    • 文件头

      每个消息块文件都有文件头,文件头大小为32字节。

      前面两个4字节分别为:MAGIC、VERSION。

      后面24字节作为保留区域,暂未使用,用0进行填充。

    • 消息记录

      消息并非固定长度,消息的前后分别用来存放 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. 消息传输流程

  2. imr

  3. ims

性能

单台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. 代码简介

你可能感兴趣的:(Golang,im,golang)