延时任务最佳实践方案


前言

很多时候,业务系统有延时处理任务的需求,当任务量很大时,可能需要维护大量的定时器,或者进行低效的扫描。例如:电商下单成功后60s之后给用户发送短信通知,电商下单后30分钟未支付,则自动取消订单;出行乘客叫单后30秒没有司机接单,重新给周边司机推单等。实现这类需求有一些常见方案。
在讨论方案前我们需要搞清楚,延时任务与定时任务究竟有啥区别?定时任务有明确的执行时间或周期性,比如定时充电,要选开始充电的具体时间点,再比如每10分钟做一次未支付订单的状态检查。而延时任务没有这些特性,它具有不确定性,是在某个事件触发后一段时间内执行。下面以“出行乘客叫单后30秒内没有司机接单,重新给周边司机推单,直到司机接单”为例,讲解每种方案的实现。


方案分析

一、轮询扫描

启动一个Timer,30秒间隔轮询扫描订单表,检查每个未接订单创建时间是否超过30秒的整数倍,如果超过,重新给周边司机推送订单。
优点:简单易行。
缺点:如果订单量过大,延迟会比较高。
适用范围:这种方案一般适用于延时任务量比较少,对于延时精确度要求不高的任务。

二、多Timer触发

为每个订单创建一个Timer,并且设置为30秒的触发时间间隔,事件触发后检查订单状态,如果是初始状态,则继续执行,否则停止并释放Timer。
优点:简单易行,不需要轮询,精确度较高。
缺点:但每个订单要启动一个timer,比较耗资源。
适用范围:同样适用于延时任务量比较少的系统。

三、RabbitMQ死信队列

死信:Dead Letter,是指被拒绝或TTL过期或队列已达到最大长度限制,无法再入队的消息。利用DLX,当消息在一个队列中变成死信之后,它能被重新publish到另一个Exchange,这个Exchange就是DLX。

死信队列生产消费模型:
在这里插入图片描述
利用死信队列可以实现延时任务,每个订单创建一个消息,消息的TTL被设置为30秒。当消息过期后,通过交换机转发给业务消费队列,消费处理程序订阅业务消费队列,有消息则进行处理,检查订单状态,如果是初始状态,则重新对该订单创建一个消息。
优点:不需要轮询,精确度高。
缺点:引入消息组件,系统复杂度提高。
适用范围:适用于有大量延时任务需求的系统。

四、环形队列

环形队列本质上就是一个数组,收尾相接,形成了一个环。数组中的每个索引位称为槽(Slot),每个槽中放一个集合,用于盛放需要处理的任务。启动一个Timer,从Slot=0处开始,每秒钟向前移动一次,拿到当前Slot中任务数据进行处理,直到数组的最后一个Slot,再从Slot=0开始,循环往复。

环形队列任务处理流程:
在这里插入图片描述
实际的业务场景中还要考虑任务不丢失,故障恢复等问题,所以增加了持久化任务队列,将新加入槽中的任务持久化到Redis,和移除任务队列,将处理完的任务从Redis清除,进程故障恢复后初始化时将存在到Redis中的任务还原到环形队列的相应槽位中。

推单任务处理模型:
在这里插入图片描述

总结

本文主要讲解了实现延时任务的不同方案,各自有不同的优缺点及适用范围,大家有延时任务的需求时可参考,希望给大家带来帮助。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值