Jack Jiang

我的最新工程MobileIMSDK:http://git.oschina.net/jackjiang/MobileIMSDK
posts - 504, comments - 13, trackbacks - 0, articles - 1

     摘要: 本文引用了ELab团队、腾讯大讲堂两个公众号分享的文章内容,引用内容见文末参考资料,感谢原作者的无私分享。1、引言对于市面上主流的IM来说,跟二维码有关的功能,比如扫码加好友、扫码登陆、扫码加群等,都是很常见的。这是微信的扫码登录功能:这是微信的扫码加好友功能: 二维码技术使用起来很简单,本系列的前三篇文章也专门针对IM扫码登录这个功能做了详细的分享,但本着学习技术不留死角的习惯,我认为...  阅读全文

posted @ 2021-11-01 19:49 Jack Jiang 阅读(157) | 评论 (0)编辑 收藏

     摘要: 本文由融云技术团队原创分享,原题“万字干货:IM “消息”列表卡顿优化实践”,为使文章更好理解,内容有修订。1、引言随着移动互联网的普及,无论是IM开发者还是普通用户,IM即时通讯应用在日常使用中都是必不可少的,比如:熟人社交的某信、IM活化石的某Q、企业场景的某钉等,几乎是人人必装。以下就是几款主流的IM应用(看首页就知道是哪款,我就不废话了):正...  阅读全文

posted @ 2021-10-26 14:41 Jack Jiang 阅读(147) | 评论 (0)编辑 收藏

本文由阿里闲鱼技术团队有攸分享,原题“向消息延迟说bybye:闲鱼消息及时到达方案”,有修订和改动,感谢作者的分享。

1、引言

IM消息作为闲鱼用户重要的交易咨询工具,核心目标有两点:

  • 1)第一是保证用户的消息不丢失;
  • 2)第二是保证用户的消息及时送达接收方。

IM消息根据消息的接收方设备是否在线,分为离线和在线推送。数据显示目前闲鱼每天有超过一半以上的IM消息是走在线通道的,而在线消息的到达率、及时性是直接影响用户体验的。

本文将根据闲鱼IM消息系统在消息及时性方面的优化实践,详细分析了IM在线通道面临的各种技术问题,并通过相应的技术手段来优化从而保证用户消息的及时到达。

PS:如果您对IM消息可靠性还没有概念,建议先阅读这篇入门文章《零基础IM开发入门(二):什么是IM系统的实时性?》。

学习交流:

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

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

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

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

2、系列文章

本文是系列文章的第5篇,总目录如下:

3、当前面临的问题

3.1 端内长连接中断

在IM场景中,用户与云端通信频繁,且为了实现用户的消息及时到达,往往采用云端下推消息的方式触达用户,所以用户在线时设备与云端会维持一条TCP长连接通道,可以更轻量级的与服务端进行交互,现代IM即时通讯的下行消息都是通过长连下发的。

当前闲鱼IM消息系统使用的是ACCS长连接,ACCS是淘宝无线提供的全双工、低延时、高安全的通道服务。

但由于用户设备网络状态的不确定性,可能会发生各种各样的网络异常情况导致ACCS长连接通道中断。而长连接一旦意外中断,就会导致用户无法及时收到在线消息。

针对这个问题,我们需要尽可能及时的感知到长连中断并尝试重连。具体的优化思路会在本文后面的内容中分享。

3.2 下推的消息未达

感知长连中断并重连只能在大多数时间保证长连接的有效性,但是在长连接无效或不稳定期间下推的消息客户端可能根本收不到。

简单说就是仅仅有重连机制无法保证下行消息必达,可能有以下场景导致下行消息失败:

  • 1)服务端发送下行消息时长连畅通,消息在传输路上通道断掉,客户端无法收到;
  • 2)设备的在线状态存在延迟,服务端下行消息时认为设备在线,实际上设备已经离线,无法收到;
  • 3)客户端收到了下行消息,但端上后续处理失败(比如落库失败,消息没有成功展示给用户)。

我们通过数据埋点统计得出,ACCS长连接的下行成功率在97%左右。

ACCS长连接的下行成功率的统计方法如下:

ACCS下行成功率 = 通过ACCS成功下行且客户端收到的消息量 / 服务端认为通过ACCS成功下行的消息量

有心急的同学就要问了,丢了3%的消息吗?

并没有!这3%的消息不会丢失,只是不保证及时触达给用户。

我们的消息同步模型是推拉结合模式,在用户拉取消息时会拉取到设备当前位点与服务端最新位点的所有消息,ACCS下行失败的消息会通过主动拉模式获取到,但客户端主动拉取消息的触发时机有限。

当前客户端主动拉取消息的触发时机主要有以下几个:

  • 1)用户冷启动app,主动同步消息;
  • 2)用户主动下拉刷新;
  • 3)app后台切换前台;
  • 4)收到一条推送消息,客户端发现新消息的位点跟本地最新的位点有gap,触发同步。

可见:上述主动同步消息的触发很大程度上依赖用户行为或者有没有收到新消息,难以保证消息及时到达。

如果是用户高频打开的IM软件,这样也不会有太大的问题。但是闲鱼app的活跃度较低,有时候甚至依赖IM消息拉活,而且一条延迟的消息触达可能导致用户错过一笔交易,闲鱼消息不允许有这样的延迟发生。

基于上述分析,我们先描述一个数据指标来反映现状。

通过上面的描述可知:ACCS消息并不全都是推下来的,也可能是主动拉下来的。如果是推,必定可以及时到达;如果是拉,则受限于用户行为。

拉的这部分消息,我们定义为ACCS消息补偿到达,然后计算ACCS消息补偿到达耗时,消息范围限定为服务端ACCS成功下行但是客户端通过主动拉取同步到的消息,以往的版本这个数据在60分钟左右。

注意:这个数据并不是消息触达到用户的耗时,因为如果在线转离线触达,拉取到消息的时间取决于用户行为(用户何时打开了app),但这个数据也能大致反映在线消息的到达延迟状况。

ACCS长连接的消息补偿到达耗时的统计方法如下:

ACCS消息补偿到达耗时 = 客户端通过拉获取到ACCS消息的时间 - 服务端ACCS下行时间

接下来本文将从长连接的重连和未达消息重发两个方面详细讲述我们是如何优化在线通道稳定性的,从而优化并保证消息的及时到达。

4、优化手段1:增加长连接重连机制

4.1 长连接为什么会中断?

有因必有果,我们先来分析下有哪些原因会导致连接中断。

对于IM这种场景下来说,通常可能有以下原因:

  • 1)用户设备断网;
  • 2)设备发生了网络切换;
  • 3)设备处于弱网环境,网络不稳定;
  • 4)设备网络正常,TCP连接由于NAT超时导致连接被运营商中断。

对于APP来说,如果是用户操作导致网络状态变化的情况,会有网络状态变化事件通知,这种情况可以监听事件并主动尝试重连。但现实中的大多数情况都是“意料之外”(正如上面列举的这些断网可能性一样)。

那么既然“意料之外”的断网无法预知,技术上可以如何有效的感知到各种异常状况呢?

PS:如果要透彻理解断网、弱网、TCP链接有效性,并不是本文能讲的清楚的,可以参照下面的资料深入理解一下,值得好好学习。

关于TCP链接本身的有效性问题,可以读以下两篇:

  1. 为何基于TCP协议的移动端IM仍然需要心跳保活机制?
  2. 不为人知的网络编程(十二):彻底搞懂TCP协议层的KeepAlive保活机制

关于移动网络的复杂性问题,可以从以下几篇入门的科普文章学习一下:

  1. IM开发者的零基础通信技术入门(十一):为什么WiFi信号差?一文即懂!
  2. IM开发者的零基础通信技术入门(十二):上网卡顿?网络掉线?一文即懂!
  3. IM开发者的零基础通信技术入门(十三):为什么手机信号差?一文即懂!
  4. IM开发者的零基础通信技术入门(十四):高铁上无线上网有多难?一文即懂!

