Redis实现延迟队列
延迟消息队列的常见实现方式是通过 ZSet 的存储于查询来实现,它的核心思想是在程序中开启一个一直循环的延迟任务的检测器,用于检测和调用延迟任务的执行
如下图所示:

方式一:zrangebyscore 查询所有任务
此实现方式是一次性查询出所有的延迟任务,然后再进行执行,实现代码如下:
import redis.clients.jedis.Jedis; import utils.JedisUtils; import java.time.Instant; import java.util.Set; /** * 延迟队列 */ public class DelayQueueExample { // zset key private static final String _KEY = "myDelayQueue"; public static void main(String[] args) throws InterruptedException { Jedis jedis = JedisUtils.getJedis(); // 延迟 30s 执行(30s 后的时间) long delayTime = Instant.now().plusSeconds(30).getEpochSecond(); jedis.zadd(_KEY, delayTime, "order_1"); // 继续添加测试数据 jedis.zadd(_KEY, Instant.now().plusSeconds(2).getEpochSecond(), "order_2"); jedis.zadd(_KEY, Instant.now().plusSeconds(2).getEpochSecond(), "order_3"); jedis.zadd(_KEY, Instant.now().plusSeconds(7).getEpochSecond(), "order_4"); jedis.zadd(_KEY, Instant.now().plusSeconds(10).getEpochSecond(), "order_5"); // 开启延迟队列 doDelayQueue(jedis); } /** * 延迟队列消费 * @param jedis Redis 客户端 */ public static void doDelayQueue(Jedis jedis) throws InterruptedException { while (true) { // 当前时间 Instant nowInstant = Instant.now(); long lastSecond = nowInstant.plusSeconds(-1).getEpochSecond(); // 上一秒时间 long nowSecond = nowInstant.getEpochSecond(); // 查询当前时间的所有任务 Set<String> data = jedis.zrangeByScore(_KEY, lastSecond, nowSecond); for (String item : data) { // 消费任务 System.out.println("消费:" + item); } // 删除已经执行的任务 jedis.zremrangeByScore(_KEY, lastSecond, nowSecond); Thread.sleep(1000); // 每秒轮询一次 } } }
以上程序执行结果如下:
消费:order2 消费:order3 消费:order4 消费:order5 消费:order_1
方式二:判断最早的任务
此实现方式是每次查询最早的一条任务,再与当前时间进行判断,如果任务执行时间大于当前时间则表示应该立即执行延迟任务,实现代码如下:
import redis.clients.jedis.Jedis; import utils.JedisUtils; import java.time.Instant; import java.util.Set; /** * 延迟队列 */ public class DelayQueueExample { // zset key private static final String _KEY = "myDelayQueue"; public static void main(String[] args) throws InterruptedException { Jedis jedis = JedisUtils.getJedis(); // 延迟 30s 执行(30s 后的时间) long delayTime = Instant.now().plusSeconds(30).getEpochSecond(); jedis.zadd(_KEY, delayTime, "order_1"); // 继续添加测试数据 jedis.zadd(_KEY, Instant.now().plusSeconds(2).getEpochSecond(), "order_2"); jedis.zadd(_KEY, Instant.now().plusSeconds(2).getEpochSecond(), "order_3"); jedis.zadd(_KEY, Instant.now().plusSeconds(7).getEpochSecond(), "order_4"); jedis.zadd(_KEY, Instant.now().plusSeconds(10).getEpochSecond(), "order_5"); // 开启延迟队列 doDelayQueue2(jedis); } /** * 延迟队列消费(方式 2) * @param jedis Redis 客户端 */ public static void doDelayQueue2(Jedis jedis) throws InterruptedException { while (true) { // 当前时间 long nowSecond = Instant.now().getEpochSecond(); // 每次查询一条消息,判断此消息的执行时间 Set<String> data = jedis.zrange(_KEY, 0, 0); if (data.size() == 1) { String firstValue = data.iterator().next(); // 消息执行时间 Double score = jedis.zscore(_KEY, firstValue); if (nowSecond >= score) { // 消费消息(业务功能处理) System.out.println("消费消息:" + firstValue); // 删除已经执行的任务 jedis.zrem(_KEY, firstValue); } } Thread.sleep(100); // 执行间隔 } } }
以上程序执行结果和实现方式一相同,结果如下:
消费:order2 消费:order3 消费:order4 消费:order5 消费:order_1

浙公网安备 33010602011771号