延迟任务的设计思路

开场白

日常的业务开发常有延迟触发的需求,比如常见的订单创建一段时间未付款,会自动触发关闭;注册用户一段时间内未完善资料,可以触发提醒资料更新等。这样的需求,就是延迟调度的应用场景。

本文旨在提出一种分布式延迟调度的实现方法,为需要进行延迟调度设计的童鞋提供一种设计思路。

实现方式

实现一、QelayQueue调度实现

DelayQueue是一个高效的内存延时阻塞队列,可以为任务元素增加延迟获取的时间,从而实现在单Java进程内延迟触发。

优点:

延迟任务可精确触发。
缺点:

支持单进程内调度,分布式场景下的需求无法支持;
DelayQueue是内存队列,进程宕机、重启都会造成任务丢失。

实现二、周期任务 + 数据库

将任务记录写入数据库,使用固定周期从数据库中加载延迟任务并执行,这是一种相对简单但暴力有效的实习思路。

优点:

任务记录写入数据库,可防止进程崩溃引起的任务丢失。
缺点:

周期任务扫描,临界点任务可能到导致多延迟一个周期触发;
如果把周期设置的很低(比如1s),则会给数据库带来负担。

实现三、周期任务 + DelayQueue + 数据库

另外一个思路:

设定一个延迟阈值,比如5m;
所有延迟任务记录先写入数据库;
延迟时间在阈值的内任务直接放入DelayQueue排队,到期则触发;
另起一个周期任务,设定阈值执行周期,每次扫描下一个周期内任务,并加载DelayQueue排队,到期则触发。
通过以上操作,保证最近延迟阈值内的任务,都已经在DelayQueue中排队等待触发。需要注意的是,任务记录需要有状态,“创建”、“排队”、“触发成功”、“触发失败”,都需做状态更新,避免任务重复执行。

实现流程可参考下图:
在这里插入图片描述

优点:

延迟任务可精确触发
任务记录写入数据库,防止进程崩溃引起任务丢失;
周期加载延迟任务无需高频率执行,减少数据库负担。
缺点:

周期加载延迟任务在进程内管理(未做分布式协调),多点部署的场景下,可能被多次执行,需要处理额外的争用问题。

实现四、Quartz + DelayQueue + 数据库实现

考虑"实现三"中在进程内周期加载延迟任务存在多次执行的问题,可引入Quartz进行任务管理,保证多点部署场景下,同一个任务只能在一台机器上执行,从而解决多点部署的场景下任务被多次执行的问题。
实现流程可参考下图:
在这里插入图片描述

优点:

延迟任务可精确触发;
延迟任务持久化,保证不会出现任务丢失;
没有高频率的轮询操作,不会给数据库造成负担;
一般业务量不大的系统,使用此实现完全足够。

考虑这样一个场景,如果一秒内要触发的任务量很大,使用Quartz又只会在一个节点上进行延迟任务加载(Quartz不支持任务分片,只能支持失败飘移),那延迟任务能精确触发吗?

实现五、分布式调度 + DelayQueue + 数据库

分布式调度系统(xxl-job、elastic-job、tb-schedule)均支持对任务进行分片,因此使用分布式调度系统替换Quartz,周期任务使用分片策略,即可实现在多机集群上用分片信息加载不同的延迟任务,从而充分发挥多机并行的计算能力,尽可能的保证延迟触发的精确性。

实现六、订单是存在数据库的,数据库不可能主动来找我,这里提供两种方案:

一、
通过消息队列(RabbitMQ、RocketMQ)延迟队列来解决,发送消息的时候设置一个消费(过期)时间,到了过期时间消费者自动获取这条消息来进行业务处理。
二、
通过Redis来进行订单过期时间记录,下订单的时候给Key设置一个过期时间,系统监听过期的Key就可以了,我们这里就用第二种实现方式
————————————————

一些后话

1. 数据库中延迟记录膨胀
任务记录在日积月累,数据库中的延迟任务记录会不断膨胀。基于延迟任务特性,一般都是在一个相对较短的时间内触发,因此可以在任务触发成功后,将记录迁到历史,只在当前库中存储还未触发、触发失败的任务,从而降低当前库的数据量,以提升在任务表上的查询性能。当然还可以有分库分表等技术方案可以考虑。
2. 处理好明确失败的任务补偿
对于触发失败的任务,已经失去精确触发的意义,处理时,首先可以在现场执行补偿(可使用spring-retry等工具,同时需要补偿回调具备幂等性),另外起一个线程补偿任务,周期扫描延迟任务记录表,补偿触发即可。
3. 处理好进程崩溃、重启的任务补偿
对于任务长时间处于"创建"、"排队"等状态的任务,应设定一定阈值(比如10m),超过延迟触发时间点一个阈值周期的任务,理解为已经失败,进行单独扫描补偿。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值