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

132次阅读

共计 8614 个字符,预计需要花费 22 分钟才能阅读完成。

本文由公众号“后盾技术汇”分享,原题“基于实际,设计一个百万级别的高可用 & 高牢靠的 IM 音讯零碎”,原文链接在文末。因为原文存在较多谬误和不精确内容,有大量订正和改变。

1、引言

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

自己从事后盾开发工作 3 年无余了,其中让我感触最粗浅的一个我的项目,就是在两年前从架构师手上接过来的 IM 音讯零碎。

本文内容将从开发者的视角登程(次要是我自已的开发领会),围绕我的项目背景、业务需要、技术原理、开发计划等主题,一步一步的与大家一起分析:设计一套百万音讯量的小规模 IM 零碎架构设计上须要留神的技术要点。

学习交换:

  • 即时通讯 / 推送技术开发交换 5 群:215477170 [举荐]
  • 挪动端 IM 开发入门文章:《新手入门一篇就够:从零开发挪动端 IM》
  • 开源 IM 框架源码:https://github.com/JackJiang2…

(本文同步公布于:http://www.52im.net/thread-37…)

2、我的项目背景

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

比方:

1)基础性服务类 - 腾讯新闻(评论音讯);
2)商务利用类 - 钉钉(审批工作流通知);
3)交换娱乐类 -QQ/ 微信(私聊群聊 & 讨论组 & 朋友圈);
4)互联网自媒体 - 抖音快手(点赞打赏告诉)。

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

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

3、零碎能力
典型的 IM 零碎通常须要满足四点能力:高可靠性、高可用性、实时性和有序性。

这几个概念我就不具体开展,如果你是 IM 开发入门者,能够详读上面这几篇:

《零根底 IM 开发入门(一):什么是 IM 零碎?》
《零根底 IM 开发入门(二):什么是 IM 零碎的实时性?》
《零根底 IM 开发入门(三):什么是 IM 零碎的可靠性?》
《零根底 IM 开发入门(四):什么是 IM 零碎的音讯时序一致性?》

4、架构设计

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

1)微服务:拆分为用户微服务 & 音讯连贯服务 & 音讯业务服务;
2)存储架构:兼容性能与资源开销,抉择 reids&mysql;
3)高可用:能够撑持起高并发场景,抉择 Spring 提供的 websocket;
4)反对多端音讯同步:app 端、web 端、微信公众号、小程序音讯;
5)反对在线与离线音讯场景。

业务架构图次要是这样:

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

5、音讯存储技术要点

5.1 了解读扩散和写扩散
5.1.1)基本概念:

咱们举个例子阐明什么是读扩散,什么是写扩散:

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

因为你最近交到女朋友了,所以发了一条音讯“我脱单了”到群外面,那么天然心愿爸爸妈妈哥哥姐姐四个亲人都能收到了。

失常逻辑下,群聊音讯发送的流程应该是这样:

1)遍历群聊的成员并发送音讯;
2)查问每个成员的在线状态;
3)成员不在线的存储离线;
4)成员在线的实时推送。

数据散发模型如下:

问题在于:如果第 4 步产生异样,群友会失落音讯,那么会导致有家人不晓得“你脱单了”,造成催婚的严重后果。

所以优化的计划是:不论群员是否在线,都要先存储音讯。

依照下面的思路,优化后的群音讯流程如下:

1)遍历群聊的成员并发送音讯;
2)群聊所有人都存一份;
3)查问每个成员的在线状态;
4)在线的实时推送。

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

问题在于:每个人都存一份雷同的“你脱单了”的音讯,对磁盘和带宽造成了很大的节约(这就是写扩散的最大弊病)。

所以优化的计划是:群音讯实体存储一份,用户只存音讯 ID 索引。

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

1)遍历群聊的成员并发送音讯;
2)先存一份音讯实体;
3)而后群聊所有人都存一份音讯实体的 ID 援用;
4)查问每个成员的在线状态;
5)在线的实时推送。

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

