其他分享
首页 > 其他分享> > 基于实践:一套百万消息量小规模IM系统技术要点总结

基于实践:一套百万消息量小规模IM系统技术要点总结

作者:互联网

本文由公众号“后台技术汇”分享,原题“基于实践,设计一个百万级别的高可用 & 高可靠的 IM 消息系统”,原文链接在文末。由于原文存在较多错误和不准确内容,有大量修订和改动。

1、引言

大家好,我是公众号“后台技术汇”的博主“一枚少年”。

本人从事后台开发工作 3 年有余了,其中让我感触最深刻的一个项目,就是在两年前从架构师手上接过来的 IM 消息系统。

本文内容将从开发者的视角出发(主要是我自已的开发体会),围绕项目背景、业务需求、技术原理、开发方案等主题,一步一步的与大家一起剖析:设计一套百万消息量的小规模IM系统架构设计上需要注意的技术要点。

学习交流:

- 即时通讯/推送技术开发交流5群:215477170 [推荐]

- 移动端IM开发入门文章:《新手入门一篇就够:从零开发移动端IM

- 开源IM框架源码:https://github.com/JackJiang2011/MobileIMSDK 

(本文同步发布于:http://www.52im.net/thread-3752-1-1.html

2、项目背景

我们仔细观察就能发现,生活中的任何类型互联网服务都有 IM 系统的存在。

比如:

在这些林林总总的互联网生态产品里,即时消息系统作为底层能力,在确保业务正常与用户体验优化上,始终扮演了至关重要的角色。

所以,现如今的互联网产品中,即时通讯技术已经不仅限于传统IM聊天工具本身,它早已通过有形或无形的方式嵌入到了各种形式的互联网应用当中。IM技术(或者说即时通讯技术)对于很多开发者来说,确实是必不好可少的领域知识,不可或缺。

3、系统能力

典型的IM系统通常需要满足四点能力:高可靠性、高可用性、实时性和有序性。

 

这几个概念我就不详细展开,如果你是IM开发入门者,可以详读下面这几篇:

4、架构设计

以我的这个项目来说,架构设设计要点主要是:

业务架构图主要是这样:

技术模块分层架构大概是这样:

5、消息存储技术要点

5.1 理解读扩散和写扩散

5.1.1)基本概念:

我们举个例子说明什么是读扩散,什么是写扩散:

一个群聊“相亲相爱一家人”,成员:爸爸、妈妈、哥哥、姐姐和我(共 5 人)。

因为你最近交到女朋友了,所以发了一条消息“我脱单了”到群里面,那么自然希望爸爸妈妈哥哥姐姐四个亲人都能收到了。

正常逻辑下,群聊消息发送的流程应该是这样:

数据分发模型如下:

问题在于:如果第4步发生异常,群友会丢失消息,那么会导致有家人不知道“你脱单了”,造成催婚的严重后果。

所以优化的方案是:不管群员是否在线,都要先存储消息。

按照上面的思路,优化后的群消息流程如下:

以上优化后的方案,便是所谓的“写扩散”了。

问题在于:每个人都存一份相同的“你脱单了”的消息,对磁盘和带宽造成了很大的浪费(这就是写扩散的最大弊端)。

所以优化的方案是:群消息实体存储一份,用户只存消息 ID 索引。

于是再次优化后的发送群消息流程如下:

二次优化后的方案,便是所谓的“读扩散”了。

5.1.2)小结一下:

从公开的技术资料来看,微信和钉钉的群聊消息应该使用的是写扩散方式,具体可以参看这两篇:《微信后台团队:微信后台异步消息队列的优化升级实践分享》、《阿里IM技术分享(四):闲鱼亿级IM消息系统的可靠投递优化实践》(注意“5.5 服务端存储模型优化”这一节)。

5.2 “消息”所关联的对象

5.2.1)消息实体模型:

常见的消息业务,可以抽象为几个实体模型概念:用户/用户关系/用户设备/用户连接状态/消息/消息队列。

