如何设计超十万级TPS的im消息沟通系统

前言

如何设计一款高性能,高并发以及高可用的im消息沟通平台是很多公司发展过程中必须要碰到并且解决的问题,如一家公司内部的通信,各个互联网平台的客服咨询,都是离不开一款好用并且方便维护的im消息沟通系统。那么我们应该怎么样来设计一款三高特性的im系统,并能支持各个业务线接入(内部oa通信,客服咨询,消息推送)等功能呢

image.png

下面就由我来介绍一下我所负责im消息沟通系统所经历的设计历程

im第一版设计

im第一版设计的初衷是公司需要一款im消息中间件用于支撑客服咨询业务,但是为了方便日后其他业务线也能接入消息沟通平台,所以将整个消息中心的能力需要给到中间件团队进行开发,各业务线接入消息沟通中心实现消息实时触达的能力

im初版架构介绍

im消息中心初版架构图.jpg

存储端
在存储端我们使用tidb,redis作为主要存储,redis用于存储消息已读未读,缓存连接信息等功能,tidb作为开源的分布式数据库,选择它是为了方便消息的存储

mq消息总线
我们使用rocketmq来实现消息总线,消息总线是整个im的核心,使用rocketmq能支持十万级别的tps。基本所有服务都要从消息总线中消费消息进行业务处理

zookeeper注册中心
服务会注册到zk中,方便服务之间内部进行调用,同样也可以暴露服务给外部进行调用

link服务
link服务主要用于接收客户端的ws,tcp,udp等协议的连接,调用用户服务进行认证,并投递连接成功的消息给位置服务进行消费,存储连接信息。ws过来的消息先到link再投递到消息总线

消息分发服务
消息分发服务主要用于接收消息总线推过来的消息进行处理,按照im内部消息协议构造好消息体后又推送到消息总线中,比如会推给会话服务、消息盒子、link服务

位置服务
存储link的ws连接,tcp连接等信息,并使用redis进行缓存,key为userId,方便根据UserId查询到该用户所登录的客户端连接在哪个link上。一个用户在相同设备只能登录一个,可以多端登录

用户服务
用于存储所有用户,提供认证查询接口

消息盒子
存储所有消息,提供消息查询,消息已读未读,消息未读数,消息检索等功能

会话服务
管理会话,群聊会话,单聊会话等功能

整体流程

im消息中心初版时序图.jpg

该设计存在的问题思考

在上面的架构设计中,我们详细介绍了初版系统架构的设计思路以及具体流程,那么在初版设计中存在什么样的问题,又该如何优化呢?

  • link服务到消息分发服务的消息推送使用消息总线
初版架构设计中,link服务将消息下推给消息分发服务进行处理使用的mq消息总线,使用mq消息总线会有一定的时延。
a用户发送消息到link --> 消息总线 -->消息分发服务 --> 消息总线 --> link --> b用户
这个流程太长,并且大大降低系统吞吐量
  • 消息落库为写扩散
其实现阶段我们使用的微信采用的是写扩散,为啥微信使用写扩散不是缺陷,对于消息沟通中心来说确实缺陷呢?
微信特性:
一、微信号称没有存储用户的聊天记录,全是实时推送
二、微信聊天记录全部会在我们手机端存储一份,两台手机终端上的聊天记录并不互通,并且互不可见
消息沟通中心特性:
一、消息沟通中心是会有拉取历史聊天记录(服务端拉取)的功能,存储了全量消息
二、消息沟通中心不仅要支持客户端版本,还需要支持网页版本,并且很大概率为网页版

综上
1)写扩散对微信这样有客户端版本的即时通讯产品十分友好,每个消息在消息分发的时候给处于这个会话(单聊,群聊)下的所有用户所在客户端先推送消息,
没找到连接就针对这个用户写一个离线缓存消息,那么下次该用户登录进来,可以从缓存中拉取到该消息,并且清掉缓存
2)写扩散对于通用消息沟通平台并不友好,由于接入方大部分是网页版的客户端,所以没有缓存消息的能力,浏览器刷新就没有了任何消息,所以需要实时去
服务端拉取历史消息,假设我是写扩散,在一个群聊中有五百个用户,针对这五百个用户在这个会话,我需要去写五百条消息,大大的增加了写io,并且还不能
写缓存,得写数据库。
  • tidb存在不稳定性,以及事务并发问题
tidb是目前主流的开源分布式数据库,查询效率高,无需分库分表。同样,tidb存在一些隐藏的问题
一、在高并发情况下并发事务会导致事务失败,具体原因不知
二、tidb排错成本高,公司很少有tidb专业运维,经常遇到不走索引的情况
  • 群聊单聊冗余在同一个服务
在初版设计中,单聊和群聊是冗余在会话服务,并且冗余在同一张表的,其实单聊群聊还是会有不同,如业务属性,虽然都是会话,我们还是需要将这两个服务拆分开,细粒度的服务拆分能更好的把控整体的逻辑。