5.1.2)小结一下:

1)读扩散:读取操作很重,写入操作很轻,资源耗费绝对小一些;
2)写扩散:读取操作很轻,写入操作很重,资源耗费绝对大一些。
从公开的技术材料来看,微信和钉钉的群聊音讯应该应用的是写扩散形式,具体能够参看这两篇:《微信后盾团队:微信后盾异步音讯队列的优化降级实际分享》、《阿里 IM 技术分享(四):闲鱼亿级 IM 音讯零碎的牢靠投递优化实际》(留神“5.5 服务端存储模型优化”这一节)。

5.2“音讯”所关联的对象
5.2.1)音讯实体模型:

常见的音讯业务,能够形象为几个实体模型概念:用户 / 用户关系 / 用户设施 / 用户连贯状态 / 音讯 / 音讯队列。

在 IM 零碎中的实体模型关系大抵如下:

5.2.2)实体模型概念解释:

用户实体:

1)用户 -> 用户终端设备:每个用户可能多端登录并收发音讯;
2)用户 -> 音讯:思考到读扩散,每个用户与音讯的关系都是 1:n;
3)用户 -> 音讯队列:思考到读扩散,每个用户都会保护本人的一份“音讯列表”(1:1),如果思考到扩容,甚至能够开拓一份音讯溢出列表接管超出“音讯列表”容量的音讯数据(此时是 1:n);
4)用户 -> 用户连贯状态:思考到用户可能多端登录,那么 app/web 都会有对应的在线状态信息(1:n);
5)用户 -> 联系人关系:思考到用户最终以某种业务联系到一起,组成多份联系人关系,最终造成私聊或者群聊(1:n);

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

1)某个家庭下有多少人,这个家庭群聊就有多少人;
2)在 ToB 场景,在钉钉企业版里,咱们往往有企业群聊这个存在。

音讯实体:

音讯 -> 音讯队列:思考到读扩散,音讯最终归属于一个或多个音讯队列里,因而群聊场景它会散布在不同的音讯队列里。

音讯队列实体:

音讯队列:确切说是音讯援用队列,它外面的索引元素最终指向具体的音讯实体对象。

用户连贯状态:

1)对于 app 端:网络起因导致断线,或者用户手动 kill 掉利用过程,都属于离线;
2)对于 web 端:网络起因导致浏览器断网,或者用户手动敞开标签页,都属于离线;
3)对于公众号:无奈别离离线在线;
4)对于小程序:无奈别离离线在线。

用户终端设备:

客户端个别是 Android&IOS,web 端个别是浏览器,还有其余灵便的 WebView(公众号 / 小程序)。

5.3 音讯的存储计划
对于音讯存储计划,实质上只有三种计划:要么放在内存、要么放在磁盘、要么两者联合存储(据说大公司为了优化性能,沉闷的音讯数据都是放在内存外面的,毕竟有钱~)。

上面别离解析次要计划的长处与弊病:

1)计划一:思考性能,数据全副放到 redis 进行存储;
2)计划二:思考资源,数据用 redis + mysql 进行存储。

5.3.1)对于计划一:redis

前提:用户 & 联系人关系,因为是业务数据,因而对立默认应用关系型数据库存储。

流程图:

解释如下:

1)用户发消息;
2)redis 创立一条实体数据 & 一个实体数据计时器;
3)redis 在 B 用户的用户队列 增加实体数据援用;
4)B 用户拉取音讯(后续 5.2 会提及拉模式)。

实现计划:

1)用户队列,zset(score 确保有序性);
2)音讯实体列表,hash(msg_id 确保唯一性);
3)音讯实体计数器,hash(反对群聊音讯的援用次数,倒计时到零时则删除实体列表的对应音讯,以节俭资源)。

长处是:内存操作,响应性能好

弊病是:

1)内存耗费微小,eg:除非大厂,小公司的服务器的贵重内存资源是耗不起业务的,随着业务增长,不想拓展资源,就须要手动清理数据了;
2)受 redis 容灾性策略影响较大,如果 redis 宕机,间接导致数据失落(能够应用 redis 的集群部署 / 哨兵机制 / 主从复制等伎俩解决)。

5.3.2)计划二:redis+mysql

前提:用户 & 联系人关系,因为是业务数据,因而对立默认应用关系型数据库存储。

流程图:

解释如下:

1)用户发消息;
2)mysql 创立一条实体数据;
3)redis 在 B 用户的用户队列 增加实体数据援用;
4)B 用户拉取音讯(下文会提及拉模式)。

实现计划:

1)用户队列,zset(score 确保有序性);
2)音讯实体列表,转移到 mysql(表主键 id 确保唯一性);
3)音讯实体计数器,hash(删除这个概念,因为磁盘可用总资源远远高于内存总资源,哪怕始终寄存 mysql 数据库,在业务量百万级别时也不会有大问题,如果是微小体量业务就须要思考分表分库解决检索数据的性能了)。

长处是:

1)抽离了数据量最大的音讯实体,大大节俭了内存资源;
2)磁盘资源易于拓展,便宜实用。

弊病是:磁盘读取操作,响应性能较差(从产品设计的角度登程,你保护的这套 IM 零碎到底是强 IM 还是弱 IM)。

6、音讯的生产模式

6.1 拉模式
选用音讯拉模式的起因:

1)因为用户数量太多(观察者),服务器无奈一一监控客户端的状态,因而音讯模块的数据交互应用拉模式,能够节约服务器资源;
2)当用户有未读音讯时,由客户器被动发动申请的形式,能够及时刷新客户端状态。
6.2 ack 机制
技术原理:

1)基于拉模式实现的数据拉取申请(第一次 fetch 接口)与数据拉取确认申请(第二次 fetch 接口)是成对呈现的;
2)客户端二次调用 fetch 接口,须要将上次音讯生产的锚点通知服务端,服务器进而删除已读音讯。

申请模型原理图如下:

实现计划 1:基于每一条音讯编号 ACK:

1)实现:客户端在接管到音讯之后,发送 ACK 音讯编号给服务端,告知曾经收到该音讯。服务端在收到 ACK 音讯编号的时候,标记该音讯曾经发送胜利;
2)弊病:这种计划,因为客户端逐条 ACK 音讯编号,所以会导致客户端和服务端交互次数过多。当然,客户端能够异步批量 ACK 多条音讯,从而缩小次数。

实现计划 2:基于滑动窗口 ACK:

1)客户端在接管到音讯编号之后,和本地的音讯编号进行比对:

  • 如果比本地的小,阐明该音讯曾经收到,疏忽不解决;
  • 如果比本地的大,应用本地的音讯编号,向服务端拉取大于本地的音讯编号的音讯列表,即增量音讯列表。
  • 拉取实现后,更新音讯列表中最大的音讯编号为新的本地的音讯编号;

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

这种形式,在业务被称为推拉联合的计划,在分布式音讯队列、配置核心、注册核心实现实时的数据同步,常常被采纳。

6.3 基于 ack 机制的益处
第一次获取音讯实现之后,如果没有 ack 机制,流程是:

1)服务器删除已读音讯数据;
2)服务端把数据包响应给客户端。

如果因为网络提早,导致客户端长时间取不到数据,这时客户端会断开该次 HTTP 申请,进而疏忽这次响应数据的解决,最终导致音讯数据被删除而后续无奈复原。

有了 ack 机制,哪怕第一次获取音讯失败,客户端还是能够持续申请音讯数据,因为在 ack 确认之前,音讯数据都不会删除掉。

7、微服务设计

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

1)用户服务;
2)业务服务;
3)连贯治理服务。

参考架构图:

他们分工合作如下。

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

