IM消息系统的设计和实现

Wesley13
• 阅读 558

点击关注上方“JAVA开发大本营”,设为“ 置顶或星标 ”,第一时间送达技术干货。

IM消息系统的设计和实现

全文预计阅读6分钟

IM消息系统的设计和实现

来源:https://juejin.im/post/5e12e80a5188253a821082ba


一、名词解释

  • 单播:服务器发送消息给单个客户端用户

  • 多播:服务器发送消息给多个客户端用户

  • 组播/广播:服务器发送消息给一组客户端。有组ID来标识这组用户

  • 上行消息:服务器发送消息给一组客户端。有组ID来标识这组用户

  • 下行消息:服务器端给客户端发送消息


二、系统架构

IM消息系统的设计和实现

  • proxy:部署在边缘机房,客户端通过智能dns就近接入

  • logicService:处理认证、心跳、上下线、进出群

  • pushService:单播,广播,接收到消息后转发给comet,之后再由comet把消息发出去

  • imService:聊天服务器,处理单聊群聊,离线消息

  • cosumerService:对群消息进行异步写扩散

  • authService:认证服务

数据结构

cacheService 维护全局在线用户,是一个二级map  user_id -> conn_id -> server_id

  • user_id 是业务指定的,唯一标识一个用户

  • conn_id 由存储进程分配,唯一标识该用户的一条连接

  • server_id 标识这条连接属于哪个接入进程

接入进程proxy维护自己在线用户, user_id+conn_id -> Connection

  • Connection 是客户端连接的封装,可以向它推送消息

接入进程proxy维护连接房间信息, room_id -> ConnectionList


三、消息模型

3.1 读扩散模型

IM消息系统的设计和实现

  • 优点:只写一次,降低了写入次数,特别在群模式下

  • 缺点: 同步消息的逻辑会比较复杂,接收端每个会话都要读取一次,放大了读,会产生很多无效请求。

3.2 写扩散模型

IM消息系统的设计和实现

  • 优点:拉取消息的逻辑简单

  • 缺点: 放大了写,单聊要额外写两次,群里要写N次


四、实现方式

4.1 单聊

4.1.1 设计目标

4.1.2 在线消息

IM消息系统的设计和实现

4.1.3 离线消息

4.1.4 检测消息丢失

4.2 群聊

4.2.1 设计目标

4.2.2 小群(写扩散)

IM消息系统的设计和实现

4.2.3 大群(读扩散)

IM消息系统的设计和实现

五、高性能分析

瓶颈 CPU > 带宽 > 内存

5.1 容量规划:(阿里云主机16C32G-2.5GHz,预留50%余量)

  • 10,000 conn per proxy

  • 100 proxy

  • 50 logicService/cacheService/pushService

  • or 改进:

  • 10 logicService

  • 5 pushService

  • kafka cluster

  • zookeeper cluster

  • 10 cacheService

5.2内部通信无瓶颈,可水平扩容的路径有:

  • 客户端发起的RPC mobile -> proxy -> micro

  • 上线/下线/切换房间/心跳 mobile -> proxy -> logicService -> cacheService

  • 单播 micro -> logicService (-> cacheService) -> pushService -> proxy -> mobile

  • 在线信息查询

  • 按用户查在线查房间 /session

5.3 内部通信,可能有瓶颈的路径:

  • 批量单播 micro -> logicService ((N-parallel)-> router) -> pushService -> proxy -> mobile

  • 限制:一批用户总数,不宜过多

  • 广播 micro -> logicService -> pushService -> proxy -> mobile

  • 限制:由于pushService定期absorb proxy上的room list, so pushService数量不可过多

  • 改进:logicService和pushService解耦,用kafka连接。由于 pushService 对 CPU 的消耗在 proxy/logicService/cacheService 中最少,只需要非常少的 pushService 实例就行。

  • 在线信息查询

  • 查在线总数 /count 由于logicService定期absorb cacheService上的room users,只能是有限的logicService打开counter定时查

  • 按房间查用户 /room 同 /count

  • 遍历 /list 调试用接口,别用于服务

5.4 proxy性能瓶颈

5.5 rpc性能瓶颈

六、高可用分析