关于移动弱网带来的各种问题、优化方案等,可以通过以下几篇系统学习一下:

  1. 现代移动端网络短连接的优化手段总结:请求速度、弱网适应、安全保障
  2. 移动端IM开发者必读(一):通俗易懂,理解移动网络的“弱”和“慢”
  3. 移动端IM开发者必读(二):史上最全移动弱网络优化方法总结
  4. 百度APP移动端网络深度优化实践分享(三):移动端弱网优化篇

4.2 心跳检测机制

像大多数链路保活场景一样,IM这种场景下最有效的检测手段就是心跳检测(如果你对TCP链路保活还没有什么概念,建议先读《为何基于TCP协议的移动端IM仍然需要心跳保活机制?)。

原理就是:客户端通过定时发送心跳包,服务端收到心跳包后再反馈给客户端,通过客户端和服务端这一来一去的配合,就可以实现客户服和服务端各自都能感知到连接是否中断。

从及时性效果来看:心跳间隔越短越好,而频繁的心跳检测势必会带来用户流量以及电量的损耗,所以我们的实现目标是如何尽可能少的心跳检测而又尽量及时地感知到长连中断的意外情况。

状态机+消息心跳队列:

在心跳协议设计上,要注意心跳包的核心目标是检测长连通道是否畅通,客户端主动上行心跳包且能收到服务端回包,就认为长连通道健康。所以心跳的上行消息以及回包的数据包应尽可能小。一般来说,通过协议头标识心跳包及响应即可(这样就能节省协议包大小)。

PS:关于心跳机制的入门文章可以详读《一文读懂即时通讯应用中的网络心跳包机制:作用、原理、实现思路等》。

4.3 心跳策略

心跳策略是实现我们上述目标的核心机制,本文仅简单列举几种心跳策略。

比如以下这几种:

  • 1)短心跳检测 初始状态连续 ping 3次 收到 ACK 后,可以认为进入稳定状态;
  • 2)常规固定时长心跳(根据app状态不同,频率可调Mid+,Mid-, Long);
  • 3)自适应心跳 根据设备网络状态变化自动适应的心跳间隔;
  • 4)冗余心跳,app后台切前台,主动心跳一次。

关于心跳策略的详细设计甚至可以单独写一篇文章,有兴趣的同学可以阅读以下推荐的文章继续深入研究。

5、优化手段2:消息ACK应答与重发机制

5.1 概述

为了解决上面的问题,我们同时也引入了消息ACK应答与重发机制。

整体思路是:客户端在收到ACCS消息并处理成功后,给服务端回一个ACK应答包,服务端下发ACCS消息时将消息加入重试队列,收到ACK应答包后更新消息到达状态,并终止重试。

整体设计流程图如下:

该方案的难点即重试处理器的实现设计,接下来我们将重点讲述这部分的详细设计。

5.2 重试队列存储设计

我们采用阿里云表格存储TimeLine模型来存储下行消息的到达状态。Timeline 模型是针对消息数据场景所设计的数据模型,它能满足消息数据场景对消息保序、海量消息存储、实时同步的特殊需求,在IM、Feed流等消息场景应用广泛。(关于TimeLine模型,这里有篇详细的文章可以学习一下《现代IM系统中聊天消息的同步和存储方案探讨

我们给每个用户设备定义一个TimeLine,timeline-id定义为userId_deviceId,sequenceId自定义为消息位点。

存储结构如下:

每通过ACCS成功下行一条消息,则插入到接收用户设备的TimeLine中,收到ACK后根据消息id更新消息到达状态。

同时由于重试动作只发生在下行消息后较短的一段时间内,所以我们设置一个比较短的全局过期时间即可,避免数据膨胀。

5.3 延迟重试设计

如上图所示:

  • 1)每通过ACCS下发一条消息,先插入到Timeline中,初始状态为未达,然后生产一条延迟N秒的延迟消息;
  • 2)每次消费到延迟消息后,读取tablestore中该消息的到达状态,如到达则终止延迟,否则继续;
  • 3)每次重试先判断设备是否在线,如果设备不在线,转发离线通道并终止重试,如果设备在线,则重推未到达的消息,并再次延迟N秒消费;
  • 4)每条消息的重试生命周期中用的同一条延迟消息,最多重试消费M次,超过次数不再重试并打日志埋点(后续可以监控这种情况并基于这个数据进行优化)。

5.4 延迟重发策略

延迟重发策略是指在重发流程中,如何选择合适的延迟时间来使得重发的效率最高。

不同用户在不同时间、地点所处的网络环境差别较大,网络恢复到稳定态所需要的时间也有差异,需要选用合适的延迟策略来保证重发效率。

最优的延迟策略的目标是在最短的时间内,使用最少的重发次数将消息投递成功。以下是几种可选的方案。

5.4.1)固定延迟时间:

要想找到最优的延迟策略,必须从数据中通过分析得到答案,天马行空的想象往往离实际相差甚远。

我们先采用固定的延迟时间(10s)最大重试6次来分析一波数据:

通过这组数据可以看到:有约85%的消息在40s内重发可以投递成功,还有12%的消息在达到最大重试次数后依旧没有收到ACK。在4次重试之后,第5次成功只有2.03%,第6次只有0.92%,继续重发的收益已经变得很低。

6次以后还有部分消息没有收到ACK,这部分消息如果用固定延迟时间策略,性价比很低,频繁重发浪费系统资源,我们需要继续改进策略。

5.4.2)固定延迟+固定步长递增:

考虑到部分用户的网络短时间无法恢复,频繁的短间隔重发价值不大,我们采用4次固定短间隔延迟N秒后,每次延迟时间都是上一次延迟时间递增固定步长M秒的策略。直到收到ACK、用户设备离线或者达到了最大延迟时间MAX(N)。

这种策略一定程度上可以解决固定延迟时间重发策略的问题,但如果用户短时间网络无法恢复,每次重发都要重新递增,也不是一种最优解。

5.4.3)自适应延迟:

设计流程图:

如上图:我们最终衍生出了自适应延迟策略。

自适应延迟是指:根据用户的网络状况,采取自动调整的延迟时间,以期望达到最高的重发效率。

具体是:新消息先通过4次固定N秒的短延迟来探测设备的网络状况,一旦网络恢复,我们将设备的N值清空(设备N值是指根据上几次重发经验,当前设备网络能回复ACK所需要的最短时间,默认情况该值为空,代表用户设备网络正常)。4次重发后依旧收不到ACK,我们尝试读取设备N值,如果为空,则取初始值,以后每次延迟都递增固定步长M,并在重发后更新当前设备的N值,直到消息收到ACK或者达到了最大延迟时间MAX(N)。

5.5 新老版本兼容性

需要注意的是老版本的app是不会回ACK的,如果下发给老版本设备的消息也加入重试队列,那此类消息将一直重试到最大次数才会终止,无端消耗资源。

所以我们设计在ACCS长连建立之后,客户端主动上行一条设备信息,其中包含app的版本号,服务端存储一定时间,在将消息加入重试队列之前,先校验接收者设备app的版本号,符合要求再加入重试队列。

6、 最终优化后的效果

消息重连重发方案上线后,我们上面定义的指标 ACCS补偿到达时间 从60分钟大幅降低至15分钟,降幅达75%。

从而印证了我们的技术分析,同时用户有关消息延迟的舆情反馈大幅下降,可见消息重发机制对保证用户消息及时到达成效显著。

7、未来展望

消息在线通道的稳定性优化至此已告一段落,未来我们将继续优化闲鱼消息的使用体验,包括基础功能的完善以及基础体验的提升。

基础功能方面:我们在近期的版本中已经支持了消息撤回、草稿功能,后续将逐步支持发送定位,会话分组、备注,消息搜索等功能。