im第二版升级设计

渐渐的我们发现初版im有很大的不足之处,在生产上暴露出了以下问题:

  • tps没达到预期,吞吐量不能满足公司业务的发展
  • 使用的存储中间件难以维护,试错成本高,经常在生产暴露问题,并且速度越来越慢
  • 消息写扩散没有太大必要,并大大增加了系统io次数
  • 一些特性无法支持,比如消息图文检索,消息已读未读

im升级版架构介绍

im消息中心升级版架构图.jpg

存储端
存储端我们使用了mysql,针对消息服务单独使用了主从mysql集群,主节点用于写消息,从节点用于消息检索

mq消息总线
与第一版相比没有改动

link服务
与第一版相比改动了link服务到消息分发服务的消息推送方式(变更为tcp实时推送)

消息分发服务
集成了消息处理能力,路由能力,每台消息分发服务拥有所有link服务的tcp连接

单聊服务
负责单聊会话的管理能力

群聊服务
负责群聊会话的管理能力

用户服务
提供用户认证,登录\注册能力

针对第一版本的改动对比

  • 将link到消息分发服务改为tcp实时连接,百万客户端连接同一台link机器,消息实时触达能力tps达到16万
link到消息分发服务的改版是本次设计的亮点之一
完全消除了mq推送的时延性,并且路由简单,几乎实时触达
用户a --> link --> 消息分发 --> link --> 用户b
link服务到消息分发服务集群的消息推送使用轮询负载均衡的方式,保证公平,不会导致个别机器负载过高
  • 取消位置服务,消息分发服务集成位置服务的能力
消息分发服务本身业务简单,不需要再单独划分位置服务,增加网络io,并且消息分发服务直连link,它负责路由更加方便
  • 存储端使用mysql,增强可维护性,消息服务使用主从读写分离方式,提高消息落库速度与检索速度,减轻数据库压力
前面有提到过使用tidb这样维护成本高,排查问题难的分布式数据库是一件很痛苦的事情,我们使用mysql更加稳定,大家对mysql的学习成本相对较低。针对消息服务使用读写分离的方式,能大大提高消息的吞吐量
  • 实现了特性功能,消息已读未读,红包推送,商品链接推送等功能
新版消息沟通中心加入了消息已读未读,发送红包,链接推送等功能,这些功能带有一定的特性,毕竟不是所有Im都需要,可通过配置取消这些功能
  • 消息存储写扩散改为读扩散
前面有提到,写扩散和读扩散的利弊,对于网页端我们更适合使用读扩散,只需要落一条消息,大大提高消息服务的吞吐量
  • 增加门面服务 im-logic,用于暴露给第三方业务线接口调用
初版都是im的各个服务各自暴露接口给到外部进行调用, 我们统一使用logic服务暴露给外部调用,在logic服务针对调用可以做一些处理,这样不会影响到整体im的通用,不会增加im底层代码的复杂度。将业务逻辑与底层进行解耦

优化效果对比

指标 第一版 第二版
消息实时触达 10000tps 160000tps
全链路tps 5000tps 15000tps
消息推送 6000tps 50000tps

业务线接入im的业务划分思考(以客服系统以及企业微信为例)

假如我开发了一款通用的im消息沟通系统,现在有很多业务方需要接入我们,我们该如何进行业务域的清晰划分显得尤为重要,在妥协与不妥协中进行平衡

当前开源消息沟通平台存在的问题

当前开源的很多消息沟通中心其实是集成了很多的业务逻辑,要不这是一款单纯的客服系统,要不这就是一款好友聊天系统。中间的业务划分并不明确。当然,这也有好处,拿来就能用,并不需要进行二次业务封装。

如何将im设计为一款真正的高性能通用im消息沟通系统

通用的消息沟通平台只需要有通用的底层能力:


im消息中心通用能力图.jpg

以下案例假设在我已经按照上述架构设计了一版im消息沟通中心

客服系统

客服系统接入im.jpg

客服系统不光需要实现自身业务,还需要整合im的消息能力,消费im的消息,来进行场景分析,实现会话变更,信令消息推送等逻辑,客服系统内部需要根据im的底层支持能力进行相应的业务封装以及客服系统的客服用户池,c端用户池如何初始化到im的用户中心这些问题都是需要考虑进去的

内部通信

内部通信接入im.jpg

员工内部通信系统需要集成好友功能,需要根据im的用户中心封装组织架构,用户权限等功能。同时,内部通信系统需要根据im实现消息已读未读,群聊列表,会话列表拉取等功能。

总结

im的消息沟通平台是一款需要高度结合业务的中间件系统,它直接与业务打交道,跟普通的中间件有根本的区别,一款好用的im的消息沟通平台,直接取决于你的通用性,可扩展性以及系统吞吐能力。希望这篇文章能对大家开发im时候的思路有所启迪。

你可能感兴趣的:(如何设计超十万级TPS的im消息沟通系统)