为用户提供 7-24 小时无间断服务。迭代式开发,要求内在模块和业务服务的升级、扩容对用户无感知。

  • proxy 无状态服务,重启、升级时,客户端检测到连接断开,自动重连到另一个 proxy

  • logicService 无状态服务,重启、升级时,proxy 会自动寻找下一个 logic

  • pushService 无状态服务,重启、升级时,有其它 pushService对外提供服务

  • cacheService 有状态服务,重启、升级时,由备 cacheService 顶上;升级完成,切回主 cacheService

  • imService 无状态服务,重启、升级时,有其它 pushService对外提供服务

  • mysql:使用mysql master master机制来保证

  • redis: 使用哨兵机制来保证可用性

七、异常情况处理

  • 如何防止消息丢失(接收端上报已接收的最大消息id,异常服务端重发)

  • redis主从切换引起自增id不连续

  • 怎么提高proxy广播的性能

  • 怎么避免rpc的单条连接成为瓶颈

八、低成本、安全

  • 几乎没有外部依赖,极低的运维成本

  • 高性能的代码实现,节省服务器成本

  • 集成认证鉴权,也支持 HTTPS

点个在看是最大的支持 IM消息系统的设计和实现

IM消息系统的设计和实现

IM消息系统的设计和实现

本文分享自微信公众号 - JAVA开发大本营(gh_42999193133a)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Wesley13 Wesley13
3年前
SQL每日一题(20200810)
点击关注上方“SQL数据库开发”,设为“置顶或星标”,第一时间送达干货题目有如下一张表Person,其中ID是自增长!(https://oscimg.oschina.net/oscnet/c23185ee3ffa98e0a1789555c8c76f4adeb.png)求解,如何将相邻两条记录
Stella981 Stella981
3年前
GitHub 上有哪些适合新手跟进的优质项目?
!(https://oscimg.oschina.net/oscnet/011f28e3bc332010e1442e6c00ed344805d.jpg)点击上方“迈微电子研发社”,选择“星标★”公众号重磅干货,第一时间送达!(https://oscimg.oschina.net/oscnet/cd44ba75f
Wesley13 Wesley13
3年前
22 张令程序员泪流满面的趣图
点击关注上方“DevOps社群”,设为“置顶或星标”,第一时间送达技术干货。【1】当我捕获Bug的时候!(https://oscimg.oschina.net/oscnet/77dc3d8bd5694b4ca60f1d9160779454.gif)【2】周五下班想要快速离开公司的时候被其
Stella981 Stella981
3年前
Android蓝牙连接汽车OBD设备
//设备连接public class BluetoothConnect implements Runnable {    private static final UUID CONNECT_UUID  UUID.fromString("0000110100001000800000805F9B34FB");
Stella981 Stella981
3年前
Docker 部署SpringBoot项目不香吗?
  公众号改版后文章乱序推荐,希望你可以点击上方“Java进阶架构师”,点击右上角,将我们设为★“星标”!这样才不会错过每日进阶架构文章呀。  !(http://dingyue.ws.126.net/2020/0920/b00fbfc7j00qgy5xy002kd200qo00hsg00it00cj.jpg)  2
Stella981 Stella981
3年前
ClickHouse大数据领域企业级应用实践和探索总结
点击上方蓝色字体,选择“设为星标”回复”资源“获取更多资源!(https://oscimg.oschina.net/oscnet/bb00e5f54a164cb9827f1dbccdf87443.jpg)!(https://oscimg.oschina.net/oscnet/dc8da835ff1b4
Stella981 Stella981
3年前
200的大额人民币即将面世?央行:Yes!
点击上方蓝字关注我们!(https://oscimg.oschina.net/oscnet/2a1c2ac00bf54458a78c48a6c2e547d5.png)点击上方“印象python”,选择“星标”公众号重磅干货,第一时间送达!!(
可莉 可莉
3年前
200的大额人民币即将面世?央行:Yes!
点击上方蓝字关注我们!(https://oscimg.oschina.net/oscnet/2a1c2ac00bf54458a78c48a6c2e547d5.png)点击上方“印象python”,选择“星标”公众号重磅干货,第一时间送达!!(