Redis 限流

发布于:2025-04-02 ⋅ 阅读:(24) ⋅ 点赞:(0)

Redis 可以用来做限流,常见的限流实现方式包括以下几种:

1. 固定窗口计数限流

适用于简单的请求限流,比如每秒最多允许 100 次请求。

实现方式:

  • 设定一个键(Key),比如 rate_limit:user_123
  • 在固定时间窗口(如 1 秒)内累加请求计数
  • 若计数超过阈值,则拒绝请求

示例代码(使用 Lua 脚本实现原子操作):

local key = KEYS[1]         -- 限流的 Redis 键
local limit = tonumber(ARGV[1])  -- 最大请求数
local expire_time = tonumber(ARGV[2]) -- 窗口过期时间 (秒)

local current = redis.call("INCR", key)
if current == 1 then
    redis.call("EXPIRE", key, expire_time)
end

if current > limit then
    return 0  -- 限流
else
    return 1  -- 允许请求
end

使用示例(在 Java 中执行):

String script = "..." // 上面的 Lua 脚本
List<String> keys = Collections.singletonList("rate_limit:user_123");
List<String> args = Arrays.asList("100", "1"); // 100 次/秒
Long result = (Long) jedis.eval(script, keys, args);
if (result == 0) {
    System.out.println("请求过多,限流!");
}

2. 滑动窗口限流

固定窗口的缺点是,窗口切换时可能导致流量突增。滑动窗口限流可以缓解这个问题。

实现方式:

  • 维护一个 有序集合(Sorted Set),记录请求时间戳
  • 每次请求时,移除超出时间范围的数据
  • 判断当前窗口内的请求数是否超过阈值

示例代码:

local key = KEYS[1]
local now = tonumber(ARGV[1])
local window_size = tonumber(ARGV[2])
local max_count = tonumber(ARGV[3])

redis.call("ZREMRANGEBYSCORE", key, 0, now - window_size)
local count = redis.call("ZCARD", key)

if count >= max_count then
    return 0
else
    redis.call("ZADD", key, now, now)
    redis.call("EXPIRE", key, window_size)
    return 1
end

3. 令牌桶算法

令牌桶适用于 平滑流量,允许一定程度的突发流量。

实现方式:

  • 设定一个令牌桶,每秒按固定速率生成令牌
  • 请求时从桶里取令牌,若令牌不足则拒绝请求

Redis 实现(Lua 脚本):

local key = KEYS[1]
local rate = tonumber(ARGV[1]) -- 令牌生成速率(每秒生成多少)
local capacity = tonumber(ARGV[2]) -- 桶容量
local now = tonumber(ARGV[3])

local token_key = key .. ":tokens"
local timestamp_key = key .. ":timestamp"

local last_time = tonumber(redis.call("GET", timestamp_key)) or now
local tokens = tonumber(redis.call("GET", token_key)) or capacity

local elapsed = now - last_time
local new_tokens = math.min(capacity, tokens + (elapsed * rate))
if new_tokens < 1 then
    return 0  -- 没有令牌,拒绝请求
else
    redis.call("SET", token_key, new_tokens - 1)
    redis.call("SET", timestamp_key, now)
    return 1  -- 允许请求
end

4. 漏桶算法

漏桶算法适用于 平滑处理请求,防止流量突增。

实现方式:

  • 设定一个队列(List),存放请求
  • 以固定速率处理请求,多余的请求直接丢弃

示例:

local key = KEYS[1]
local rate = tonumber(ARGV[1])  -- 处理速率(多少秒处理一个请求)
local capacity = tonumber(ARGV[2]) -- 队列最大长度
local now = tonumber(ARGV[3])

local size = redis.call("LLEN", key)
if size >= capacity then
    return 0 -- 队列已满,丢弃请求
else
    redis.call("LPUSH", key, now)
    redis.call("LTRIM", key, 0, capacity - 1)
    redis.call("EXPIRE", key, rate * capacity)
    return 1
end

选择适合的限流算法

算法 适用场景 特点
固定窗口 简单限流,适合低并发 实现简单,窗口切换时可能突发
滑动窗口 适用于高并发场景 计算较复杂,能更平滑地限流
令牌桶 允许突发流量,保证一定吞吐量 适用于 API 请求限流
漏桶 平滑请求处理,防止流量抖动 控制流出速率,避免流量过载

你目前的项目是 高并发的 Spring Boot 订单系统,可以考虑 令牌桶算法,用 Redis 来实现限流,结合 Spring Boot 的拦截器或者网关过滤器来执行。


网站公告

今日签到

点亮在社区的每一天
去签到