基础体验方面:我们对消息的UI样式做了优化升级,并优化了app消息tab页的cpu及内存使用,后续将继续从流量、电量、性能方面继续优化消息的使用体验。

附录:参考资料

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

[2] 不为人知的网络编程(十二):彻底搞懂TCP协议层的KeepAlive保活机制

[3] 现代IM系统中聊天消息的同步和存储方案探讨

[4] 现代移动端网络短连接的优化手段总结:请求速度、弱网适应、安全保障

[5] 移动端IM开发者必读(二):史上最全移动弱网络优化方法总结

[6] IM开发者的零基础通信技术入门(十二):上网卡顿?网络掉线?一文即懂!

[7] IM开发者的零基础通信技术入门(十三):为什么手机信号差?一文即懂!

[8] 移动端IM实践:实现Android版微信的智能心跳机制

[9] 融云技术分享:融云安卓端IM产品的网络链路保活技术实践

[10] Web端即时通讯实践干货:如何让你的WebSocket断网重连更快速?

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

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

posted @ 2021-10-19 23:05 Jack Jiang 阅读(261) | 评论 (0)编辑 收藏

     摘要: 本文由作者“阿宝哥”分享,原题“你不知道的 WebSocket”,有修订和改动。1、引言本文将从基本概念、技术原理、常见易错常识、动手实践等多个方面入手,万字长文,带你一起全方位探索 WebSocket 技术。阅读完本文,你将了解以下内容:1)了解 WebSocket 的诞生背景、WebSocket 是什么及它的优点;2)了解 WebSocket 含...  阅读全文

posted @ 2021-10-11 14:35 Jack Jiang 阅读(173) | 评论 (0)编辑 收藏

     摘要: 本文由阿里闲鱼技术团队景松分享,原题“到达率99.9%:闲鱼消息在高速上换引擎(集大成)”,有修订和改动,感谢作者的分享。1、引言在2020年年初的时候接手了闲鱼的IM即时消息系统,当时的消息存在各种问题,网上的用户舆情也是接连不断。典型的问题,比如:1)“聊天消息经常丢失”;2)“消息用户头像乱了”;3)“订单状...  阅读全文

posted @ 2021-09-26 14:47 Jack Jiang 阅读(224) | 评论 (0)编辑 收藏

本文由阿里闲鱼技术团队今朝、有攸分享,本次有修订。

1、引言

闲鱼即时消息系统历经数代迭代,目前已能稳定的支撑亿级消息体量。

在此消息系统的建设过程中,我们经历了从简单到复杂、从困扰到破局,每一次的技术改变都是为了更好的解决当下业务所面临的问题。

本文分享的是闲鱼即时消息系统架构从零开始的技术变迁之路,以期更多的同行们在此基础上汲取经验,得到有价值的启发。

学习交流:

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

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

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

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

2、系列文章

本文是系列文章的第3篇,总目录如下:

  1. 阿里IM技术分享(一):企业级IM王者——钉钉在后端架构上的过人之处
  2. 阿里IM技术分享(二):闲鱼IM基于Flutter的移动端跨端改造实践
  3. 阿里IM技术分享(三):闲鱼亿级IM消息系统的架构演进之路》(* 本文
  4. 阿里IM技术分享(四):闲鱼亿级IM消息系统的可靠性投递技术实践》(* 稍后发布

3、1.0版:业务初创期、最小化可用

3.1 技术背景

2014年启动闲置交易独立APP “闲鱼”,一期构建完成APP主链路,包含商品:发布→搜索→商品详情→IM会话→交易

作为初创app,业务需尽快上线验证效果,技术建设上需要完成闲鱼消息从无到有的系统搭建。

3.2 技术方案

作为即时通讯系统,最小化能力包含:

  • 1)消息存储:会话、摘要、消息;
  • 2)消息同步:推、拉;
  • 3)消息通道:长连接、厂商推送。

与一般IM会话模型不同的是,闲鱼会话以商品为主体,“人+人+商品”为要素构建会话。

因会话模型的差异,淘系已有的消息系统,短期内无法满足业务需求,而闲鱼完全自建消息系统耗时巨大。

为了保障业务高效上线,技术选型上最大化复用已有系统能力,避免重复造轮子。

所以,我们的技术方案是:

  • 1)数据模型与底层存储依赖淘系私信体系进行建设;
  • 2)消息数据获取上,客户端全量从服务端拉取消息数据;
  • 3)通讯协议使用来往SDK及mtop。

总体架构如下图,以此模式完成快速交付保障了业务最小化可用:

4、2.0版:用户量增速快、需要重建消息系统

4.1 技术背景

闲鱼用户量正快速突破100万,即时消息服务的调用量暴涨。在这样的背景下,用户反馈消息数据获取的卡顿、白屏成为常态,大量的消息Push发送下,系统告警频发。

造成这些问题的原因:1.0版的架构模式下,获取消息数据全量拉模式,客户端纯UI不做数据存储。

具体就是:

  • 1)当用户需要查看消息数据时,数据拉取成功与否取决于网络、数据访问速度,偶发性的造成卡顿、白屏;
  • 2)中心化的数据存储,读远大于写,高并发下,服务端负载过大。

针对第2)点:比如1W个用户同时在线聊天,按照当前架构并发拉取全量消息,估算5万QPS。不妨假设,同时在线聊天用户数10万时,对服务端压力可想而知。

4.2 技术方案

基于上述问题,我们决定重建消息系统架构,以应对未来更大的用户增量。

回归到IM系统的核心功能:

4.2.1)消息存储模型:

  • 1)会话模型:由owner、itemid、user、sessionType 来标识唯一会话,增加扩展属性支持个性化;
  • 2)摘要模型:作为用户会话视图,同一会话的不同用户可个性化呈现,由userid、sid标识唯一摘要;
  • 3)消息模型:由sender、消息内容、消息版本、sid组成。sid+消息版本唯一确定一条消息;
  • 4)指令模型:是一种双端约定,由服务端下发,客户端执行的指令集。如免打扰指令、删除指令等。

4.2.2)消息通道:

1)在线通道:使用淘宝无线ACCS长连接提供的全双工、低延时、高安全的通道服务;

2)离线通道:使用淘宝消息推送平台AGOO. 其屏蔽了各主流厂商对接的复杂度,直接对业务系统提供服务。

4.2.3)消息同步模型:

1)客户端建立数据库,存储消息数据:当消息数据存储在本地设备上,消息同步从全量拉取优化为全量+增量同步结合的模式。

增量和全量同步具体指的是:

  • a. 增量同步:客户端存储消息位点信息,通过与服务端最新位点比较,仅同步增量消息;
  • b. 全量同步:当用户卸载重装或位点gap过大时,客户端全量拉取历史消息数据,进行端上数据重建。

2)服务端建设个人消息域环(收件箱模型):以和客户端进行增量数据同步。同时,1.0版本架构中存在的读多写少的问题,通过个人域环的写扩散来平衡读写压力。

下图为一条消息从发送到接收的过程以及服务端和客户端的执行流程:

如上图所示:假设Ua给Ub发送一条消息,消息写扩散至Ua和Ub的各自的域环中:

  • 1)当客户端online时,接收到推送的消息位点=当前端上域版本+1,本地消息数据库merge即可;
  • 2)当客户端offline时,仅进行离线推送通知,当用户重新上线时,进行数据同步,由服务端判断触发增量同步还是全量同步。

针对第2)点,具体逻辑是:

  • 1)如果域环版本差值小于阀值,增量同步后,进行本地消息数据库merge;
  • 2)当域环版本差值大于阀值,进行全量消息拉取,做端上数据重建。

整个同步逻辑基于闲鱼的即时消息域环,域环可以看作是有着固定容量的用户消息收件箱,给一个用户发送的所有消息都会同步到他的域环中。

