本文援用了沈剑《如何保障 IM 实时音讯的“时序性”与“一致性”?》一文的图片和内容(因为太懒,图没从新画),原文链接在文末。
1、引言
本文接上篇《零根底 IM 开发入门 (三):什么是 IM 零碎的可靠性?》,解说 IM 零碎中音讯时序的一致性问题。
所谓的一致性,在 IM 中通常指的是音讯时序的一致性,那就是:
- 1)聊天音讯的上下文连续性;
- 2)聊天音讯的相对工夫序。
再具体一点,IM 音讯的一致性体现在:
- 1)单聊时:要保障发送方收回聊天音讯的程序与接管方看到的程序统一;
- 2)群聊时:要保障所有群员看到的聊天音讯,与发送者收回音讯时的相对工夫序是统一的。
IM 零碎中音讯时序的一致性问题是个看似简略,实则十分有难度的技术热点话题之一,本文尽量以艰深简显的文字为你解说 IM 音讯时序一致性问题的产品意义、产生起因、解决思路等。
- 学习交换:
- 即时通讯 / 推送技术开发交换 5 群:215477170 [举荐]
- 挪动端 IM 开发入门文章:《新手入门一篇就够:从零开发挪动端 IM》
- 开源 IM 框架源码:https://github.com/JackJiang2011/MobileIMSDK
(本文同步公布于:http://www.52im.net/thread-3189-1-1.html)
2、系列文章
《零根底 IM 开发入门 (一):什么是 IM 零碎?》
《零根底 IM 开发入门 (二):什么是 IM 零碎的实时性?》
《零根底 IM 开发入门 (三):什么是 IM 零碎的可靠性?》
《零根底 IM 开发入门 (四):什么是 IM 零碎的音讯时序一致性?》(* 本文)
《零根底 IM 开发入门 (五):什么是 IM 零碎的安全性?(稍后公布)》
《零根底 IM 开发入门 (六):什么是 IM 零碎的的心跳机制?(稍后公布)》
《零根底 IM 开发入门 (七):如何了解并实现 IM 零碎音讯未读数?(稍后公布)》
《零根底 IM 开发入门 (八):如何了解并实现 IM 零碎的多端音讯漫游?(稍后公布)》
3、音讯时序的一致性,对于 IM 的意义
现如今,因为挪动互联网的遍及,现代人的理论社交关系,简直齐全是靠 IM 这种即时通讯社交工具所组织起来的,IM 这种工具的重要性显而易见。
IM 在现代人的生存中,越来越重要,但也越来平时。当初想分割一个人,第一工夫想到的不是打个电话,而是发个“微信”或“QQ”。是的,IM 这玩意承载的意义越来越多。
音讯时序的一致性问题,对于 IM 的意义,毫无疑问带来的不只是简简单单的所谓用户体验问题。咱们来看看例子。
假如: 你跟女神的表白正进入到要害阶段,聊着聊着就因为这烂 IM,导致聊天音讯前言不搭后语,此刻 1000 公里外你的女神真一脸蒙逼的盯着手机看你的“醉话”,结果是如许重大——这半年来的“舔狗”生存、忍无可忍,算是白白被程序员这群“格子衫”、“地中海”们给祸患了。
再往重大了说,就因为这烂 IM,让你失去了这难得的借助女神低劣基因,革新家族后辈颜值的绝佳机会,无不让人痛心疾首。。。
下面这个例子,说的还只是单聊,如果是群聊,则问题可能还会被有限放大 :试想一个技术交换群,正在强烈的争吵着“php 是世界最好语言”这种话题的时候,突然就想砸手机了,不是因为吵的太凶,而因为音讯程序全乱齐全没法看,曾经重大影响键盘侠们踊跃发表集体意见了。
4、凭什么说保障音讯时序的一致性很艰难?
4.1 根本认知
在一般 IM 用户的眼里,音讯无非是从一台手机传递到另一台手机而已,保障时序有何艰难?
是的,普通用户这么认为,从技术上讲,他只是单纯的将 IM 音讯的收发过程了解为单线程的工作模式而已。
实际上,在 IM 这种高性能场景下,服务端为了谋求高吞吐、高并发,用到了多线程、异步 IO 等等技术。
在这种状况下,“高并发”与“程序”对于 IM 服务端来说,原本就是矛盾的,这就有点鱼与熊掌的滋味了(两者很难兼得)。
所以,要实现 IM 场景下的音讯时序一致性,须要做出衡量,而且要思考的技术维度相当多。这就导致具体技术施行起来没有固定的套路,而因为开发者技术能力的参差不齐,也就使得很多 IM 零碎在理论的成果上会有较大问题,对于用户而言也将间接在产品体验上反馈进去。
上面将具体阐明技术难点所在。
4.2 没有全局时钟
如上图所示,一个真正堪用的生产零碎,显示不可能所有服务都跑在一台服务器上,分布式环境是必定的。
那么: 在分布式环境下,客户端 + 服务端后盾的各种后盾服务,都各自散布在不同的机器上,机器之间都是应用的本地时钟,没有一个所谓的“全局时钟”(也没方法做到真正的全局时钟),那么所谓的音讯时序也就没有真正意义上的时序基准点。所以音讯时序问题显然不是“本地工夫”能够齐全决定的。
4.3 多发送方问题
服务端分布式的状况下,不能用“本地工夫”来保障时序性,那么是否用接管方本地工夫示意时序呢?
遗憾的是,因为多个客户端的存在(比方群聊时),即便是一台服务器的本地工夫,也无奈示意“相对时序”。
如上图所示: 相对时序上,APP1 先收回 msg1,APP2 后收回 msg2,都发往服务器 web1,网络传输是不能保障 msg1 肯定先于 msg2 达到的,所以即便以一台服务器 web1 的工夫为准,也不能精准形容 msg1 与 msg2 的相对时序。
4.4 多接管方问题
多发送方不能保障时序,假如只有一个发送方,是否用发送方的本地工夫示意时序呢?遗憾的是,因为多个接管方的存在,无奈用发送方的本地工夫,示意“相对时序”。
如上图,相对时序上,web1 先收回 msg1,后收回 msg2,因为网络传输及多接管方的存在,无奈保障 msg1 先被接管到先被解决,故也无奈保障 msg1 与 msg2 的解决时序。
4.5 网络传输与多线程问题
既然多发送方与多接管方都难以保障相对时序,那么假如只有繁多的发送方与繁多的接管方,是否保障音讯的相对时序一致性呢?
论断是乐观的,因为网络传输与多线程的存在,这依然不行。
如上图所示,web1 先收回 msg1、后收回 msg2,即便 msg1 先达到(网络传输其实还不能保障 msg1 先达到),因为多线程的存在,也不能保障 msg1 先被解决完。
5、如何保障相对的音讯时序一致性?
通过上一章内容的总结,咱们曾经对 IM 中音讯时序一致性问题所产生的原因,有了较为粗浅的意识。
从纯技术的角度来说,假如:
- 1)只有一个发送方;
- 2)一个接管方;
- 3)上下游连贯只有一条 socket 连贯;
- 4)通过阻塞的形式通信。
这样的状况下,难道不能保障先收回的音讯被先解决,进而被先展现给音讯的接收者吗?
是的,能够!
但理论生产状况下不太可能呈现这种 IM 零碎,必竟单发送方、单接管方、单 socket 连贯、阻塞形式,这样的 IM 一旦做进去,产品经理会立马死给你看。。。
6、实用的优化思路
6.1 一对一单聊的音讯一致性保障思路
假如两人一对一聊天,发送方 A 顺次收回了 msg1、msg2、msg3 三条音讯给接管方 B,这三条音讯该怎么保障显示时序的一致性(发送与显示的程序统一)?
咱们晓得,发送方 A 顺次收回的 msg1、msg2、msg3 三条音讯,到底服务端后,再由服务端直达收回时,这个程序因为多线程的网络的问题,是有可能乱序的。
那么后果就可能是这样:
如上图所示,会呈现与收回时的音讯时序不统一问题(收到的音讯程序是:msg3、msg1、msg2)。
不过,实际上一对一聊天的两个人,并不需要全局音讯时序的统一(因为聊天只在两人的同一会话在产生),只须要对于同一个发送方 A,发给 B 的音讯时序统一就行了。
常见优化计划,在 A 往 B 收回的音讯中,加上发送方 A 本地的一个相对时序(比方本机工夫戳),来示意接管方 B 的展示时序。
那么当接管方 B 收到音讯后,即便极其状况下音讯可能存在乱序达到,但因为这个乱序的时间差对于普通用户来说体感是很短的,在 UI 展示层依照音讯中自带的相对时序排个序后再显示,用户其实是没有太多感知的。
6.2 多对多群聊的音讯一致性保障思路
假如 N 个群友在一个 IM 群里聊天,应该怎么保障所有群员收到音讯的显示时序一致性呢?
首先: 不能像一对聊天那样利用发送方的相对时序来保障音讯程序,因为群聊发送方不单点,工夫也不统一。
或者: 咱们能够利用服务器的单点做序列化。
如上图所示,此时 IM 群聊的发送流程为:
- 1)sender1 收回 msg1,sender2 收回 msg2;
- 2)msg1 和 msg2 通过接入集群,服务集群;
- 3)service 层到底层拿一个惟一 seq,来确定接管方展现时序;
- 4)service 拿到 msg2 的 seq 是 20,msg1 的 seq 是 30;
- 5)通过投递服务讲音讯给多个群友,群友即便接管到 msg1 和 msg2 的工夫不同,但能够对立依照 seq 来展示。
这个办法:
- 1)长处是:能实现所有群友的音讯展现时序雷同;
- 2)毛病是:这个生成全局递增序列号的服务很容易成为零碎瓶颈。
还有没有进一步的优化办法呢?
从技术角度看: 群音讯其实也不必保障全局音讯序列有序,而只有保障一个群内的音讯有序即可,这样的话,“音讯 id 序列化”就成了一个很好的思路。
上图这个计划中,service 层不再须要去一个对立的后端拿全局 seq(序列号),而是在 service 连接池层面做细小的革新,保障一个群的音讯落在同一个 service 上,这个 service 就能够用本地 seq 来序列化同一个群的所有音讯,保障所有群友看到音讯的时序是雷同的。
对于 IM 的零碎架构下应用怎么样实现音讯序列化,或者说全局音讯 ID 的生成计划,这又是另一个很热门的技术话题。
有趣味,能够深刻浏览上面这个系列:
《IM 音讯 ID 技术专题 (一):微信的海量 IM 聊天音讯序列号生成实际(算法原理篇)》
《IM 音讯 ID 技术专题 (二):微信的海量 IM 聊天音讯序列号生成实际(容灾计划篇)》
《IM 音讯 ID 技术专题 (三):解密融云 IM 产品的聊天音讯 ID 生成策略》
《IM 音讯 ID 技术专题 (四):深度解密美团的分布式 ID 生成算法》
《IM 音讯 ID 技术专题 (五):开源分布式 ID 生成器 UidGenerator 的技术实现》
《IM 音讯 ID 技术专题 (六):深度解密滴滴的高性能 ID 生成器 (Tinyid)》
这个系列中,尤其微信的趋势递增 ID 生成思路(留神:趋势递增不是严格递增,趋势递增意味着中问有 ID 被跳过也没事),对于分布式 IM 的音讯 ID 来说是十分切实可行的。
是的,对于 IM 零碎来说,相对意义上的时序很难保障,但通过服务端生成的枯燥递增音讯 ID 的形式,利用递增 ID 来保障时序性,也是一个很可性的计划。
7、小结一下
IM 零碎架构下,音讯的相对时序是很艰难的,起因多种多样,比方:没有全局时钟、多发送方、多接管方、多线程、网络传输不确定性等。
一对一单聊时,其实只须要保障收回的时序与接管的时序统一,就根本能让用户感觉不到乱序了。
多对多的群聊状况下,保障同一群内的所有接管方音讯时序统一,也就能让用户感觉不到乱序了,办法有两种,一种单点相对时序,另一种实现音讯 id 的序列化(也就是实现一种全局递增音讯 ID)。
8、参考资料
[1] 如何保障 IM 实时音讯的“时序性”与“一致性”?,作者:沈剑
[2] 一个低成本确保 IM 音讯时序的办法探讨,作者:封宇
附录:更多 IM 开发热门技术点
《挪动端 IM 开发者必读 (一):通俗易懂,了解挪动网络的“弱”和“慢”》
《挪动端 IM 开发者必读 (二):史上最全挪动弱网络优化办法总结》
《古代挪动端网络短连贯的优化伎俩总结:申请速度、弱网适应、平安保障》
《挪动端 IM 中大规模群音讯的推送如何保障效率、实时性?》
《挪动端 IM 开发须要面对的技术问题》
《开发 IM 是本人设计协议用字节流好还是字符流好?》
《请问有人晓得语音留言聊天的支流实现形式吗?》
《如何保障 IM 实时音讯的“时序性”与“一致性”?》
《一个低成本确保 IM 音讯时序的办法探讨》
《IM 单聊和群聊中的在线状态同步应该用“推”还是“拉”?》
《IM 群聊音讯如此简单,如何保障不丢不重?》
《谈谈挪动端 IM 开发中登录申请的优化》
《挪动端 IM 登录时拉取数据如何作到省流量?》
《浅谈挪动端 IM 的多点登录和音讯漫游原理》
《齐全自已开发的 IM 该如何设计“失败重试”机制?》
《微信对网络影响的技术试验及剖析(论文全文)》
《IM 开发基础知识补课 (五):通俗易懂,正确理解并用好 MQ 音讯队列》
《微信技术分享:微信的海量 IM 聊天音讯序列号生成实际(算法原理篇)》
《IM 开发基础知识补课 (六):数据库用 NoSQL 还是 SQL?读这篇就够了!》
《IM 里“左近的人”性能实现原理是什么?如何高效率地实现它?》
《IM 的扫码登录性能如何实现?一文搞懂支流利用的扫码登录技术原理》
《IM 开发宝典:史上最全,微信各种性能参数和逻辑规定材料汇总》
本文已同步公布于“即时通讯技术圈”公众号。
▲ 本文在公众号上的链接是:点此进入,原文链接是:http://www.52im.net/thread-3189-1-1.html