在IM系统中的实体模型关系大致如下:

5.2.2)实体模型概念解释:

用户实体:

联系人关系(主要由业务决定用户与用户之间的关系),比如说:

消息实体:

消息->消息队列:考虑到读扩散,消息最终归属于一个或多个消息队列里,因此群聊场景它会分布在不同的消息队列里。

消息队列实体:

消息队列:确切说是消息引用队列,它里面的索引元素最终指向具体的消息实体对象。

用户连接状态:

用户终端设备:

客户端一般是 Android&IOS,web 端一般是浏览器,还有其他灵活的 WebView(公众号/小程序)。

5.3 消息的存储方案

对于消息存储方案,本质上只有三种方案:要么放在内存、要么放在磁盘、要么两者结合存储(据说大公司为了优化性能,活跃的消息数据都是放在内存里面的,毕竟有钱~)。

下面分别解析主要方案的优点与弊端:

5.3.1)对于方案一:redis

前提:用户 & 联系人关系,由于是业务数据,因此统一默认使用关系型数据库存储。

流程图:

解释如下:

实现方案:

优点是:内存操作,响应性能好

弊端是:

5.3.2)方案二:redis+mysql

前提:用户 & 联系人关系,由于是业务数据,因此统一默认使用关系型数据库存储。

流程图:

解释如下:

实现方案:

优点是:

弊端是:磁盘读取操作,响应性能较差(从产品设计的角度出发,你维护的这套 IM 系统究竟是强 IM 还是弱 IM)。

6、消息的消费模式

6.1 拉模式

选用消息拉模式的原因:

6.2 ack 机制

技术原理:

请求模型原理图如下:

实现方案1:基于每一条消息编号 ACK:

实现方案2:基于滑动窗口 ACK:

1)客户端在接收到消息编号之后,和本地的消息编号进行比对:

 - 如果比本地的小,说明该消息已经收到,忽略不处理;

 - 如果比本地的大,使用本地的消息编号,向服务端拉取大于本地的消息编号的消息列表,即增量消息列表。

 - 拉取完成后,更新消息列表中最大的消息编号为新的本地的消息编号;

2)服务端在收到 ack 消息时,进行批量标记已读或者删除。

这种方式,在业务被称为推拉结合的方案,在分布式消息队列、配置中心、注册中心实现实时的数据同步,经常被采用。

6.3 基于ack 机制的好处

第一次获取消息完成之后,如果没有 ack 机制,流程是:

如果由于网络延迟,导致客户端长时间取不到数据,这时客户端会断开该次 HTTP 请求,进而忽略这次响应数据的处理,最终导致消息数据被删除而后续无法恢复。

有了 ack 机制,哪怕第一次获取消息失败,客户端还是可以继续请求消息数据,因为在 ack 确认之前,消息数据都不会删除掉。

7、微服务设计

一般来说 IM 微服务,能拆分为基础的三个微服务:

参考架构图:

他们分工合作如下。

用户微服务(用户设备的登录 & 登出):

连接管理微服务:

消息业务微服务:

最后提一下消息的路由:

微服务之间也有通信手段,比如业务服务到连接管理服务,两者之间可以通过 RPC 实现实时消息的路由通知。

8、离线消息推送

离线推送方案上,大家一般都会考虑采用两种方案:

8.1 企业自研后台离线 PUSH 系统

技术原理:

在应用级别,客户端与后台离线 PUSH 系统保持长连接,当用户状态被检测为离线时,通过这个长连接告知客户端“有新消息”,进而唤醒手机弹窗标题。

弊端就是:

随着安卓和苹果系统的限制越来越严格,一般客户端的活动周期被限制的死死的,一旦客户端进程被挪到后台就立马被 kill 掉了,导致客户端保活特别难做好(这也是很多中小企业头疼的地方,毕竟只有微信或者 QQ 这种体量的一级市场 APP,手机系统愿意给他们留后门来做保活)。具体可以读一下《Android P正式版即将到来:后台应用保活、消息推送的真正噩梦》这篇。

