Java 延时队列DelayQueue实现原理及Demo
在上一篇文章中,我们使用jdk自带的DelayQueue延时队列写了一个小demo,现在来看看使用redis的zset有序集合是怎么实现延时队列的。在实现之前,先把zset相关的指令了解一下。
一、zset集合
zset是一个有序集合,通过排序属性score进行排序;也就是说每个存储元素都是由两个元素组成,一个是有序值,另一个是排序值。
二、zset相关指令
#往集合里添加元素
zadd 有序集合key 排序值 有序值 排序值 有序值
# 取出有序集合里的所有元素
zrange 有序集合key 0 -1
# 根据score进行删除
zremrangebyscore 有序集合key 开始score 结束score
参考文档:https://redis.io/docs/manual/data-types
三、使用zset实现延迟队列
3.1 使用当前时间加上随机秒数作为score分值,往key值为AA的zset中添加数据;
@Autowired
private RedisTemplate redisTemplate;
// 日期格式化,精确到时分秒
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
/**
* 60秒执行一次
*/
@Scheduled(cron = "*/60 * * * * ?")
public void initKeys() {
// zset数据添加
ZSetOperations zset = redisTemplate.opsForZSet();
Random random = new Random();
int score = 0;
Long scheduleTime = 0L;
for(int i=0;i<100;i++){
// 取值1到100的随机数
score = random.nextInt(100);
// 设置score,在当前时间上加score秒
scheduleTime = System.currentTimeMillis() + (1000) * score;
log.info("AA{}{},过期时间:{}",i,score, format.format(new Date(scheduleTime)));
zset.add("AA", "AA" + i + score, scheduleTime);
}
}
3.2 根据当前时间取出zset中的过期数据并删除;
// 延迟任务处理
ZSetOperations zset = redisTemplate.opsForZSet();
// TimerTask 定时任务
new Timer().schedule(new TimerTask() {
@Override
public void run() {
Long now = System.currentTimeMillis();
// 取出小于等于当前时间的元素
Set
运行结果:
最后总结
使用redis的有序集合zset实现延迟队列,核心就在score分值上,通过当前时间加上延迟时间作为score,使用zremrangebyscore 命令取出0到当前时间(long型)的元素,能够取出来的就是过期的元素。当然,这个算法也存在不足之处,在取值的时候,采用了轮询,会白白地浪费一部分性能。当然我们主要是了解这个算法是怎么实现的。
参考文档:
https://blog.csdn.net/u012791490/article/details/125243933
https://mp.weixin.qq.com/s/WymM9LDjEcNZDhx4e6nLkA
https://zhuanlan.zhihu.com/p/423916169