基于rabbitmq死信队列实现运行时秒级定时任务框架

源码:        朗月/基于rabbitmq的任意时长秒级定时任务框架 (gitee.com)https://gitee.com/beiding/delay-task

简述

当我们面对一些及时性比较高的场景时,比如,秒杀活动在活动开始前十分钟预热要下架相关商品,活动结束后又要将相关商品自动上架;再比如,租房系统房租到期需要做结算。我们第一个想到的解决方案是使用一个定时任务每个10分钟扫一次库。但当数据量较大时,对数据库而言这无疑是一场灾难,而且轮询的粒度为10分钟精度太差。

直接使用rabbitmq死信队列实现有什么问题?

       使用死信队列实现延时的工作原理,如下图所示:

定时任务投递到一个指定了超时时间(x-message-ttl:毫秒数)的队列,并指定其死亡交换机和key(x-dead-letter-exchange: 交换机, x-dead-letter-routing-key: key)。当死信队列超出一定时间未被消费被判定为死亡,会投放到目标交换机上,进而投放到处理队列上。

        rabbitmq的死信队列无法在运行时任意指定时长创建延时任务,其延时任务必须是在开发时预定义好的时长。这是rabbitmq消息队列本身的特点:队列中只有队头消息是处于活跃状态,rabbitmq判断消息死亡也只能判断队头消息。这就会导致非队头的消息可能被队头消息阻塞。比如,先往队列中压入一个5分钟后到期的任务A再压入一个3分钟后到期的任务B,如果任务A没有被消费则任务B无法检查过期时间也无法执行过期操作,在业务上将表现为3分钟到期的任务实际过了5分钟才执行。

 

但如果任务A和任务B都延时相同的时长,就不会出现队头阻塞的问题。

我们不妨用几个简单的数学算式说明这个问题

设A放入队列的时间是Ta,B放入队列的时间为Tb ,队列死亡时间为dt,A执行时间为T'a,B执行时间为T'b。已知Ta<Tb。

因为:Ta<Tb

所以: Ta+dt<Tb+dt

所以: T'a<T'b

而且,我们还可以得出结论T'b-T'a=Tb-Ta(即B执行较于A执行晚的时长等于B入队相较于A入队晚的时长)。

如何使用有限的固定时长的死信队列实现任意时长定时任务?

思路:多层级时间轮

假设我们有三个队列,分别对应的死亡时长为4秒、2秒、1秒,即(2^2、2^1、2^0),就可以排列组合出0到7秒以内的任意时间,比如:5=2^2+2^0,2=2^1,7=2^0+2^1+2^2。而对于多于7秒的时间可以表示吗,我们只需要在最大的队列上反复等待几轮。比如:13=3*2^2+2^0,即等待13秒只需要在2^2的队列上等待3轮,然后再在2^0队列上等1轮就可以表示出13秒这个时间。这就是本方案的原理。这里做一个定义,称不会在最大队列上重复的时间为无重时间,反之成为有重时间。

创建足够数量的死信队列(比如20个,可表示大约24天的无重时间)。创建一个控制对象(下文称“时间中控”),由时间中控控制在不同队列上的等待次数。外部调用时间中控以目标队列、负载、延时时长为参数,时间中控将被反复触发、经过有限次等待到指定时长,最终将消息投递到目标队列上。

讨论

1. 可靠性保证

        消息会丢吗,不会!消息由时间中控在多个队列中流转、如果流转时投递下个队列失败,消息不被确认将一直重试。这由rabbitmq自身的ack机制保证。

2. 消息重复

        消息会重复吗,会!需要在代码中做幂等保证,时间中控在处理的过程中由于网络抖动导致消息重发是有可能的。由于网络通信中的“二将军”问题,重试总是不可避免的,幂等是在任何基于mq异步开发所要进行的必要操作。

3. 准时性

        消息执行时间会不准吗,可能会。当短时间内投放了大量的定时任务,由于mq本身的带宽以及IO传输效率,导致消息出现延迟是有可能的。虽然时间中控在每次被触发时都会校准时间,但也不可避免可能会出现延时。所以,建议使用该方案时不要创建过多的任务,尽量将可以合并的定时任务合并,比如,对于秒杀活动普通商品的上下架,以活动为单位创建定时任务、而不要以每个商品为单位进行上下架操作。使用该方案,也应该针对自身环境测试得出准时的最大任务密度,综合考量。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值