8.2 企业自行对接第三方厂商 PUSH 系统

技术原理:

在系统级别,每个硬件系统都会与对应的手机厂商保持长连接,当用户状态被检测为离线时,后台将推送报文通过 HTTP 请求,告知第三方手机厂商服务器,进而通过系统唤醒 app 的弹窗标题。

弊端就是:

总之,IM里离线消息推送是个很头疼的问题(当然这里主要说是Andriod了,iOS里苹果官方的APNs就舒服多了),有兴趣好一读一下下面这些文章:

  1. 全面盘点当前Android后台保活方案的真实运行效果(截止2019年前)
  2. 融云技术分享:融云安卓端IM产品的网络链路保活技术实践
  3. 2020年了,Android后台保活还有戏吗?看我如何优雅的实现!
  4. 史上最强Android保活思路:深入剖析腾讯TIM的进程永生技术
  5. Android进程永生技术终极揭密:进程被杀底层原理、APP应对被杀技巧
  6. Android保活从入门到放弃:乖乖引导用户加白名单吧(附7大机型加白示例)
  7. 阿里IM技术分享(六):闲鱼亿级IM消息系统的离线推送到达率优化

9、其它需要考虑的技术要点

9.1 安全性

关于IM安全性,我个人的体会是这样:

以上要点中:IM中的长连接安全性是比较重要且不容易处理的,因为它需要在安全性和性能上作平衡和取舍(不能光顾着安全而损失IM长连接的高吞吐性能),这方面可以参考微信团队分享的这篇《微信新一代通信安全解决方案:基于TLS1.3的MMTLS详解》。

另外:更高安全性的场景可以考虑组合加密方案,详情可以参考《探讨组合加密算法在IM中的应用》。

9.2 一致性

IM消息一致性难题,主要是保证消息不乱序的问题。这个话题,初学者可以读读这篇《零基础IM开发入门(四):什么是IM系统的消息时序一致性?》,我就不再赘述了。

解决一致性问题的切入点有很多,最常见的是使用有序的消息唯一id,关于有序且唯一的ID生成问题,微信团队的思路就很好,可以借鉴一下《微信技术分享:微信的海量IM聊天消息序列号生成实践(算法原理篇)》。

另外,以下几篇关于消息有序性问题的总结也非常好,可以进行参考:

  1. 如何保证IM实时消息的“时序性”与“一致性”?
  2. 一个低成本确保IM消息时序的方法探讨
  3. 一套亿级用户的IM架构技术干货(下篇):可靠性、有序性、弱网优化等

9.3 可靠性

IM里所谓的可靠性,说直白一点就是保证消息不丢失,这看似理所当然、稀松平常的技术点,在IM系统中又是另一个很大的话题,鉴于本人水平有限,就不班门弄斧,IM初学者可以能过《零基础IM开发入门(三):什么是IM系统的可靠性?》这篇来理解可靠性这个概念。

然后再读读《IM消息送达保证机制实现(一):保证在线实时消息的可靠投递》、《IM消息送达保证机制实现(二):保证离线消息的可靠投递》这两篇,基本上就能对IM可靠性这个技术要点有了比较深刻的认识了。

下面这几篇实战性的总结,适合有一定IM经验的同行们学习,可以借鉴学习一下:

  1. 融云技术分享:全面揭秘亿级IM消息的可靠投递机制
  2. IM开发干货分享:如何优雅的实现大量离线消息的可靠投递
  3. 从客户端的角度来谈谈移动端IM的消息可靠性和送达机制
  4. 阿里IM技术分享(四):闲鱼亿级IM消息系统的可靠投递优化实践

9.4 实时性

