Redis 实现延迟队列的方案及实践

阅读时长 11 分钟读完

什么是延迟队列?

延迟队列是一种处理任务的方式,可以在一定时间后再执行某些特定操作,通常应用于消息队列中。举个例子,在订单系统中,如果订单过期未支付,可以将订单信息放入延迟队列中,在一定的时间后检查该订单是否支付,如果没有支付,则触发将该订单关闭的操作。

为什么要用 Redis 实现延迟队列?

Redis 具有高性能、高可用性、支持分布式部署等特点,因此成为了实现延迟队列的最佳选择。

Redis 实现延迟队列的方案

基于 Redis 的 Zset

Redis 的 Zset 是有序集合,可以排序,每个元素还绑定了一个分数。在延迟队列中可以将任务作为 Zset 的元素,将执行时间作为分数,实现按照时间顺序排列。定时任务调度器每隔一段时间(比如 1 秒)去查询 Zset 中分数在当前时间范围内的任务,如果查询到任务,则将其弹出队列,执行任务。

基于 Redis 的 List

Redis 的 List 是一个双端队列,支持在队头和队尾进行插入和删除操作。在延迟队列中可以将任务作为 List 的元素,将执行时间作为任务的一个属性,如下图所示:

在每个时间单位(比如 1 秒),将队列的第一个任务弹出,判断其执行时间和当前时间的差值,如果大于零,则重新将该任务加入队列,如果小于等于零,则执行该任务。

Redis 实现延迟队列的实践

下面我们结合代码来实现一个基于 Redis 的延迟队列。

安装 Redis

首先我们需要在本地安装 Redis,可以前往官网下载对应版本:https://redis.io/download

引入依赖

接着我们需要引入 Redis 相关依赖,例如 redis.clients:jedisorg.springframework.data:spring-data-redis 等。

实现基于 Zset 的延迟队列

-- -------------------- ---- -------
------
----------
------ ----- ----------------------- -

    ----------
    ------- --------- ----------

    ---
     - - ---- -----
     -
     - ------ ------- ----
     - ------ -----   -------
     - ------- ------
     --
    ------ ------- ----------------- -------- ---- ------ -
        ----- ----- - -----
        --- -
            ----- - ------------------------
            ---- ---- - -------------------------- - ----- - -----
            ------ ------------------------------ ----- -------- -- --
        - ----- ---------- -- -
            --------------------- ------ ----------- ---------- -------- ------ ---
            ------ ------
        - ------- -
            -- ------ -- ----- -
                --------------
            -
        -
    -

    ---
     - -- ---- ----
     --
    ------ ---- ---------------- -
        ----- ----- - -----
        --- -
            ----- - ------------------------
            ----- ------ -
                ----------- --- - --------------------------------------- -- --------------------------- -- ---
                -- --------------- -
                    -------------------
                    ---------
                -
                ------ ------- - ----------------------
                -- ------------------------------- -------- -- -- -
                    ----------------- -------- ---- ---------
                -
            -
        - ----- ---------- -- -
            ------------------------- ------- ---
        - ------- -
            -- ------ -- ----- -
                --------------
            -
        -
    -
-

首先我们定义了一个 RedisDelayQueueWithZset 类,该类提供了向 Zset 中添加消息的方法 addMessage 和消费 Zset 中的消息的方法 consumeMessage

addMessage 方法中,我们使用 zadd 方法将消息添加到 Zset 中,其中 delay 参数代表了延迟的时间,使用 System.currentTimeMillis() 计算了一个到期时间。在返回值中,我们通过判断插入是否成功来判断是否成功添加消息。

consumeMessage 方法中,我们使用 zrangeByScore 方法查询符合条件的消息,如果没有符合条件的消息,则等待 1 秒继续查询。如果有符合条件的消息,则使用 zrem 方法将其从 Zset 中删除,并打印出该消息。

实现基于 List 的延迟队列

-- -------------------- ---- -------
------
----------
------ ----- ----------------------- -

    ----------
    ------- ------------------- --------------------

    ---
     - - ---- -----
     -
     - ------ ------- ----
     - ------ -----   -------
     - ------- ------
     --
    ------ ------- ----------------- -------- ---- ------ -
        --- -
            ---- ---- - -------------------------- - ----- - -----
            ------------------------------------------------------------- ---- - --- - ---------
            ------ -----
        - ----- ---------- -- -
            --------------------- ------ ----------- ---------- -------- ------ ---
            ------ ------
        -
    -

    ---
     - -- ---- ----
     --
    ------ ---- ---------------- -
        ----- ------ -
            ------ ----- - --------------------------------------------------------------
            -- ------ -- ----- -
                --- -
                    -------------------
                - ----- --------------------- -- -
                    ------------------------- ------- ---
                -
            - ---- -
                -------- -------- - ---------------- ---
                ---- ---- - ----------------------------
                -- ----- - --------------------------- -
                    -------------------------------------------------------------- -------
                    --- -
                        -------------------
                    - ----- --------------------- -- -
                        ------------------------- ------- ---
                    -
                - ---- -
                    ----------------- -------- ---- -------------
                -
            -
        -
    -
-

RedisDelayQueueWithZset 类似,我们定义了一个 RedisDelayQueueWithList 类,该类提供了向 List 中添加消息的方法 addMessage 和消费 List 中的消息的方法 consumeMessage

addMessage 方法中,我们将消息和有效时间拼接成一个字符串,使用 opsForList().leftPush 方法将消息存入 List 中。

consumeMessage 方法中,我们使用 opsForList().rightPop 方法获取 List 中的首位元素。如果该元素为空,说明 List 中没有任务需要执行,则等待 1 秒继续查询。如果该元素不为空,则判断该任务的有效时间和当前时间的差值,如果大于零则继续将该任务插入队列等待执行,如果小于等于零则执行该任务。

测试代码

-- -------------------- ---- -------
---------------
------
----- ------------------------------- -

    ----------
    ------- ----------------------- ------------------------

    ----------
    ------- ----------------------- ------------------------

    -----
    ---- ----------------------------- ------ -------------------- -
        ----------------------------------------- ------ ---
        ----------------------------------------- ------ ----
        ----------------------------------------- ------ ---
        ----------------------------------------- ------ ---
        ----------------------------------------- ------ ---
        ----- ------ -
            -----------------------------------------
            -------------------
        -
    -

    -----
    ---- ----------------------------- ------ -------------------- -
        ----------------------------------------- ------ ---
        ----------------------------------------- ------ ----
        ----------------------------------------- ------ ---
        ----------------------------------------- ------ ---
        ----------------------------------------- ------ ---
        ----- ------ -
            -----------------------------------------
        -
    -
-

我们使用 JUnit 进行测试,在 RedisDelayQueueWithZset 中调用了 addMessage 方法添加了五个消息,在 RedisDelayQueueWithList 中调用了 addMessage 方法添加了五个消息,然后分别调用了 consumeMessage 方法。

总结

本文介绍了 Redis 实现延迟队列的方案,并通过代码示例进行了实践。Zset 和 List 都是实现延迟队列的常见方案,选取哪种方案还需要根据具体业务场景进行评估。我希望本文可以对大家理解延迟队列有所帮助,并能够帮助大家在实际业务中灵活使用 Redis 实现延迟队列。

来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/64717db8968c7c53b0f5a75e

纠错
反馈