基于 Redis 的分布式限流方案实现

阅读时长 9 分钟读完

介绍

在互联网应用中,流量控制是非常关键的一环。流量过大会导致系统崩溃,流量过小会导致用户体验不佳。因此,限流是保障系统稳定性和用户体验的一种重要手段。

在分布式系统中,限流需要考虑多个节点的流量控制,这就需要用到分布式限流方案。本文将介绍基于 Redis 的分布式限流方案实现。

Redis 的限流方案

Redis 是一个高性能的内存数据库,支持多种数据结构,其中包括计数器和有序集合。这两种数据结构可以用来实现限流。

计数器限流

计数器限流是最简单的限流方案之一。它通过对某个时间窗口内的请求计数,来判断是否超过了限制。如果超过了限制,就拒绝请求。

计数器限流的实现非常简单,可以用 Redis 的 INCR 命令实现。每来一个请求,就将计数器加 1,然后与限制值进行比较。如果超过了限制值,就拒绝请求。

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

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

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

上面的代码中,我们将计数器的键名作为参数传入。如果计数器不存在,则创建一个新的计数器,并设置过期时间为时间窗口的长度。如果计数器存在,则通过 INCR 命令将计数器加 1。如果计数器的值超过了限制,则拒绝请求。

有序集合限流

有序集合限流是一种更加高级的限流方案。它通过有序集合来保存请求的时间戳和计数器值,然后使用 Redis 的 ZREMRANGEBYSCORE 命令来删除过期的请求。这种方案可以更加精确地控制请求的数量。

有序集合限流的实现需要用到 Redis 的 ZADD、ZCARD 和 ZREMRANGEBYSCORE 命令。首先,每来一个请求,就将请求的时间戳作为有序集合的分值,将计数器值作为有序集合的成员。然后,使用 ZCARD 命令获取有序集合的长度,如果长度超过了限制,则使用 ZREMRANGEBYSCORE 命令删除过期的请求。

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

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

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

上面的代码中,我们将有序集合的键名作为参数传入。每来一个请求,就将请求的时间戳作为有序集合的分值,将计数器值作为有序集合的成员。然后,使用 ZREMRANGEBYSCORE 命令删除过期的请求。最后,使用 ZCARD 命令获取有序集合的长度,如果长度超过了限制,则拒绝请求。

分布式限流方案

在分布式系统中,需要考虑多个节点的流量控制。这就需要用到分布式限流方案。基于 Redis 的分布式限流方案可以通过 Redis 的 Lua 脚本来实现。

计数器限流

计数器限流的分布式实现可以将计数器的键名加上节点标识符,然后通过 Redis 的 EVALSHA 命令执行 Lua 脚本来实现。

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

上面的 Lua 脚本中,我们将计数器的键名作为参数传入,并加上节点标识符。然后,通过 Redis 的 GET 命令获取计数器的值,如果计数器不存在,则创建一个新的计数器,并设置过期时间为时间窗口的长度。如果计数器存在,则通过 INCR 命令将计数器加 1。如果计数器的值超过了限制,则返回 0,否则返回 1。

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

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

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

上面的代码中,我们将节点的标识符作为参数传入,并将节点的标识符加到计数器的键名中。然后,通过 EVALSHA 命令执行 Lua 脚本。如果 Lua 脚本返回 1,则允许请求,否则拒绝请求。

有序集合限流

有序集合限流的分布式实现需要将有序集合的键名加上节点标识符,并使用 Redis 的 EVALSHA 命令执行 Lua 脚本。

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

上面的 Lua 脚本中,我们将有序集合的键名作为参数传入,并加上节点标识符。然后,将请求的时间戳作为有序集合的分值,将计数器值作为有序集合的成员。使用 ZREMRANGEBYSCORE 命令删除过期的请求。最后,使用 ZCARD 命令获取有序集合的长度,如果长度超过了限制,则返回 0,否则返回 1。

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

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

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

上面的代码中,我们将节点的标识符作为参数传入,并将节点的标识符加到有序集合的键名中。然后,通过 EVALSHA 命令执行 Lua 脚本。如果 Lua 脚本返回 1,则允许请求,否则拒绝请求。

总结

本文介绍了基于 Redis 的分布式限流方案实现。通过计数器和有序集合两种数据结构,我们可以实现简单和精确的限流。通过 Lua 脚本,我们可以实现分布式限流,保证多个节点的流量控制。这种方案可以在高并发的场景下保障系统的稳定性和用户体验。

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

纠错
反馈