具体就是:

  • 1)域环存储:域环需要支持高并发数据读写,使用阿里分布式KV存储系统tair来实现;
  • 2)域环容量:为减少全量消息同步,以用户下次进入闲鱼需要同步的平均消息量来规划个人域环容量。同时利用FIFO循环覆盖历史数据;
  • 3)域环版本:用户当前消息位点,在消息进入个人域环时通过tair的counter实现域环版本严格连续递增,用于全量、增量同步判断。

上述建设完成后,闲鱼具备了自己独立的即时消息系统,当下遇到的问题得到了缓解,用户体验度有大幅提升。

5、3.0版:随着业务快速发展,系统稳定性需得到保障

5.1 技术背景

随着闲鱼业务生态的丰富,IM会话与消息内容类型不断扩展,同时在用户量的快速增长下,用户反馈消息收不到、消息延迟等舆情问题日渐突出。

5.2 问题分析

问题1:闲鱼app进程无有效保活机制,app退到后台后进程很快就会被系统挂起,导致长连接中断。此时消息推送走厂商通道,而厂商通道的实时性较差,且对消息推送的优先级设定有差异,从而造成用户感知消息延迟。

问题2:accs在线消息推送时,平均延时较短,但存在假连情况。而且目前的消息推送链路无ack机制,造成服务端以为消息发出去了但实际上客户端并没有收到,用户下次打开app后才能看到消息,用户感知消息延迟。

PS:造成假连接的原因主要是用户退到后台,accs长连中断,但是设备状态更新有延时。

问题3:目前消息同步的推模式(accs push)、拉模式(mtop),客户端未做隔离,异步进行处理,导致在某些极端情况下消息数据库处理异常,引发消息丢失。

如:某用户上线后连续收到多条消息,其中一条触发域黑洞,在进行消息同步端上数据重建时,小概率处理出错。

问题4:大部分线上消息问题发现靠舆情反馈,如消息错乱,出问题后系统无感知、无补救措施且排查困难,仅能跟随版本做修复。

问题5:业务不断丰富,孵化出基于消息系统的服务号及小程序内容营销、消息群组等,各类消息发送链路共用域环与数据存储,造成稳定性问题。

如:个人域环的消息包括IM聊天和营销消息,IM聊天由用户触发,需要保证强到达;而营销消息一般是由系统通过班车等方式批量发送,消息量级大,tps高,影响IM服务稳定性。

5.3 解案决方案

基于上述分析,我们逐个问题进行专项解决。

1)消息重发与推拉隔离:

如上图所示:

  • a. ACK:保障消息及时到达。服务端下行accs消息时,将消息加入重试队列并延迟重试,客户端在收到accs消息并处理成功后,给服务端回一个ack,服务端收到ack后更新消息到达状态,并终止重试,以此避免设备假连或网络不稳定的情况;
  • b. 重发:根据延迟重发策略决定何时重发消息,保障消息确定性到达。自适应延迟重发策略是指新消息先通过4次固定N秒的短延迟来探测设备的网络状况,然后根据网络状况来递增固定步长M的延迟策略,这种策略可以保障在最短的时间内,使用最少的重发次数将消息投递成功;
  • c. 消息队列:端上引入消息队列,按顺序处理消息,保证消息处理的准确性。同时进行推拉隔离,保障队列有序消费,解决了复杂状况下并发处理消息数据合并出错的问题。

2)数据存储拆分:

闲鱼每天发送的即时消息中有一半以上是营销消息,营销消息的发送具有明显的波峰波谷流量,高峰期会导致消息数据库抖动,影响IM消息。我来对消息、摘要、域环存储做业务隔离,以适应不同业务场景对稳定性不同的要求。

具体做法是:

  • 1)IM消息需要极高的稳定性保证,其消息及摘要继续使用mysql存储;
  • 2)营销消息存储周期短,稳定性要求低于IM,采用Lindorm存储;
  • 3)域环做实例级别隔离,保证IM域环的容量不会被其他消息占用,从而影响到消息同步。

PS:Lindorm是一种多模型的云原生数据库服务,具有成本低、自定义TTL、容量横向扩展等优势。

3)线上问题发现与恢复:

保障稳定性的关键要素是做好各种核心指标的监控,而监控首先要有数据来源,对服务端+客户端的关键链路节点埋点,基于集团UT、SLS,通过blink进行实时清洗、计算,最终形成统一规范的日志数据落至SLS,以供实时监控及链路排查。

消息系统的核心目标是保障用户消息发的出、收得到且及时收到,所以我们通过计算发送成功率、到达率、消息延迟来监控系统的稳定性。

此外,为了解决用户舆情排查困难的问题:

  • 1)我们设计了一套指令集,通过约定指令协议,服务端向指定用户下发指令,客户端执行对应指令进行异常数据上报,提高排查效率;
  • 2)扩展了强制全量同步、数据校正等指令,定向修复用户消息数据问题,相较以往出现严重bug只能让用户卸载重装解决,这种方式显然对用户是更友好的。

经过一系列专项治理,技术类舆情下降50%,从0到1建设了消息稳定性体系,用户体验进一步提升。

6、展望未来

闲鱼作为电商交易APP, 其中IM是交易的前置链路,IM的产品体验极大影响用户交易效率。

前段时间进行用户调研,从闲鱼IM的NPS低于预期(NPS是用户忠诚度衡量指标 = 推荐者%-贬损者%)。

从用户反馈来看:

  • 1)部分用户对产品功能有较强烈的诉求,诸如消息搜索、分组等;
  • 2)大部分用户对发送消息过程中的违规问题难以理解;
  • 3)仍有较多舆情反馈消息收不到或延迟。

映射到目前闲鱼的即时消息系统上,我们的系统架构依然有很多需要持续改进的地方。

典型的如:同步协议冗余,在需求迭代过程中容易引发问题、有效保活机制的缺失对消息即时送达的影响、小众机型离线消息收不到、多年的数据积累在线库臃肿等问题,影响着闲鱼业务迭代速度与NPS。

作为技术团队,下一步将提升NPS作为核心技术目标,闲鱼的即时消息系统4.0版架构正在路上 ......

附录:更多相关文章

[1] 更多阿里巴巴的技术资源:

阿里钉钉技术分享:企业级IM王者——钉钉在后端架构上的过人之处

现代IM系统中聊天消息的同步和存储方案探讨

阿里技术分享:深度揭秘阿里数据库技术方案的10年变迁史

阿里技术分享:阿里自研金融级数据库OceanBase的艰辛成长之路

来自阿里OpenIM:打造安全可靠即时通讯服务的技术实践分享

钉钉——基于IM技术的新一代企业OA平台的技术挑战(视频+PPT) [附件下载]

阿里技术结晶:《阿里巴巴Java开发手册(规约)-华山版》[附件下载]

重磅发布:《阿里巴巴Android开发手册(规约)》[附件下载]

作者谈《阿里巴巴Java开发手册(规约)》背后的故事

《阿里巴巴Android开发手册(规约)》背后的故事

干了这碗鸡汤:从理发店小弟到阿里P10技术大牛

揭秘阿里、腾讯、华为、百度的职级和薪酬体系

淘宝技术分享:手淘亿级移动端接入层网关的技术演进之路

难得干货,揭秘支付宝的2维码扫码技术优化实践之路

淘宝直播技术干货:高清、低延时的实时视频直播技术解密

阿里技术分享:电商IM消息平台,在群聊、直播场景下的技术实践

阿里技术分享:闲鱼IM基于Flutter的移动端跨端改造实践

阿里IM技术分享(三):闲鱼亿级IM消息系统的架构演进之路

 

[2] 有关IM架构设计的文章:

浅谈IM系统的架构设计

简述移动端IM开发的那些坑:架构设计、通信协议和客户端

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

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

从零到卓越:京东客服即时通讯系统的技术架构演进历程

蘑菇街即时通讯/IM服务器开发之架构选择

