IM 内容分享(十三): 系统消息逻辑实现

本文分析了IM系统中的私信和广播系统消息处理,指出广播系统消息在高并发时需特殊设计。提出通过只写云系统消息库,在用户登录时对齐联系人库的策略,以及一种通用的分布式事务解决思路,确保数据最终一致性。
摘要由CSDN通过智能技术生成

目录

一、私信系统消息

二、广播系统消息

总结文中关键


IM 消息中除了点对点的私信消息和群消息外,还有由 “系统” 发给用户的 “系统消息”。

系统消息通常包括两类:一类是由系统单独发给一个用户的私信系统消息,比如用户下单或支付后,系统会发消息通知到用户;一类是由系统群发给很多用户的广播系统消息,比如通知本周所有在平台下单的用户来领取奖品之类的。

广播系统消息,因为需要短时群发大量消息,会对系统瞬时产生很高的负载,这是 IM 系统设计的关键;根据广播消息的量级不同,广播系统消息的落地方案也会有所不同。

今天我们基于 IM 系统的分层架构,分析私信系统消息和广播系统消息的处理逻辑。

一、私信系统消息

私信系统消息处理流程与点对点的私信处理流程类似,但更简单许多,见下图。

IM 系统消息对实时性和可靠性的要求不高,而且系统消息经常需要进行业务迭代,所以这一部分业务需要放在 Extlogic 服务中进行处理。

  • 当业务平台需要向用户推送私信系统消息时,就向 MQ 发送一条消息,比如,当用户被关注了、用户支付了等事件被触发;“平台业务” 是整个的业务系统,比如,电商系统、外卖系统等;IM 可以被看做是整个业务系统能链接到终端用户的通道工具,MQ 对平台业务系统和 IM 系统进行了解耦;

  • Extlogic 作为下游服务,对 MQ 进行消费;Extlogic 拿到一条消息后,首先进行落库处理,即通过调用数据访问层 Das 写 “云系统消息库” 和 “联系人库” ;这里需要注意,在点对点的私信消息处理中,写消息库和联系人库是分别需要写两条,这里只写一条即可,毕竟 “系统” 不是 用户,不需要站在 “系统” 的维度进行存储;

  • 系统消息落库成功以后,Extlogic 访问路由层 Router ,获取用户在线数据,如果用户离线,则结束处理流程;如果用户在线,Extlogic 则将系统消息推送给相应的 Entry 节点,由 Entry 将系统消息推送到用户客户端;

  • 对于非常重要的系统消息类型,需要客户端回复 ACK 包进行确认;而对于一般的系统消息,服务端只向客户端推送即可,允许推送阶段的消息丢失,毕竟消息已经落库,将来客户端一定会拉取到。

这就是普通的私信系统消息的逻辑处理流程,并不复杂,下面我们重点分析广播系统消息。

二、广播系统消息

上面说过,广播系统消息是一对多的方式,即一条消息内容广播给很多用户,具有瞬时高并发的特点。按上面 “私信系统消息” 的逻辑处理流程,来应对广播系统消息是否可以呢?

在电商 IM 系统的实践中,对于广播 “几万量级” 的系统消息,按私信系统消息的处理流程,是完全OK的,当量级增加时,就会出现系统异常和用户体验的问题。

我们分析一下上面 “私信系统消息” 的处理流程:

广播系统消息时,“平台业务” 会将大量的用户id封装成消息写入到 MQ 中,然后由 Extlogic 进行消费; Extlogic 和 Das 都是无状态服务,当需要消费大量 MQ 消息时,对 Extlogic 和 Das 进行线性水平扩容即可;核心问题在于对 “云系统消息库” 和 “联系人库” 的写操作,当对数据库瞬间产生大量写入的时候,数据库会因高负载导致写入高延时,可能失败也可能超时;“云系统消息库” 和 “联系人库” 是两个数据库,无法通过数据库本身保证其业务逻辑的原子性。

  • 当写 “云系统消息库” 和 “联系人库” 都成功或都失败时,业务逻辑没有问题;

  • 当写 “云系统消息库” 成功,写 “联系人库” 失败时,用户登录后没有 “未读数” 引导用户拉取系统消息,但用户的系统消息聊天窗口中是产生了新的消息;有新消息产生,但却没有通知到用户,这样的体验是很糟糕的;

  • 当写 “云系统消息库” 失败,写 “联系人库” 成功时,用户登录后会看到一个 “未读数”,但是真正进入系统消息聊天窗口时,却看不到新的消息;站在用户的角度,用户会感觉被耍了,这样的体验更糟糕。