1)设施号存库;
2)连贯状态更新;
3)其余登录端用户踢出等。

连贯治理微服务:

1)状态保留:保留用户设施长连贯对象;
2)剔除有效连贯:轮训已有长连贯对象状态,超时删除对象;
3)承受客户端的心跳包:刷新长连贯对象的状态。

音讯业务微服务:

1)音讯存储:进行私聊 / 群聊的音讯存储策略(请参看“音讯存储模型”一节);
2)音讯生产:进行音讯获取响应与 ack 确认删除(请参看“音讯生产模式”一节);
3)音讯路由:用户在线时,路由音讯告诉包到“音讯连贯治理微服务”,以告诉用户客户端来取音讯。

最初提一下音讯的路由:
微服务之间也有通信伎俩,比方业务服务到连贯治理服务,两者之间能够通过 RPC 实现实时音讯的路由告诉。

8、离线音讯推送

离线推送计划上,大家个别都会思考采纳两种计划:

1)企业自研后盾离线 PUSH 零碎;
2)企业自行对接第三方手机厂商 PUSH 零碎。

8.1 企业自研后盾离线 PUSH 零碎
技术原理:

在利用级别,客户端与后盾离线 PUSH 零碎放弃长连贯,当用户状态被检测为离线时,通过这个长连贯告知客户端“有新音讯”,进而唤醒手机弹窗题目。

弊病就是:

随着安卓和苹果零碎的限度越来越严格,个别客户端的流动周期被限度的死死的,一旦客户端过程被挪到后盾就立马被 kill 掉了,导致客户端保活特地难做好(这也是很多中小企业头疼的中央,毕竟只有微信或者 QQ 这种体量的一级市场 APP,手机零碎违心给他们留后门来做保活)。具体能够读一下《Android P 正式版行将到来:后盾利用保活、音讯推送的真正噩梦》这篇。

8.2 企业自行对接第三方厂商 PUSH 零碎
技术原理:

在零碎级别,每个硬件零碎都会与对应的手机厂商放弃长连贯,当用户状态被检测为离线时,后盾将推送报文通过 HTTP 申请,告知第三方手机厂商服务器,进而通过零碎唤醒 app 的弹窗题目。

弊病就是:

1)作为利用端,音讯是否确切送达给用户侧,是未知的;推送的稳定性也取决于第三方手机厂商的服务稳定性;
2)额定进行 sdk 的对接工作,减少了工作量;
3)第三方厂商随时可能降级 sdk 版本,导致没有降级 sdk 的服务器呈现推送失败的状况,给 Sass 零碎部署带来艰难;
4)推送证书配置也要思考到保护老本。

总之,IM 里离线音讯推送是个很头疼的问题(当然这里次要说是 Andriod 了,iOS 里苹果官网的 APNs 就难受多了),有趣味好一读一下上面这些文章:

《全面盘点以后 Android 后盾保活计划的实在运行成果(截止 2019 年前)》
《融云技术分享:融云安卓端 IM 产品的网络链路保活技术实际》
《2020 年了,Android 后盾保活还有戏吗?看我如何优雅的实现!》
《史上最强 Android 保活思路:深刻分析腾讯 TIM 的过程永生技术》
《Android 过程永生技术终极揭密:过程被杀底层原理、APP 应答被杀技巧》
《Android 保活从入门到放弃:乖乖疏导用户加白名单吧(附 7 大机型加白示例)》
《阿里 IM 技术分享(六):闲鱼亿级 IM 音讯零碎的离线推送达到率优化》

9、其它须要思考的技术要点

9.1 安全性
对于 IM 安全性,我集体的领会是这样:

1)业务数据传输安全性应用 https 拜访;
2)实时音讯应用 SSL/TLS 对长连贯进行加密;
3)应用公有协定,不容易解析;
4)内容安全性端到端加密,两头任何环节都不能解密(即发送和接收端替换相互的密钥来解密,服务器端解密不了);
5)服务器端不存储音讯。

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