腾讯QQ1.4亿在线用户的技术挑战和架构演进之路PPT

微信后台基于时间序的海量数据冷热分级架构设计实践

微信技术总监谈架构:微信之道——大道至简(演讲全文)

如何解读《微信技术总监谈架构:微信之道——大道至简》

快速裂变:见证微信强大后台架构从0到1的演进历程(一)

移动端IM中大规模群消息的推送如何保证效率、实时性?

现代IM系统中聊天消息的同步和存储方案探讨

微信朋友圈千亿访问量背后的技术挑战和实践总结

子弹短信光鲜的背后:网易云信首席架构师分享亿级IM平台的技术实践

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

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

社交软件红包技术解密(一):全面解密QQ红包技术方案——架构、技术实现等

从游击队到正规军(一):马蜂窝旅游网的IM系统架构演进之路

从游击队到正规军(二):马蜂窝旅游网的IM客户端架构演进和实践总结

从游击队到正规军(三):基于Go的马蜂窝旅游网分布式IM系统技术实践

瓜子IM智能客服系统的数据架构设计(整理自现场演讲,有配套PPT)

IM开发基础知识补课(九):想开发IM集群?先搞懂什么是RPC!

阿里技术分享:电商IM消息平台,在群聊、直播场景下的技术实践

一套亿级用户的IM架构技术干货(上篇):整体架构、服务拆分等

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

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

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

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

IM开发技术学习:揭秘微信朋友圈这种信息推流背后的系统设计

阿里IM技术分享(三):闲鱼亿级IM消息系统的架构演进之路

>> 更多同类文章 ……

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

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

posted @ 2021-09-13 15:12 Jack Jiang 阅读(329) | 评论 (0)编辑 收藏

     摘要: 本文引用自“ 豆米博客”的《JS实时通信三把斧》系列文章,有优化和改动。1、引言有关Web端即时通讯技术的文章我已整理过很多篇,阅读过的读者可能都很熟悉,早期的Web端即时通讯方案,受限于Web客户端的技术限制,想实现真正的“即时”通信,难度相当大。传统的Web端即时通讯技术从短轮询到长连询,再到Comet技术,在如此原始的HTML标准之下,为了实现...  阅读全文

posted @ 2021-09-07 10:47 Jack Jiang 阅读(193) | 评论 (0)编辑 收藏

本文由融云技术团队原创分享,原题“技术实践丨万人群聊的消息分发控速方案”,为使文章更好理解,内容有修订。

1、引言

传统意义上的IM群聊,通常都是像微信这样的500人群,或者QQ的2000人群(QQ有3000人群,但那是单独收费的,也就意味着它并非无门槛标配,能用上的人并不多)。

自从国外某号称“世界上最安全的IM”搞出万人群聊之后,万人群迅速被国内的使用者们接受。伴随着移动互联网的发展,即时通讯服务被广泛应用于各个行业(以经不再局限于传统IM社交应用领域),随着业务快速发展,传统百人、千人上限的群聊已经无法满足很多业务场景需求,所以万人甚至十万人的超大群也算是相伴而生、顺应潮流。 

▲ “纸飞机”的万人群(开发人员颤抖中...)

IM群聊一直是IM应用中比较有难度的热点技术之一,通常意义的群聊,无非就是500人群、1000人群、2000人群这样,技术实现上比单聊要复杂不少。然而对于万人群聊(甚至十万人群聊)来说,相比百人、千人群聊,技术实现上那几乎是另一个技术维度的事情,难度要高很多。

本文根据融云技术团队的实践经验,总结了万人群聊消息投递方案的一些思考和实践,希望能给你带来启发。

学习交流:

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

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

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

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

2、相关文章

万人群聊有关的技术文章还可读一读以下这篇:

  1. 网易云信技术分享:IM中的万人群聊技术方案实践总结
  2. 企业微信的IM架构设计揭秘:消息模型、万人群、已读回执、消息撤回等
  3. 阿里钉钉技术分享:企业级IM王者——钉钉在后端架构上的过人之处

融云技术团队分享的其它文章:

  1. 融云技术分享:融云安卓端IM产品的网络链路保活技术实践
  2. 融云技术分享:全面揭秘亿级IM消息的可靠投递机制
  3. 融云技术分享:基于WebRTC的实时音视频首帧显示时间优化实践
  4. IM消息ID技术专题(三):解密融云IM产品的聊天消息ID生成策略

3、超大群面临的技术挑战

与百人群、千人群相比,万人、甚至十万人超大群,大幅提升了群的触达人数,对于很多业务场景来说,好处不言而喻。

然而单群成员如此之大,给 IM 系统的流量冲击非常巨大,技术难度可想而之。我们先来分析一下超大群的技术挑战。

以一个万人群的模型为例:

  • 1)如果群中有人发了消息,那么这条消息需要按照 1:9999 的比例进行分发投递,如果我们按照常规消息的处理流程,那么消息处理服务压力巨大;
  • 2)消息量大的情况下,服务端向客户端直推消息的处理速度将会成为系统瓶颈,而一旦用户的消息下发队列造成了挤压,会影响到正常的消息分发,也会导致服务缓存使用量激增;
  • 3)在微服务架构中,服务以及存储(DB,缓存)之间的 QPS 和网络流量也会急剧增高;
  • 4)以群为单位的消息缓存,内存和存储开销较大(消息体的存储被放大了万倍)。

基于这些技术挑战,要想真正达成超大群的技术目标,势必要做特定的技术优化来应对。

4、一般群聊的消息投递模型

先来看看普通群聊的消息投递模型。

我们的普通群聊消息投递模型如下图所示:

如上图所示,当用户在普通群里发了一条消息后,投递路径是:

  • 1)消息先到群组服务;
  • 2)然后通过群组服务缓存的群关系,锁定这条消息最终需要分发的目标用户;
  • 3)再根据一定的策略分发到消息服务上;
  • 4)消息服务再根据用户的在线状态和消息状态来判断这条消息是直推、通知拉取还是转 Push,最终投递给目标用户。

普通群聊的消息投递,正像您期待的那样,基本上大家的实现手段都大差不差。然而对于万人群来说,这显然还不够。

下面来看看我们针对万人群聊消息投递的技术优化手段。

5、万人群聊消息投递优化手段1:控速

针对万人群的消息投递,我们的一个主要手段就是控速。

如上图所示。

首先:我们会根据服务器的核数来建立多个群消息分发队列,这些队列我们设置了不同的休眠时间以及不同的消费线程数。

通俗来讲,可以将队列这样划分为快、中、慢等队列。

其次:我们根据群成员数量的大小来将所有群映射到相应的队列中。

规则是:

  • 1)小群映射到快队列中;
  • 2)大群映射到相应的慢队列中。

然后:小群由于人数少,对服务的影响很小,所以服务利用快队列快速的将群消息分发出去,而大群群消息则利用慢队列的相对高延时来起到控速的作用。

6、万人群聊消息投递优化手段2:合并

在本文第3节中提到的万人群聊所面临的技术挑战,最主要的挑战其实就是消息进行扩散分发投递后,消息被克隆出N条,消息流量瞬间被放大。

举个例子:当一条群消息发送到 IM 服务器后,需要从群组服务投递给消息服务,如果每一个群成员都投递一次,并且投递的群消息内容是一致的话,那肯定会造成相应的资源浪费和服务压力。

那么针对这种情况,我们的解决方案就是进行消息合并投递。

原理就是:服务落点计算中我们使用的是一致性哈希,群成员落点相对固定,所以落点一致的群成员我们可以合并成一次请求进行投递,这样就大幅提高了投递效率同时减少了服务的压力。

下图是云信团队分享的万人群消息合并投递逻辑:

▲ 上图引用自《IM中的万人群聊技术方案实践总结

如上图所示,云信团队的万人群消息合并投递方案是:按Link分组路由消息,同一Link上的全部群成员只需要路由一条消息即可。

