如果你还不了解IM系统的整体结构,可以先看看《一个海量在线用户即时通讯系统(IM)的完整设计》(一下简称《IM完整设计》)这篇文章。
在《IM完整设计》文章中,服务端的结构是非常简单的,所有逻辑都集中在logic节点。如下图
在实际生产环境下,这种简单结构存在很多弊端。
1、功能扩展性欠佳
逻辑集中在logic一个节点,要上线新功能,只能修改logic再部署,无法实现类似微服务架构的快速部署能力。
2、存在性能瓶颈
由于gate到logic的消息传递采用的同步方式,为了匹配不同节点消息处理能力的差异,需要在logic中维护缓冲区。不同类消息,对消息延迟的容忍度不完全一致,缓冲区需要有不同的区域(或主题)概念。仅靠logic的一点java代码来维护这样的结构不是特别合适。
一个简单的缓冲区结构,会导致处理速度慢的消息拖慢整个消息处理速度!
3、维护成本高
由于所有逻辑集中在logic节点,导致logic节点逻辑复杂,代码臃肿。项目过程中加入的研发人员,需要很好的理解工程代码之后,才能对功能进行修改、维护,上手难度大。
修改任何一个逻辑,都需要回归整个logic节点主要功能,且要将所有功能都重新部署,无形中加大了维护工作量。
为了解决这些问题,引入消息总线(Kafka),系统结构进化为下图。(其中Rest-api模块往下的部分提供http短连接的业务逻辑,不做详细说明。)
1、tcp-gate,websocket-gate是tcp和websocket两种长连接的接入模块。
2、Dispatcher接收来自接入模块的消息,并对消息进行分发。
(1)对于auth,handshake类的需要同步处理的消息,通过RPC调用相关服务进行处理,并及时返回结果
(2)对于一般消息(可异步处理),按照业务(cmdid)不同,分别投递到消息总线的对应主题(topic)
3、单聊、群聊、离线、历史等具体业务逻辑节点从消息队列中获取对应的消息(单聊获取单聊消息),进行相应的逻辑处理,如写数据库、写缓存、计算等。将处理后的结果(或信息)再次放入消息总线(topic为接下来需要处理消息的逻辑节点对应的topic,如Deliver Service)
4、Deliver Service从消息总线接收“单聊”等节点处理后的消息,将消息投递给目标用户。
采用消息总线解耦的结构有诸多好处。
1、高可扩展性
通过消息总线,各个业务逻辑单元(单聊、群聊……)从代码层面完全独立,可以独立部署。当需要修改现有逻辑,只需要修改对应逻辑单元;如果要增加业务逻辑,增加新的业务逻辑单元即可。
支持公众号、工作台消息的业务逻辑单元,可以通过这种方式轻松挂接到消息总线。
2、高性能
(1)Kafka消息总线有充足的缓冲区,并且可以适配逻辑单元处理速度的差异(通过不同的topic区分逻辑单元)
(2)各个接入层节点、业务逻辑单元都可以通过部署节点实现水平扩展,性能线性增长
(3)数据库和Redis缓存都采用hash水平分库,性能能够实现线性增长
3、维护成本低
由于接入层(tcp-gate,websocket-gate,Dispatcher,Deliver Service)功能相对固定,逻辑层的逻辑单元又完全独立,新加入项目的开发人员只需要关注需要处理的逻辑单元即可,上手难度大大降低。
上线新的逻辑单元,完全不影响已有的逻辑,对测试资源消耗也得到很好的控制。
这个结构解决了很多问题,但是依然面临挑战。对于公司级的万人大群,大家活跃时,会面临消息延迟的问题。这个问题也是由方法可以缓解的。
相关阅读
一个海量在线用户即时通讯系统(IM)的完整设计
IM系统海量消息数据是怎么存储的?