在公司业务规模迅速扩张,每天都会多次广播系统消息时,产品和运营肯定会责成研发团队尽快解决该问题;本着架构设计 “降本增效” 的原则,如何合理设计广播系统消息的改进方案呢?

  • 对 MQ 消费进行限速:数据的高负载是由 Extlogic 高速消费 MQ 导致的,在消费时限制消费流量是否可行呢?技术上完全没有问题,但产品是有问题的;试想:中午 12:30 广播系统消息,是为了用户在午饭后打开手机消遣时能恰好看到该广播消息;对 MQ 消费限速后,虽然用户在 13:30 成功收到了消息,但消息对用户的触达率是很受影响的。

  • 扩容数据库:数据库的高负载通过对其进行扩容,的确会有效解决问题;但稳定扩容一个现有记录数在千万甚至亿级的数据库,绝对是一个浩大的工程,既要完成扩容,又必须保证线上业务稳定运行,没有至少两周时间,是很难完成的;研发同学可以等,但产品和运营不会等。

  • 引入缓存:在同等量级的情况下,缓存可以抗住数据的高并发写入,可以解决数据库高负载的问题;但需要明白,缓存的本质是提供高并发的读,而非写,同时缓存中数据的可靠性远远低于数据库,如何保证缓存和数据库的一致性,会涉及调整上层大量的业务逻辑。

  • 增大数据库超时:在实践中,系统的异常主要是写数据库的超时,那么增大超时时间是否可行呢?比如由原来的 2 秒超时,调整到10 秒;这是一种典型的治标不治本的解决思路,实践中会发现,即使超时时间设定为 20 秒,问题仍然存在。

  • 分布式事务:该问题的本质其实就是分布式事务的问题,那么引入分布式事务的解决方案是否可行呢?比如 2PC / 3PC、TCC、Saga 等。分布式事务解决方案,在业务开发应用中,经常会被采取能避免则尽量避免的原子,原因是其性能低和复杂度高,在没有其他更好的解决方案的情况下,可以作为兜底策略。

广播系统消息的落地方案,在实践中到底是如何设计的呢?

分析一下用户群体,大部分用户在广播系统消息时是处于离线状态的,只有非常小的一部分用户在线;所以,在广播操作时,只需要写 “云系统消息库” 一个库即可,当用户登录时,触发对 “联系人库” 的写入,完成两个数据库之间的原子性逻辑。见下图。

  • 广播系统消息时,“平台业务” 封装要广播的用户id写入 MQ,下游 Extlogic 进行消费;

  • 对于非常少量的在线用户,仍然走 “私信系统消息” 的处理流程;

  • 对于离线用户,通过调用 Das 完成对 “云系统消息库” 的写入;

  • 当用户登录时,Logic 封装 “登录” 事件写入 MQ,Extlogic 消费时,完成 “云系统消息库” 和 “联系人库” 之间的数据对齐操作。

对齐 “云系统消息库” 和 “联系人库”,是将用户未读的系统消息写入到联系人表中,同时将最后一条云系统消息内容写入到联系人表中;这一步并不复杂,首先根据联系人表中的最新消息查询 “云系统消息库” 最近记录,然后与 “联系人库” 数据做对比,若存在差异,修复即可。

该解决方案采用的是 “压力均摊” 的核心思路,因为用户登录操作是分散的,不会对 IM 系统的联系人核心库造成冲击。

同时,我们可以从该解决方案中,抽象出一种通用的分布式事务解决思路,即:写数据时,只写一个库,在读数据发生前,触发两个库的对齐操作,完成数据的最终一致性。

总结文中关键

  1. IM 系统消息从业务上包括:私信系统消息和广播系统消息;

  2. 私信系统消息处理逻辑与点对点私信类似,由 Extlogic 完成业务处理,分别写 “云系统消息库” 和 “联系人库”;

  3. 广播系统消息处理逻辑,在广播量级较低时,可采用私信系统消息方式;在广播量级较高时,通过 “压力均摊” 的思路设计方案,即:广播发生时,只写 “云系统消息库” ,当用户登录时,触发对 “联系人库” 的写操作,对齐两个数据库之间登录用户的数据差异。

  4. 抽象通用的分布式事务解决思路:写数据时,只写一个库;在读数据发生前,触发两个库的对齐操作,完成数据的最终一致性。

  • 16
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

之乎者也·

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值