一、使用 Redis 的 Sorted Set 实现
-
实现原理 :
-
将消息的唯一标识作为成员,消息的执行时间(通常为时间戳)作为分数,存入 Sorted Set 中。消费者通过定时任务不断查询 Sorted Set,获取分数小于等于当前时间的消息进行处理,并将已处理的消息从 Sorted Set 中移除。
-
-
优点 :
-
灵活方便,无需额外搭建环境,利用 Redis 本身即可实现。
-
可进行消息持久化,提高延迟队列的可靠性。
-
支持分布式,利用 Redis 的高可用方案,可实现分布式环境下的延迟队列
-
-
需要主动轮询,增加 CPU 使用率,且延迟精度受轮询间隔影响。
-
多个消费者同时查询 Sorted Set 时,可能会出现重复获取任务的情况,需要在业务逻辑中实现幂等性。
二、使用 Redis 的过期 key 机制实现
实现原理 :
-
将延迟任务本身作为 key,延迟时间作为过期时间,存入 Redis 中。通过 Redis 的
notify-keyspace-events
配置,使 Redis 在 key 过期时发布事件,消费者监听该事件并处理任务。
优点 :
-
自动清理过期 key,减少了内存占用。
-
实现相对简单,利用 Redis 内置的过期机制即可。
缺点 :
-
实时性不足,Redis 的过期 key 并非实时删除,可能导致任务延迟执行。
-
缺乏灵活性,只支持基本的过期时间设置,无法满足复杂的延迟逻辑。
-
消息丢失风险,当 Redis 重启或客户端断开连接时,可能会丢失部分过期事件。
三、使用 Redis Streams 实现
实现原理 :
-
生产者将消息及其延迟时间发送到 Redis Stream 中,消费者定期检查 Stream 中的消息,当消息的延迟时间小于等于当前时间时,将消息从 Stream 中读取并处理
优点 :
-
支持消息的持久化存储和回溯,即使 Redis 重启,消息也不会丢失。
-
可以有多个消费者组同时读取消息,实现消息的分布式处理和负载均衡。
-
消费者可以从上次读取的位置继续读取消息,保证了消息处理的顺序性。
缺点 :
-
消费者需要定期轮询 Stream,可能会对 Redis 服务器造成一定压力。
-
Stream 的消息读取需要处理消息的确认和重复消费问题,实现较为复杂。
四、使用 Redisson 实现
实现原理 :
-
Redisson 提供了
RDelayedQueue
类,其底层基于ZSet
结构实现。生产者将延迟任务存入RDelayedQueue
,消费者从RDelayedQueue
中取出任务执行。RDelayedQueue
会自动将延迟队列中的任务转移到一个RQueue
中,消费者只需从RQueue
中取出任务执行即可。
优点 :
-
使用简单,Redisson 封装了底层操作,提供了易于使用的 API。
-
支持分布式,可在多个 Redis 实例之间共享和同步延迟队列状态。
-
原子性操作,利用 Lua 脚本保证操作的原子性,避免并发问题。
-
支持持久化,数据存储在 Redis 中,可利用 Redis 的持久化机制避免数据丢失。
缺点 :
-
引入了 Redisson 依赖,增加了项目的复杂度和学习成本。
-
需要 Redis 作为存储支持,对 Redis 的依赖可能成为潜在风险点。
-
客户端需要不断轮询,可能导致不必要的性能开销。