IM实时性这个技术点,就回归到了“即时通讯”这个技术的立身之本了,可以说,没有实时性,也就不存在“即时通讯”这个技术范畴了,可以见它的重要性。关于实时性这个概念,初学者可以通过《零基础IM开发入门(二):什么是IM系统的实时性?》这篇去学习一下,我就不啰嗦了,人家比我说的好。

笔者公司的项目里实时通信用方案都是采用 WebSocket(如果你不了解WebSocket,可以读一下《WebSocket从入门到精通,半小时就够!》,以及《搞懂现代Web端即时通讯技术一文就够:WebSocket、socket.io、SSE》),但是某些低版本的浏览器可能不支持 WebSocket,所以实际开发时,要兼容前端所能提供的能力进行方案设计。

以下两篇关于实时性的同行实践性总结也不错:

  1. 移动端IM中大规模群消息的推送如何保证效率、实时性?
  2. 阿里IM技术分享(五):闲鱼亿级IM消息系统的及时性优化实践

10、我在项目实践中的体会

作为研发者,有两年多的时间都在维护迭代公司的 IM 消息系统,以下是我自已的小小体会。

我体会到的重点难点有以下几方面:

项目还存在可优化的地方:

11、写在最后

两年前从架构师手上接过来的 IM 消息系统模块,让我逐步培养了架构思维,见贤思齐,感谢恩师。

IM技术是个经久不衰的领域,但同时可直接使用的技术资产也非常匮乏,必竟传统的IM巨头们的产品通常都是私有化协议、私有化方案,很难有业界共同的方案可以直接使用(包括资料或开源代码),正是这种不通用、不准,间接导致IM技术门槛的提高。所以通常公司要搞IM的话,如果没有技术积累,就只能从零开始造轮子。

为了改变这种局面,也希望搞IM开发的同学不要闷头造车,应该多多借鉴同行的思路,同时也能积极分享自已的经验,让IM开发不再痛苦。

以上抛砖引玉,欢迎留言讨论,一起进步。

12、参考资料

[1] 新手入门一篇就够:从零开发移动端IM

[2] 为何基于TCP协议的移动端IM仍然需要心跳保活机制?

[3] Android P正式版即将到来:后台应用保活、消息推送的真正噩梦

[4] WebSocket从入门到精通,半小时就够!

[5] 搞懂现代Web端即时通讯技术一文就够:WebSocket、socket.io、SSE

[6] 一套海量在线用户的移动端IM架构设计实践分享(含详细图文)

[7] 一套原创分布式即时通讯(IM)系统理论架构方案

[8] 一套高可用、易伸缩、高并发的IM群聊、单聊架构方案设计实践

[9] 微信技术分享:微信的海量IM聊天消息序列号生成实践(算法原理篇)

[10] 阿里IM技术分享(四):闲鱼亿级IM消息系统的可靠投递优化实践

[11] 阿里IM技术分享(五):闲鱼亿级IM消息系统的及时性优化实践

[12] 一套亿级用户的IM架构技术干货(下篇):可靠性、有序性、弱网优化等

[13] 从新手到专家:如何设计一套亿级消息量的分布式IM系统

[14] 企业微信的IM架构设计揭秘:消息模型、万人群、已读回执、消息撤回等

[15] 融云技术分享:全面揭秘亿级IM消息的可靠投递机制

[16] 即时通讯安全篇(六):非对称加密技术的原理与应用实践

[17] 通俗易懂:一篇掌握即时通讯的消息传输安全原理

[18] 微信新一代通信安全解决方案:基于TLS1.3的MMTLS详解

[19] 零基础IM开发入门(二):什么是IM系统的实时性?

[20] 零基础IM开发入门(三):什么是IM系统的可靠性?

[21] 零基础IM开发入门(四):什么是IM系统的消息时序一致性?

本文已同步发布于“即时通讯技术圈”公众号。

同步发布链接是:http://www.52im.net/thread-3752-1-1.html

标签:系统,离线,用户,IM,消息,要点,小规模,客户端
来源: https://www.cnblogs.com/imteck4713/p/15611596.html