业务背景
在平时的业务中我们可能会碰到这样的需求,用户A将任务分配给用户B,如果30天后用户B还没有处理这个任务,那么系统自动将这个任务转发给用户C,或者将任务退回给任务A。
这里我们就可以使用延迟队列,我们写好转发方法或者退回方法,用户A分配任务时将时间记录放入延迟队列。当30天后用户B没有处理,我们获取从延迟队列里面获取这个记录,能获取得到,就执行转发方法或退回方法。如果30天内用户B处理了任务,那么就将延迟队列对应的时间记录删掉。
任务转办
当然延迟队列还有很多使用场景,比如用户下单30分钟还没付款,就将订单关闭、外卖平台发送订餐通知,下单成功后60s给用户推送短信等。
Redis实现的延迟队列
我们可以使用Redis的zset
可以用于作延迟队列,score
为延迟的时间点,获取时顺序获取端口的值,如果当前时间戳等于score
则可取出。
至于如何使用Redis做延迟队列,有兴趣的童鞋可以看看我之前下的这篇文章:
“[Redis实现延迟队列](Redis实现延迟队列 (qq.com))”
Java自带的延迟队列
当然使用Redis做延迟队列并投入生产,其设计还是很复杂的,这里我推荐JDK中的延迟队列API,在java.util.concurrent
包下DelayQueue
。
我们来看看它的使用。
首先我们直接创建一个延迟队列:
代码语言:javascript复制// 延迟消息队列
private static DelayQueue delayQueue = new DelayQueue();
系统启动后,我们先后添加两个消息:
代码语言:javascript复制public static void producer() {
// 添加消息
delayQueue.put(new MyDelay(1000, "消息1"));
delayQueue.put(new MyDelay(3000, "消息2"));
}
我们再来从延迟队列中获取数据:
代码语言:javascript复制public static void consumer() throws InterruptedException {
System.out.println("开始执行时间:"
DateFormat.getDateTimeInstance().format(new Date()));
while (!delayQueue.isEmpty()) {
System.out.println(delayQueue.take());
}
System.out.println("结束执行时间:"
DateFormat.getDateTimeInstance().format(new Date()));
}
测试结果为:
代码语言:javascript复制开始执行时间:2020-12-5 13:50:34
消息1
消息2
结束执行时间:2020-12-5 13:50:37
我们看到,消息2在延迟了3s后才从队列中被取出。
关于DelayQueue
实际是基于优先队列来实现的。所谓的优先队列,出队是按照优先级来出的,并不是像传统的队列那样先进先出。优先队列底层是二叉堆,关于什么是二叉堆,有兴趣的童鞋可以去网上了解下。
下面是优先队列的示例代码:
实体Student
的值:
@AllArgsConstructor
@Data
public class Student {
private Integer score;
private String name;
...
}
这里我们按照score
值从大到小出队。
代码执行结果:
代码语言:javascript复制Name:Zhouzhou Level:100
Name:Lvshen Level:80
Name:Hall Level:60
关于其它的方式实现延迟队列
我在网上收集了几种延迟队列的实现方式:
- 定期轮询(数据库等)
- DelayQueue(JDK的API)
- Timer
- ScheduledExecutorService
- 时间轮(kafka)
- RabbitMQ
- Quartz
- Redis Zset
- Koala
- JCronTab
- SchedulerX(阿里)
- 有赞延迟队列
具体实现方式可以看看这篇文章:
“你真的了解延时队列吗”
好啦今天的文章就到这里啦!如果你的项目中有需要使用延迟队列的地方,希望这篇文章能帮助你。