7、十万、百万级的超大群处理方案

在实际群聊业务中,还有一种业务场景是超大规模群,这种群的群人数达到了数十万甚至上百万。

这种群如果按照上述的投投递方案,势必仍会造成消息节点的巨大压力。

比如我们有一个十万人的群,消息节点五台,消息服务处理消息的上限是一秒钟 4000 条,那每台消息节点大约会分到 2 万条群消息,这已大大超出了消息节点的处理能力。

所以为了避免上述问题,我们会将群成员上线超过3000的群识别为万人群、超级群,这种级别的群可以根据服务器数量和服务器配置相应做调整针对这种超级群会用特殊的队列来处理群消息的投递。

这个特殊的队列1秒钟往后端消息服务投递的消息数是消息服务处理上限的一半(留相应的能力处理其他消息),如果单台消息服务处理的 QPS 上限是 4000,那群组服务一秒往单台消息服务最多投递 2000 条。

8、写在最后

未来,我们也会针对群消息进行引用投递,对于大群里发的消息体比较大的消息,我们给群成员只分发和缓存消息的索引,比如 MessageID。等群成员真正拉取群消息时再从将消息组装好给客户端分发下去。这样做会节省分发的流量以及存储的空间。

随着互联网的发展,群组业务的模型和压力也在不停地扩展,后续可能还会遇到更多的挑战,当然也会不断迭代出更优的处理方式来应对。

附录:更多IM群聊技术文章

快速裂变:见证微信强大后台架构从0到1的演进历程(一)

如何保证IM实时消息的“时序性”与“一致性”?

IM单聊和群聊中的在线状态同步应该用“推”还是“拉”?

IM群聊消息如此复杂,如何保证不丢不重?

微信后台团队:微信后台异步消息队列的优化升级实践分享

移动端IM中大规模群消息的推送如何保证效率、实时性?

现代IM系统中聊天消息的同步和存储方案探讨

关于IM即时通讯群聊消息的乱序问题讨论

IM群聊消息的已读回执功能该怎么实现?

IM群聊消息究竟是存1份(即扩散读)还是存多份(即扩散写)?

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

[技术脑洞] 如果把14亿中国人拉到一个微信群里技术上能实现吗?

IM群聊机制,除了循环去发消息还有什么方式?如何优化?

网易云信技术分享:IM中的万人群聊技术方案实践总结

阿里钉钉技术分享:企业级IM王者——钉钉在后端架构上的过人之处

IM群聊消息的已读未读功能在存储空间方面的实现思路探讨

直播系统聊天技术(一):百万在线的美拍直播弹幕系统的实时推送技术实践之路

直播系统聊天技术(二):阿里电商IM消息平台,在群聊、直播场景下的技术实践

直播系统聊天技术(三):微信直播聊天室单房间1500万在线的消息架构演进之路

直播系统聊天技术(四):百度直播的海量用户实时消息系统架构演进实践

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

融云IM技术分享:万人群聊消息投递方案的思考和实践

>> 更多同类文章 ……

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

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

posted @ 2021-08-30 11:34 Jack Jiang 阅读(211) | 评论 (0)编辑 收藏

     摘要: 本文由微医云技术团队前端工程师张宇航分享,原题“从0到1打造一个 WebRTC 应用”,有修订和改动。1、引言去年初,突如其来的新冠肺炎疫情让线下就医渠道几乎被切断,在此背景下,在线问诊模式快速解决了大量急需就医病患的燃眉之急。而作为在线问诊中重要的一环——医患之间的视频问诊正是应用了实时音视频技术才得以实现。众所周之,实时音视频聊天技术门槛很高,一...  阅读全文

posted @ 2021-08-24 12:24 Jack Jiang 阅读(319) | 评论 (0)编辑 收藏

本文由徐宁发表于腾讯大讲堂,原题“程序员如何把你关注的内容推送到你眼前?揭秘信息流推荐背后的系统设计”,有改动和修订。

1、引言

信息推流(以下简称“Feed流”)这种功能在我们手机APP中几乎无处不在(尤其是社交/社群产品中),最常用的就是微信朋友圈、新浪微博等。

对Feed流的定义,可以简单理解为只要大拇指不停地往下划手机屏幕,就有一条条的信息不断涌现出来。就像给牲畜喂饲料一样,只要它吃光了就要不断再往里加,故此得名Feed(饲养)。

大多数带有Feed流功能的产品都包含两种Feed流:

  • 1)一种是基于算法:即动态算法推荐,比如今日头条、抖音短视频;
  • 2)一种是基于关注:即社交/好友关系,比如微信、知乎。

例如下图中的微博和知乎,顶栏的页卡都包含“关注”和“推荐”这两种:

如上图中这两种Feed流,它们背后用到的技术差别会比较大。不同于“推荐”页卡那种千人千面算法推荐的方式,通常“关注”页卡所展示的内容先后顺序都有固定的规则,最常见的规则是基于时间线来排序,也就是展示“我关注的人所发的帖子、动态、心情,根据发布时间从晚到早依次排列”。

本文将重点讨论的是“关注”功能对应的技术实现:先总结常用的基于时间线Feed流的后台技术实现方案,再结合具体的业务场景,根据实际需求在基本设计思路上做一些灵活的运用。

学习交流:

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

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

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

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

2、本文作者

徐宁:腾讯应用开发工程师,腾讯学院讲师,毕业于上海交通大学。目前负责腾讯智慧零售业务的后端开发工作,有丰富的视频直播,自动化营销系统开发经验。

3、Feed流技术实现方案1:读扩散

读扩散也称为“拉模式”,这应该是最符合我们认知直觉的一种技术实现方式。

原理如下图:

如上图所示:每一个内容发布者都有一个自己的发件箱(“我发布的内容”),每当我们发出一个新帖子,都存入自己的发件箱中。当我们的粉丝来阅读时,系统首先需要拿到粉丝关注的所有人,然后遍历所有发布者的发件箱,取出他们所发布的帖子,然后依据发布时间排序,展示给阅读者。

这种设计:阅读者读一次Feed流,后台会扩散为N次读操作(N等于关注的人数)以及一次聚合操作,因此称为读扩散。每次读Feed流相当于去关注者的收件箱主动拉取帖子,因此也得名——拉模式。

这种模式:

  • 1)好处是:底层存储简单,没有空间浪费;
  • 2)坏处是:每次读操作会非常重,操作非常多。

设想一下:如果我关注的人数非常多,遍历一遍我所关注的所有人,并且再聚合一下,这个系统开销会非常大,时延上可能达到无法忍受的地步。

因此:读扩散主要适用系统中阅读者关注的人没那么多,并且刷Feed流并不频繁的场景。

拉模式还有一个比较大的缺点:就是分页不方便,我们刷微博或朋友圈,肯定是随着大拇指在屏幕不断划动,内容一页一页的从后台拉取。如果不做其他优化,只采用实时聚合的方式,下滑到比较靠后的页码时会非常麻烦。

4、Feed流技术实现方案2:写扩散

据统计:大多数Feed流产品的读写比大概在100:1,也就是说大部分情况都是刷Feed流看别人发的朋友圈和微博,只有很少情况是自己亲自发一条朋友圈或微博给别人看。

因此:读扩散那种很重的读逻辑并不适合大多数场景。

我们宁愿让发帖的过程复杂一些,也不愿影响用户读Feed流的体验,因此稍微改造一下前面方案就有了写扩散。写扩散也称为“推模式”,这种模式会对拉模式的一些缺点做改进。

原理如下图:

如上图所示:系统中每个用户除了有发件箱,也会有自己的收件箱。当发布者发表一篇帖子的时候,除了往自己发件箱记录一下之外,还会遍历发布者的所有粉丝,往这些粉丝的收件箱也投放一份相同内容。这样阅读者来读Feed流时,直接从自己的收件箱读取即可。

