所谓的延时任务给大家举个例子:你买了一张火车票,必须在30分钟之内付款,否则该订单被自动取消。订单30分钟不付款自动取消,这个任务就是一个延时任务。我之前已经写过2篇关于延时任务的文章:
《完整实现-通过DelayQueue实现延时任务》
《延时任务(二)-基于netty时间轮算法实战》
这两种方法都有一个缺点:都是基于单体应用的内存的方式运行延时任务的,一旦出现单点故障,可能出现延时任务数据的丢失。所以此篇文章给大家介绍实现延时任务的第三种方式,结合rediszset实现延时任务,可以解决单点故障的问题。给出实现原理、完整实现代码,以及这种实现方式的优缺点。
一、实现原理
首先来介绍一下实现原理,我们需要使用rediszset来实现延时任务的需求,所以我们需要知道zset的应用特性。zset作为redis的有序集合数据结构存在,排序的依据就是score。
所以我们可以利用zsetscore这个排序的这个特性,来实现延时任务
在用户下单的时候,同时生成延时任务放入redis,key是可以自定义的,比如:delaytask:order
value的值分成两个部分,一个部分是score用于排序,一个部分是member,member的值我们设置为订单对象(如:订单编号),因为后续延时任务时效达成的时候,我们需要有一些必要的订单信息(如:订单编号),才能完成订单自动取消关闭的动作。
延时任务实现的重点来了,score我们设置为:订单生成时间+延时时长。这样redis会对zset按照score延时时间进行排序。
开启redis扫描任务,获取"当前时间score"的延时任务并执行。即:当前时间订单生成时间+延时时长的时候,执行延时任务。
二、准备工作
使用rediszset这个方案来完成延时任务的需求,首先肯定是需要redis,这一点毫无疑问。redis的搭建网上有很多的文章,我这里就不赘述了。
其次,笔者长期的java类应用系统开发都是使用SpringBoot来完成,所以也是习惯使用SpringBoot的redis集成方案。首先通过maven坐标引入spring-boot-starter-data-redis
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId/dependencydependencygroupIdorg.apache.