另外:更高安全性的场景能够思考组合加密计划,详情能够参考《探讨组合加密算法在 IM 中的利用》。

9.2 一致性
IM 音讯一致性难题,次要是保障音讯不乱序的问题。这个话题,初学者能够读读这篇《零根底 IM 开发入门(四):什么是 IM 零碎的音讯时序一致性?》,我就不再赘述了。

解决一致性问题的切入点有很多,最常见的是应用有序的音讯惟一 id,对于有序且惟一的 ID 生成问题,微信团队的思路就很好,能够借鉴一下《微信技术分享:微信的海量 IM 聊天音讯序列号生成实际(算法原理篇)》。

另外,以下几篇对于音讯有序性问题的总结也十分好,能够进行参考:

《如何保障 IM 实时音讯的“时序性”与“一致性”?》
《一个低成本确保 IM 音讯时序的办法探讨》
《一套亿级用户的 IM 架构技术干货(下篇):可靠性、有序性、弱网优化等》

9.3 可靠性
IM 里所谓的可靠性,说直白一点就是保障音讯不失落,这看似天经地义、稀松平时的技术点,在 IM 零碎中又是另一个很大的话题,鉴于自己程度无限,就不班门弄斧,IM 初学者能够能过《零根底 IM 开发入门(三):什么是 IM 零碎的可靠性?》这篇来了解可靠性这个概念。

而后再读读《IM 音讯送达保障机制实现(一):保障在线实时音讯的牢靠投递》、《IM 音讯送达保障机制实现(二):保障离线音讯的牢靠投递》这两篇,基本上就能对 IM 可靠性这个技术要点有了比拟粗浅的意识了。

上面这几篇实战性的总结,适宜有肯定 IM 教训的同行们学习,能够借鉴学习一下:

《融云技术分享:全面揭秘亿级 IM 音讯的牢靠投递机制》
《IM 开发干货分享:如何优雅的实现大量离线音讯的牢靠投递》
《从客户端的角度来谈谈挪动端 IM 的音讯可靠性和送达机制》
《阿里 IM 技术分享(四):闲鱼亿级 IM 音讯零碎的牢靠投递优化实际》

9.4 实时性
IM 实时性这个技术点,就回归到了“即时通讯”这个技术的立身之本了,能够说,没有实时性,也就不存在“即时通讯”这个技术领域了,能够见它的重要性。对于实时性这个概念,初学者能够通过《零根底 IM 开发入门(二):什么是 IM 零碎的实时性?》这篇去学习一下,我就不啰嗦了,人家比我说的好。

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

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

《挪动端 IM 中大规模群音讯的推送如何保障效率、实时性?》
《阿里 IM 技术分享(五):闲鱼亿级 IM 音讯零碎的及时性优化实际》

10、我在我的项目实际中的领会

作为研发者,有两年多的工夫都在保护迭代公司的 IM 音讯零碎,以下是我自已的小小领会。

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

1)业务闭环:音讯是如何写入存储、音讯是如何生产掉、在线音讯是如何实现、离线音讯是如何实现、群聊 / 私聊有何不一样、多端音讯如何实现;
2)解 Bug 填坑:在线音讯收不到,第三方推送证书如何配置;
3)代码优化:单体架构拆分微服务;
4)存储优化:1.0 版本的 redis 存储到 2.0 版本的 redis+mysql;
5)性能优化:未读揭示等接口性能优化。

我的项目还存在可优化的中央:

1)高可用计划之一:是部署多部连贯治理服务器,以撑持更多的用户连贯;
2)高可用计划之二:是对单部连贯治理服务,应用 Netty 进行框架层优化,让一个服务器撑持更多的用户连贯;
3)音讯量剧增时:能够思考对音讯存储作进一步优化;
4)音讯冷热部署:不同的地区会存在业务量差别,比方在某些经济发达的省份,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-37…

正文完
 0