这种设计:每次发表帖子,都会扩散为M次写操作(M等于自己的粉丝数),因此成为写扩散。每篇帖子都会主动推送到所有粉丝的收件箱,因此也得名推模式。

这种模式可想而知:发一篇帖子,背后会涉及到很多次的写操作。通常为了发帖人的用户体验,当发布的帖子写到自己发件箱时,就可以返回发布成功。后台另外起一个异步任务,不慌不忙地往粉丝收件箱投递帖子即可。

写扩散的好处在于通过数据冗余(一篇帖子会被存储M份副本),提升了阅读者的用户体验。通常适当的数据冗余不是什么问题,但是到了微博明星这里,完全行不通。比如目前微博粉丝量Top2的谢娜与何炅,两个人微博粉丝过亿。

设想一下:如果单纯采用推模式,那每次谢娜何炅发一条微博,微博后台都要地震一次。一篇微博导致后台上亿次写操作,这显然是不可行的。

另外:由于写扩散是异步操作,写的太慢会导致帖子发出去半天,有些粉丝依然没能看见,这种体验也不太好。

通常写扩散适用于好友量不大的情况,比如微信朋友圈正是写扩散模式。每一名微信用户的好友上限为5000人,也就是说你发一条朋友圈最多也就扩散到5000次写操作,如果异步任务性能好一些,完全没有问题。

关于微信朋友圈的技术资料:

5、Feed流技术实现方案2:读写混合模式

读写混合也可以称作“推拉结合”,这种方式可以兼具读扩散和写扩散的优点。

我们首先来总结一下读扩散和写扩散的优缺点:

见上图:仔细比较一下读扩散与写扩散的优缺点,不难发现两者的适用场景是互补的。

因此:在设计后台存储的时候,我们如果能够区分一下场景,在不同场景下选择最适合的方案,并且动态调整策略,就实现了读写混合模式。

原理如下图:

以微博为例:当何炅这种粉丝量超大的人发帖时,将帖子写入何炅的发件箱,另外提取出来何炅粉丝当中比较活跃的那一批(这已经可以筛掉大部分了),将何炅的帖子写入他们的收件箱。当一个粉丝量很小的路人甲发帖时,采用写扩散方式,遍历他的所有粉丝并将帖子写入粉丝收件箱。

对于那些活跃用户登录刷Feed流时:他直接从自己的收件箱读取帖子即可,保证了活跃用户的体验。

当一个非活跃的用户突然登录刷Feed流时:

  • 1)一方面需要读他的收件箱;
  • 2)另一面需要遍历他所关注的大V用户的发件箱提取帖子,并且做一下聚合展示。

在展示完后:系统还需要有个任务来判断是否有必要将该用户升级为活跃用户。

因为有读扩散的场景存在,因此即使是混合模式,每个阅读者所能关注的人数也要设置上限,例如新浪微博限制每个账号最多可以关注2000人。

如果不设上限:设想一下有一位用户把微博所有账号全部关注了,那他打开关注列表会读取到微博全站所有帖子,一旦出现读扩散,系统必然崩溃(即使是写扩散,他的收件箱也无法容纳这么多的微博)。

读写混合模式下,系统需要做两个判断:

  • 1)哪些用户属于大V,我们可以将粉丝量作为一个判断指标;
  • 2)哪些用户属于活跃粉丝,这个判断标准可以是最近一次登录时间等。

这两处判断标准就需要在系统发展过程中动态地识别和调整,没有固定公式了。

可以看出:读写结合模式综合了两种模式的优点,属于最佳方案。

然而他的缺点是:系统机制非常复杂,给程序员带来无数烦恼。通常在项目初期,只有一两个开发人员,用户规模也很小的时候,一步到位地采用这种混合模式还是要慎重,容易出bug。当项目规模逐渐发展到新浪微博的水平,有一个大团队专门来做Feed流时,读写混合模式才是必须的。

6、Feed流中的分页问题

上面几节已经叙述了基于时间线的几种Feed流常见设计方案,但实操起来会比理论要麻烦许多。

接下来专门讨论一个Feed流技术方案中的痛点——Feed流的分页。

不管是读扩散还是写扩散,Feed流本质上是一个动态列表,列表内容会随着时间不断变化。传统的前端分页参数使用page_size和page_num,分表表示每页几条,以及当前是第几页。

对于一个动态列表会有如下问题:

如上图所示:在T1时刻读取了第一页,T2时刻有人新发表了“内容11”,在T3时刻如果来拉取第二页,会导致错位出现,“内容6”在第一页和第二页都被返回了。事实上,但凡两页之间出现内容的添加或删除,都会导致错位问题。

为了解决这一问题:通常Feed流的分页入参不会使用page_size和page_num,而是使用last_id来记录上一页最后一条内容的id。前端读取下一页的时候,必须将last_id作为入参,后台直接找到last_id对应数据,再往后偏移page_size条数据,返回给前端,这样就避免了错位问题。

如下图:

采用last_id的方案有一个重要条件:就是last_id本身这条数据不可以被硬删除。

设想一下:

  • 1)上图中T1时刻返回5条数据,last_id为内容6;
  • 2)T2时刻内容6被发布者删除;
  • 3)那么T3时刻再来请求第二页,我们根本找不到last_id对应的数据了,也就无法确认分页偏移量。

通常碰到删除的场景:我们采用软删除方式,只是在内容上置一个标志位,表示内容已删除。

由于已经删除的内容不应该再返回给前端,因此软删除模式下,找到last_id并往后偏移page_size条,如果其中有被删除的数据会导致获得足够的数据条数给前端。

这里一个解决方案是找不够继续再往下找,另一种方案是与前端协商,允许返回条数少于page_size条,page_size只是个建议值。甚至大家约定好了以后,可以不要page_size参数。

7、Feed流技术方案在某直播应用中的实践

7.1 需求背景

本节将结合实际业务,分享一下实际场景中碰到的一个非常特殊的Feed流设计方案。

xx 直播是一款直播带货工具,主播可以创建一场未来时刻的直播,到时间后开播卖货,直播结束后,主播的粉丝可以查看直播回放。

这样,每个直播场次就有三种状态——预告中(创建一场直播但还未开播)、直播中、回放。

作为观众,我可以关注多位主播,这样从粉丝视角来看,也会有个直播场次的Feed流页面。

这个Feed流最特殊的地方在于它的排序规则:

解释一下这个Feed流排序规则:

  • 1)我关注的所有主播:正在直播中的场次排在最前;预告中的场次排中间;回放场次排最后;
  • 2)多场次都在直播中的:按开播时间从晚到早排序;
  • 3)多场次都在预告中的:按预计开播时间从早到晚排序;
  • 4)多场次都在回放的:按直播结束时间从晚到早排序。

7.2 问题分析

本需求最复杂的点在于Feed流内容融入的“状态”因素,状态的转变会直接导致Feed流顺序不同。

为了更清晰解释一下对排序的影响,我们可以用下图详细说明:

上图中:展示了4个主播的5个直播场次,作为观众,当我在T1时刻打开页面,看到的顺序是场次3在最上方,其余场次均在预告状态,按照预计开播时间从早到晚展示。当我在T2时刻打开页面,场次5在最上方,其余有三场在预告状态排在中间,场次3已经结束了所以排在最后。以此类推,直到所有直播都结束,所有场次最终的状态都会变为回放。

这里需要注意一点:如果我在T1时刻打开第一页,然后盯着页面不动,一直盯到T4时刻再下划到第二页,这时上一页的last_id,即分页偏移量很有可能因为直播状态变化而不知道飞到了什么位置,这会导致严重的错位问题,以及直播状态展示不统一的问题(第一页展示的是T1时刻的直播状态,第二页展示的是T4时刻的直播状态)。

7.3 解决方案

直播系统是个单向关系链,和微博有些类似,每个观众会关注少量主播,每个主播会可能有非常多的关注者。

