本文由作者jhon_11分享,有大量修订和改动。
1、引言
如何设计一款高性能、高并发、高可用的im综合消息平台是很多公司发展过程中会碰到且必须要解决的问题。比如一家公司内部的通讯系统、各个互联网平台的客服咨询系统,都是离不开一款好用且维护的方便im综合消息系统。
那么,我们应该怎么样来设计一款三高特性的im系统,并能同时支持各个业务线的接入(比如:内部OA通讯、客服咨询、消息推送等等功能)有呢?
下面就由我来介绍一下我所负责的公司IM综合消息系统所经历的架构设计历程,以及架构设计过程中的一些思路和总结,希望能给你带来启发。
2、初版IM架构
2.1概述
im第一版设计的初衷是公司需要一款im消息中间件用于支撑客服咨询业务。
但是,考虑到为了方便日后其他业务线也能接入消息沟通平台,所以一开始就将整个消息中心的能力需求给到中间件团队进行开发,以便除客服外的各业务线接入综合消息中心,从而实现多元的消息实时触达能力。
2.2初版架构介绍
初版架构图如下图所示:
针对上面的架构图,我们逐个解释一下各模块的作用。
1)存储端:
在初版的架构下,存储端我们使用tidb、redis作为主要存储:
[1]redis用于存储消息已读未读,缓存连接信息等功能;
[2]tidb作为开源的分布式数据库,选择它是为了方便消息的存储。
2)mq消息总线:
我们使用rocketmq来实现消息总线(PS:即分布式情况下,不同im实例间通过MQ进行消息交互)。
消息总线是整个im的核心,使用rocketmq能支持十万级别的tps。基本所有服务都要从消息总线中消费消息进行业务处理。
3)zookeeper注册中心:各个服务会注册到zk中,方便服务之间内部进行调用,同样也可以暴露服务给外部进行调用。
4)link服务:
link服务主要用于接收客户端的ws(WebSocket协议)、tcp、udp等协议的连接。
同时调用用户服务进行认证,并投递连接成功的消息给位置服务进行消费,存储连接信息。
ws(WebSocket协议)过来的消息先到link再投递到消息总线。
5)消息分发服务:
消息分发服务主要用于接收消息总线推过来的消息进行处理,按照im内部消息协议构造好消息体后,又推送到消息总线中(比如会推给会话服务、消息盒子、link服务)。
6)位置服务:
存储link的(WebSocket协议)连接、tcp连接等信息,并使用redis进行缓存(key为userId),方便根据UserId查询到该用户所登录的客户端连接在哪个link上。
一个用户在相同设备只能登录一个,但可以支持多端登录。
7)用户服务:用于存储所有用户,提供认证查询接口。
8)消息盒子:存储所有消息,提供消息查询、消息已读未读、消息未读数、消息检索等功能。
9)会话服务:管理会话、群聊会话、单聊会话等功能。
2.3整体时序图
整体架构的时序图如下:
3、初版IM架构存在的问题及思考
在上节的架构设计介绍中,我们详细分享了初版IM系统架构的设计思路以及具体流程。
那么在初版IM架构设计中还存在什么样的问题,又该如何优化呢?我们一条条来看看。
3.1使用MQ消息总线的问题
正如上节所分享的那样,我们初版IM架构中,link服务到消息分发服务的消息使用的MQ消息总线。
初版架构设计中,link服务将消息下推给消息分发服务进行处理时,使用的是mq消息总线(通俗了说,IM集群内不同IM实例间的通信是依赖于MQ进行的消息传递),而mq消息总线必然做对有一定的时延(而且时延受制于MQ本身的系统实现和技术策略)。
举个例子:
当两个处于不同IM实例的客户端A和B聊天时,A用户发送消息到link--消息总线--消息分发服务--消息总线--link--B用户。
正如上面这个例子,im消息投递流程太长了,并且这样也会大大降低系统的吞吐量。
3.2消息落库为写扩散的问题
其实现阶段我们使用的是跟