• 设为首页
  • 收藏本站
  • 积分充值
  • VIP赞助
  • 手机版
  • 微博
  • 微信
    微信公众号 添加方式:
    1:搜索微信号(888888
    2:扫描左侧二维码
  • 快捷导航
    福建二哥 门户 查看主题

    Redis延迟队列的实现示例

    发布者: 土豆服务器 | 发布时间: 2025-6-19 12:47| 查看数: 120| 评论数: 0|帖子模式

    一、什么是 Redis 延迟队列

    Redis 延迟队列是一种使用 Redis 实现的消息队列,其中的消息在被消费之前会等待一段时间,这段时间就是延迟时间。延迟队列常用于一些需要延迟处理的任务场景,例如订单超时未支付取消、定时提醒等。

    二、实现原理


    • 使用 ZSET(有序集合)存储消息

      • 在 Redis 中,可以使用 ZSET 存储延迟消息。ZSET 的成员是消息的唯一标识,分数(score)是消息的到期时间戳。这样,消息会根据到期时间戳自动排序。
      • 例如,我们可以使用以下 Redis 命令添加一条延迟消息:
      1. ZADD delay_queue <timestamp> <message_id>
      复制代码
      其中
      1. <timestamp>
      复制代码
      是消息到期的时间戳,
      1. <message_id>
      复制代码
      是消息的唯一标识。

    • 消费者轮询 ZSET

      • 消费者会不断轮询 ZSET,使用
        1. ZRANGEBYSCORE
        复制代码
        命令查找分数小于或等于当前时间戳的元素。
      • 例如:
      1. ZRANGEBYSCORE delay_queue 0 <current_timestamp>
      复制代码
      这里的
      1. 0
      复制代码
      表示最小分数,
      1. <current_timestamp>
      复制代码
      是当前时间戳,这个命令会返回所有到期的消息。

    • 处理到期消息

      • 当消费者找到到期消息后,会将消息从 ZSET 中移除并进行处理。可以使用
        1. ZREM
        复制代码
        命令移除消息:
      1. ZREM delay_queue <message_id>
      复制代码
      然后将消息发送到实际的消息处理程序中。


    三、Java 代码示例

    以下是一个使用 Jedis(Redis 的 Java 客户端)实现 Redis 延迟队列的简单示例:
    1. import redis.clients.jedis.Jedis;
    2. import java.util.Set;

    3. public class RedisDelayQueue {
    4.     private Jedis jedis;

    5.     public RedisDelayQueue() {
    6.         jedis = new Jedis("localhost", 6379);
    7.     }

    8.     // 生产者添加延迟消息
    9.     public void addDelayMessage(String messageId, long delayMillis) {
    10.         long score = System.currentTimeMillis() + delayMillis;
    11.         jedis.zadd("delay_queue", score, messageId);
    12.     }

    13.     // 消费者轮询并处理消息
    14.     public void consume() {
    15.         while (true) {
    16.             // 查找到期的消息
    17.             Set<String> messages = jedis.zrangeByScore("delay_queue", 0, System.currentTimeMillis(), 0, 1);
    18.             if (messages.isEmpty()) {
    19.                 try {
    20.                     // 没有消息,等待一段时间再轮询
    21.                     Thread.sleep(100);
    22.                 } catch (InterruptedException e) {
    23.                     Thread.currentThread().interrupt();
    24.                 }
    25.                 continue;
    26.             }
    27.             String messageId = messages.iterator().next();
    28.             // 移除消息
    29.             Long removed = jedis.zrem("delay_queue", messageId);
    30.             if (removed > 0) {
    31.                 // 消息成功移除,进行处理
    32.                 System.out.println("Processing message: " + messageId);
    33.                 // 在这里添加实际的消息处理逻辑
    34.             }
    35.         }
    36.     }

    37.     public static void main(String[] args) {
    38.         RedisDelayQueue delayQueue = new RedisDelayQueue();
    39.         // 生产者添加消息,延迟 5 秒
    40.         delayQueue.addDelayMessage("message_1", 5000);
    41.         // 启动消费者
    42.         delayQueue.consume();
    43.     }
    44. }
    复制代码
    代码解释

      1. RedisDelayQueue
      复制代码
      类封装了延迟队列的基本操作。
      1. addDelayMessage
      复制代码
      方法:

      • 计算消息的到期时间戳,将消息添加到
        1. delay_queue
        复制代码
        ZSET 中,使用
        1. jedis.zadd
        复制代码
        命令。

      1. consume
      复制代码
      方法:

      • 不断轮询
        1. delay_queue
        复制代码
        ZSET,使用
        1. jedis.zrangeByScore
        复制代码
        查找到期消息。
      • 如果没有消息,线程休眠 100 毫秒后继续轮询。
      • 若找到消息,使用
        1. jedis.zrem
        复制代码
        移除消息,如果移除成功,说明该消息被此消费者处理,进行后续处理。


    四、注意事项


    • 并发处理

      • 多个消费者同时轮询 ZSET 时,可能会出现竞争条件,需要注意消息的重复处理问题。可以使用 Redis 的事务(
        1. MULTI
        复制代码
        1. EXEC
        复制代码
        )或 Lua 脚本保证原子性。
      • 例如,可以使用 Lua 脚本将查找和移除操作合并为一个原子操作:
      1. local message = redis.call('ZRANGEBYSCORE', 'delay_queue', 0, ARGV[1], 'LIMIT', 0, 1)
      2. if #message > 0 then
      3.     if redis.call('ZREM', 'delay_queue', message[1]) == 1 then
      4.         return message[1]
      5.     end
      6. end
      7. return nil
      复制代码
      然后在 Java 中调用这个脚本:
      1. String script = "local message = redis.call('ZRANGEBYSCORE', 'delay_queue', 0, ARGV[1], 'LIMIT', 0, 1)\n" +
      2.                "if #message > 0 then\n" +
      3.                "    if redis.call('ZREM', 'delay_queue', message[1]) == 1 then\n" +
      4.                "        return message[1]\n" +
      5.                "    end\n" +
      6.                "end\n" +
      7.                "return nil";
      8. while (true) {
      9.     String messageId = (String) jedis.eval(script, 0, String.valueOf(System.currentTimeMillis()));
      10.     if (messageId!= null) {
      11.         System.out.println("Processing message: " + messageId);
      12.         // 在这里添加实际的消息处理逻辑
      13.     } else {
      14.         try {
      15.             Thread.sleep(100);
      16.         } catch (InterruptedException e) {
      17.             Thread.currentThread().interrupt();
      18.         }
      19.     }
      20. }
      复制代码
    • 消息持久化

      • Redis 是内存数据库,需要考虑消息的持久化问题,确保在 Redis 重启后不会丢失重要消息。可以使用 Redis 的 RDB 或 AOF 持久化机制,但要注意性能和数据安全的平衡。


    五、使用 Redis 模块

    除了上述基本实现,还可以使用 Redis 的一些第三方模块,如 Redis 的
    1. Redisson
    复制代码
    库,它提供了更高级的延迟队列实现,使用更加方便和可靠:
    1. import org.redisson.Redisson;
    2. import org.redisson.api.RBlockingQueue;
    3. import org.redisson.api.RDelayedQueue;
    4. import org.redisson.api.RedissonClient;
    5. import org.redisson.config.Config;
    6. import java.util.concurrent.TimeUnit;

    7. public class RedissonDelayQueueExample {
    8.     public static void main(String[] args) {
    9.         Config config = new Config();
    10.         config.useSingleServer().setAddress("redis://127.0.0.1:6379");
    11.         RedissonClient redisson = Redisson.create(config);

    12.         RBlockingQueue<String> blockingQueue = redisson.getBlockingQueue("myQueue");
    13.         RDelayedQueue<String> delayedQueue = redisson.getDelayedQueue(blockingQueue);

    14.         // 生产者添加延迟消息
    15.         delayedQueue.offer("message_1", 5, TimeUnit.SECONDS);

    16.         // 消费者
    17.         new Thread(() -> {
    18.             while (true) {
    19.                 try {
    20.                     String message = blockingQueue.take();
    21.                     System.out.println("Processing message: " + message);
    22.                     // 在这里添加实际的消息处理逻辑
    23.                 } catch (InterruptedException e) {
    24.                     Thread.currentThread().interrupt();
    25.                 }
    26.             }
    27.         }).start();
    28.     }
    29. }
    复制代码
    代码解释

      1. Redisson
      复制代码
      是一个功能强大的 Redis 客户端库。
      1. RBlockingQueue
      复制代码
      是阻塞队列,
      1. RDelayedQueue
      复制代码
      是延迟队列。
    • 使用
      1. delayedQueue.offer("message_1", 5, TimeUnit.SECONDS)
      复制代码
      添加延迟消息。
    • 消费者通过
      1. blockingQueue.take()
      复制代码
      阻塞等待消息,当消息到期时,会自动从延迟队列转移到阻塞队列并被消费者接收。
    通过上述几种方法,可以使用 Redis 实现延迟队列,满足不同场景下的延迟任务处理需求。根据具体情况,可以选择简单的 ZSET 实现或使用更高级的第三方库,同时要注意并发处理和消息持久化等问题,以确保延迟队列的稳定性和可靠性。
    总之,Redis 延迟队列是一种高效且灵活的实现延迟任务的方式,在分布式系统中具有广泛的应用,利用 Redis 的特性可以轻松处理延迟消息,减少系统的复杂性和开发成本。
    到此这篇关于Redis延迟队列的实现示例的文章就介绍到这了,更多相关Redis延迟队列内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

    来源:https://www.jb51.net/database/334481yw2.htm
    免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

    最新评论

    浏览过的版块

    QQ Archiver 手机版 小黑屋 福建二哥 ( 闽ICP备2022004717号|闽公网安备35052402000345号 )

    Powered by Discuz! X3.5 © 2001-2023

    快速回复 返回顶部 返回列表