由于有状态变化的存在,写扩散几乎无法实现。

因为:如果采用写扩散的方式,每次主播创建直播、直播开播、直播结束这三个事件发生时导致的场次状态变化,会扩散为非常多次的写操作,不仅操作复杂,时延上也无法接受。

微博之所以可以写扩散:就是因为一条微博发出后,这篇微博就不会再有任何影响排序的状态转变。

而在我们场景中:“预告中”与“直播中”是两个中间态,而“回放”状态才是所有直播的最终归宿,一旦进入回放,这场直播也就不会再有状态转变。因此“直播中”与“预告中”状态可以采用读扩散方式,“回放”状态采取写扩散方式。

最终的方案如下图所示:

如上图:会影响直播状态的三种事件(创建直播、开播、结束直播)全部采用监听队列异步处理。

我们为每一位主播维护一个直播中+预告中状态的优先级队列:

  • 1)每当监听到有主播创建直播时,将直播场次加入队列中,得分为开播的时间戳的相反数(负数);
  • 2)每当监听到有主播开播时,把这场直播在队列中的得分修改为开播时间(正数);
  • 3)每当监听到有主播结束直播,则异步地将播放信息投递到每个观众的回放队列中。

这里有一个小技巧:前文提到,直播中状态按照开播时间从大到小排序,而预告中状态则按照开播时间从小到大排序,因此如果将预告中状态的得分全部取开播时间相反数,那排序同样就成为了从大到小。这样的转化可以保证直播中与预告中同处于一个队列排序。预告中得分全都为负数,直播中得分全都为正数,最后聚合时可以保证所有直播中全都自然排在预告中前面。

另外:前文还提到的另一个问题是T1时刻拉取第一页,T4时刻拉取第二页,导致第一页和第二页直播间状态不统一。

解决这个问题的办法是通过快照方式:当观众来拉取第一页Feed流时,我们依据当前时间,将全部直播中和预告中状态的场次建立一份快照,使用一个session_id标识,每次前端分页拉取时,我们直接从快照中读取即可。如果快照中读取完毕,证明该观众的直播中和预告中场次全部读完,剩下的则使用回放队列进行补充。

照此一来,我们的Feed流系统,前端分页拉取的参数一共有4个:

每当碰到session_id和last_id为空,则证明用户想要读取第一页,需要重新构建快照。

这里还有一个衍生问题:session_id的如何取值?

答案是:

  • 1)如果不考虑同一个观众在多端登录的情况,其实每一位观众维护一个快照id即可,也就是直接将系统用户id设为session_id;
  • 2)如果考虑多端登录的情况,则session_id中必须包含每个端的信息,以避免多端快照相互影响;
  • 3)如果不心疼内存,也可以每次随机一个字符串作为session_id,并设置一个足够长的过期时间,让快照自然过期。

以上设计:其实系统计算量最大的时刻就是拉取第一页,构建快照的开销。

目前的线上数据,对于只关注不到10个主播的观众(这也是大多数场景),拉取第一页的QPS可以达到1.5万。如果将第二页以后的请求也算进来,Feed流的综合QPS可以达到更高水平,支撑目前的用户规模已经绰绰有余。如果我们拉取第一页时只获取到前10条即可直接返回,将构建快照操作改为异步,也许QPS可以更高一些,这可能是后续的优化点。

8、本文小结

几乎所有基于时间线和关注关系的Feed流都逃不开三种基本设计模式:

  • 1)读扩散;
  • 2)写扩散;
  • 3)读写混合。

具体到实际业务中,可能会有更复杂的场景,比如本文所说的:

  • 1)状态流转影响排序;
  • 2)微博朋友圈场景中会有广告接入、特别关注、热点话题等可能影响到Feed流排序的因素。

这些场景就只能根据业务需求,做相对应的变通了。

附录:更多社交应用架构设计文章

  1. 浅谈IM系统的架构设计
  2. 简述移动端IM开发的那些坑:架构设计、通信协议和客户端
  3. 一套海量在线用户的移动端IM架构设计实践分享(含详细图文)
  4. 一套原创分布式即时通讯(IM)系统理论架构方案
  5. 从零到卓越:京东客服即时通讯系统的技术架构演进历程
  6. 蘑菇街即时通讯/IM服务器开发之架构选择
  7. 腾讯QQ1.4亿在线用户的技术挑战和架构演进之路PPT
  8. 微信后台基于时间序的海量数据冷热分级架构设计实践
  9. 微信技术总监谈架构:微信之道——大道至简(演讲全文)
  10. 如何解读《微信技术总监谈架构:微信之道——大道至简》
  11. 快速裂变:见证微信强大后台架构从0到1的演进历程(一)
  12. 移动端IM中大规模群消息的推送如何保证效率、实时性?
  13. 现代IM系统中聊天消息的同步和存储方案探讨
  14. 微信朋友圈千亿访问量背后的技术挑战和实践总结
  15. 以微博类应用场景为例,总结海量社交系统的架构设计步骤
  16. 子弹短信光鲜的背后:网易云信首席架构师分享亿级IM平台的技术实践
  17. 知乎技术分享:从单机到2000万QPS并发的Redis高性能缓存实践之路
  18. 微信技术分享:微信的海量IM聊天消息序列号生成实践(算法原理篇)
  19. 微信技术分享:微信的海量IM聊天消息序列号生成实践(容灾方案篇)
  20. 一套高可用、易伸缩、高并发的IM群聊、单聊架构方案设计实践
  21. 社交软件红包技术解密(一):全面解密QQ红包技术方案——架构、技术实现等
  22. 社交软件红包技术解密(二):解密微信摇一摇红包从0到1的技术演进
  23. 社交软件红包技术解密(三):微信摇一摇红包雨背后的技术细节
  24. 社交软件红包技术解密(四):微信红包系统是如何应对高并发的
  25. 社交软件红包技术解密(五):微信红包系统是如何实现高可用性的
  26. 社交软件红包技术解密(六):微信红包系统的存储层架构演进实践
  27. 社交软件红包技术解密(七):支付宝红包的海量高并发技术实践
  28. 社交软件红包技术解密(八):全面解密微博红包技术方案
  29. 社交软件红包技术解密(九):谈谈手Q红包的功能逻辑、容灾、运维、架构等
  30. 从游击队到正规军(一):马蜂窝旅游网的IM系统架构演进之路
  31. 从游击队到正规军(二):马蜂窝旅游网的IM客户端架构演进和实践总结
  32. 从游击队到正规军(三):基于Go的马蜂窝旅游网分布式IM系统技术实践
  33. 瓜子IM智能客服系统的数据架构设计(整理自现场演讲,有配套PPT)
  34. 阿里钉钉技术分享:企业级IM王者——钉钉在后端架构上的过人之处
  35. 微信后台基于时间序的新一代海量数据存储架构的设计实践
  36. 阿里技术分享:电商IM消息平台,在群聊、直播场景下的技术实践
  37. 一套亿级用户的IM架构技术干货(上篇):整体架构、服务拆分等
  38. 一套亿级用户的IM架构技术干货(下篇):可靠性、有序性、弱网优化等
  39. 从新手到专家:如何设计一套亿级消息量的分布式IM系统
  40. 企业微信的IM架构设计揭秘:消息模型、万人群、已读回执、消息撤回等
  41. 融云技术分享:全面揭秘亿级IM消息的可靠投递机制
  42. IM开发技术学习:揭秘微信朋友圈这种信息推流背后的系统设计
  43. >> 更多同类文章 ……

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

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

posted @ 2021-08-17 15:34 Jack Jiang 阅读(193) | 评论 (0)编辑 收藏

仅列出标题
共51页: First 上一页 22 23 24 25 26 27 28 29 30 下一页 Last 
Jack Jiang的 Mail: jb2011@163.com, 联系QQ: 413980957, 微信: hellojackjiang