业务场景实战(四)如何高性能实现延时消息

思维导图

思维导图.png

系列总目录


前言

  • 延时消息指的在分布式异步消息场景下,生产端发送一条消息,希望在指定延时或者指定时间点被消费端消费到,而不是立刻被消费

使用场景

  • 订单30分钟内未支付取消
  • 任务截止日志前几天未完成,提醒

时间轮原理

  • 高效延时消息,包含两个重要的数据结构:
  1. 环形队列,例如可以创建一个包含3600个slot的环形队列(可以用数组实现)
  2. 任务集合,环上每一个slot是一个Set<Task>, Task结构中有两个很重要的属性:
    2.1 Cycle-Num:当Current Index第几圈扫描到这个Slot时,执行任务
    2.2 Task-Function:需要执行的任务函数


    时间轮原理.png
  • 同时,启动一个timer:
  1. 此timer每隔一定时间(比如1秒),在环形队列中移动一格
  2. 用一个Current Index来标识正在检测的slot
  • 如上图,假设当前Current Index指向第一格,当有延时消息到达之后,例如希望3610秒之后,触发一个延时消息任务,只需:
  1. 计算这个Task应该放在哪一个slot,现在指向1,3610秒之后,应该是第11格,所以这个Task应该放在第11个slot的Set<Task>中;
  2. 计算这个Task的Cycle-Num,由于环形队列是3600格(每秒移动一格,正好1小时),这个任务是3610秒后执行,所以应该绕3610/3600=1圈之后再执行,于是Cycle-Num=1;
  • Current Index不停的移动,每秒移动一格,当移动到一个新slot,遍历这个slot中对应的Set<Task>,每个Task看Cycle-Num是不是0:
  1. 如果不是0,说明还需要多移动几圈,将Cycle-Num减1;
  2. 如果是0,说明马上要执行这个Task了,取出Task-Funciton执行,丢给工作线程执行,并把这个Task从Set<Task>中删除;

实现

定时扫描数据库

  • 定时扫描mysql数据库,扫描快到期的任务,然后进行处理。
  • 优劣势: 实现简单,但不适合数据量大场景

RocketMq

  • 天然支持延迟消息,只支持 18 个 Level 的延时,并不支持任意时间。对于普通业务也是够用
  • 原理:延时 Level 的消息会被暂存在名为SCHEDULE_TOPIC_XXXX的topic中,并根据 level 存入特定的queue,queueId = delayTimeLevel – 1(即一个queue只存相同延时的消息,保证具有相同发送延时的消息能够顺序消费), broker会调度地费SCHEDULE_TOPIC_XXXX,将消息写入真实的topic。原理图:


    原理图

Qmq

  • Qmq是携程开源的消息队列,开源版本跟内部版本有一定的差。Qmq提供任意时间的延时/定时消息,你可以指定消息在未来两年内(可配置)任意时间内投递。目前为止最适合的延迟任务设计方案
  • 设计原理: 多级时间轮 + 延时加载 + 延时消息单独磁盘存储
  • 两层hash轮
  1. 第一层位于磁盘上,每个小时为一个刻度(可以配置),每个刻度会生成一个日志文件,因为QMQ支持两年内的延时消息(默认支持两年内,可以进行配置修改),则最多会生成 2 * 366 * 24 = 17568 个文件。
  2. 第二层在内存中,当消息的投递时间即将到来的时候,会将这个刻度的消息索引(索引包括消息在schedule log中的offset和size)从磁盘文件加载到内存中的hash wheel上,内存中的hash wheel则是以500ms为一个刻度。
  • 优点
  1. 通过多级时间轮设计,支持了超大时间跨度的延时消息;
  2. 通过延时加载,内存中只会有最近要消费的消息,更久的延时消息会被存储在磁盘中,对内存友好;
  3. 延时消息单独存储,不会影响到正常消息的空间回收;

参考文章

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 一、缘起 很多时候,业务有“在一段时间之后,完成一个工作任务”的需求。 例如:滴滴打车订单完成后,如果用户一直不评...
    duzhongli阅读 2,114评论 2 3
  • 为了实现高效延时触发,我们需要实现两个重要的数据结构: (1)环形队列,例如可以创建一个包含3600个slot的环...
    西三旗靓仔阅读 737评论 0 2
  • 队列常见的使用场景:异步处理、系统解耦、数据同步、流量削峰,常见的种类有任务队列、消息队列、请求队列。现在考虑这样...
    packet阅读 4,286评论 0 2
  • ​ 场景 延时消息即消息发送后并不立即对消费者可见,而是在用户指定的时间投递给消费者。比如我们现在发送一条延时1分...
    云图智联IT教育阅读 521评论 0 0
  • 背景 Disruptor是英国外汇交易公司LMAX开发的一个高性能队列,研发的初衷是解决内存队列的延迟问题(在性能...
    孤独可贵阅读 560评论 0 1

友情链接更多精彩内容