Redisson是Redis服务器上的分布式可伸缩Java数据结构,这篇文中主要为大家介绍了Redisson实现的优雅的延迟队列的方法,需要的可以参考一下
前言
工作中常常会遇到这样的场景,如订单到期未支付取消,到期自动续费等,我们发现延迟队列非常适合在这样的场景中使用。常见的延迟队列的优秀实现有rabbitMQ的死信队列,RocketMQ的延迟队列等,但是了有时候项目没有特别的大,没有引入类似的消息中间件,但是了又遇到了特别适合使用延迟队列的场景,我们一般会利用已有的redis实现一个简陋的延迟队列。常见的实现方式有监听过期key,使用zset利用分值进行一个匹配,但是了这些实现或多或少有些问题,不够优雅。监听过期key是一种危险行为,一是如果过redis中key数量较大监听过期key可能导致服务负载异常,二是redis中key过期后key是惰性删除的,因此监听机制需要主动触发。利用zset分值实现呢,需要自己开发代码处理定时轮训以及key删除的逻辑,具有一定的工作量和复杂度。哪有没有一种优雅的redis延迟队列的实现呢?
Redisson是Redis服务器上的分布式可伸缩Java数据结构----驻内存数据网格(In-Memory Data Grid,IMDG)。底层使用netty框架,并提供了与java对象相对应的分布式对象、分布式集合、分布式锁和同步器、分布式服务等一系列的Redisson的分布式对象。为我们提供了许多开箱即用的功能。今天介绍Redisson实现的优雅的延迟队列。
使用
依赖配置
备注:处理redisson和springboot兼容性问题
配置文件
springboot整合redisson有三种方式
- 第一种:通用的redis配置+redisson的自动配置[最简单]
- 第二种:使用单独的redisson配置文件
- 第三种:使用spring.redis.redisson这个配置key下进行配置
详细的整合查看 springboot整合redisson配置
demo代码
执行效果
原理分析
从RedissonDelayedQueue
实现中我们看到有四个角色
- redisson_delay_queue_timeout:xxx,sorted set数据类型,存放所有延迟任务,按照延迟任务的到期时间戳(提交任务时的时间戳 + 延迟时间)来排序的,所以列表的最前面的第一个元素就是整个延迟队列中最早要被执行的任务,这个概念很重要
- redisson_delay_queue:xxx,list数据类型,暂时没发现什么用,只是在提交任务时会写入这里面,队列转移时又会删除里面的元素
- xxx:list数据类型,被称为目标队列,这个里面存放的任务都是已经到了延迟时间的,可以被消费者获取的任务,所以上面demo中的RBlockingQueue的take方法是从这个目标队列中获取到任务的
- redisson_delay_queue_channel:xxx,是一个channel,用来通知客户端开启一个延迟任务
队列创建
RedissonDelayedQueue
延迟队列创建时,指定了队列转移服务,以及实现延迟队列的四个重要校色的key。核心代码是指定队列转移任务
生产者
核心代码RedissonDelayedQueue#offerAsync
消费者
消费者最简单,直接从不带前缀的list中BLPOP读取就可以
整个流程
总结思考
Lua是redis的好朋友,我们可以看到Redisson实现延迟队列时,大量使用到lua脚本,因Redis会将整个脚本作为一个整体执行,中间不会被其他请求插入。因此在脚本运行过程中无需担心会出现竞态条件,无需使用事务。我们在平时开发时有多个redis命令操作的有简单的业务逻辑,不妨尝试一下lua脚本的方式,可以避免使用分布式锁来保障一致性。
Redisson的源码值得一读,有很多新东西值得学习,如果其用到的netty基于时间轮算法的定时任务调度,可以让我们基于此实现自己的任务调度框架,也让我有了去探究这种实现方式和基于ScheduledThreadPoolExecutor的定时调度的差异及各自优劣的欲望。
以上就是Redis优雅地实现延迟队列的方法分享的详细内容,更多关于Redis延迟队列的资料请关注编程学习网其它相关文章!