MST

星途 面试题库

面试题:Redis固定窗口限流与其他限流算法结合应用

在复杂的高并发业务场景下,仅使用Redis固定窗口限流可能无法满足需求。请探讨如何将Redis固定窗口限流与其他限流算法(如漏桶算法、令牌桶算法)结合使用,以达到更合理、高效的限流效果,并给出具体的设计思路和实现方案。
38.4万 热度难度
数据库Redis

知识考点

AI 面试

面试题答案

一键面试

设计思路

  1. 固定窗口限流:Redis固定窗口限流简单地统计在一个固定时间窗口内的请求次数,当达到设定的阈值时进行限流。但它存在临界问题,在窗口切换瞬间可能出现突发流量高峰。
  2. 漏桶算法:漏桶算法将请求看作水,以固定速率从桶中流出。无论请求产生速率如何,处理速率恒定。可用于平滑流量,避免瞬间大量请求。
  3. 令牌桶算法:令牌桶以固定速率生成令牌放入桶中,请求到来时尝试从桶中获取令牌,有令牌则处理请求,无令牌则限流。能在限制平均速率同时允许一定程度突发流量。

结合方式:可先使用令牌桶算法处理突发流量,允许一定程度的流量高峰,然后用漏桶算法平滑流量,确保整体流量稳定,最后再用Redis固定窗口限流做兜底,防止长时间流量超出预期。

实现方案

  1. 令牌桶算法实现
    • 使用Redis的 INCR 命令模拟令牌生成。例如,设置一个键 token_bucket,每次生成令牌时执行 INCR token_bucket
    • 设定令牌生成速率和桶的容量。可以通过计算两次生成令牌的时间间隔来控制速率,如每 x 秒生成 y 个令牌。
    • 请求到达时,通过 GET 命令获取当前令牌数量,若大于0则减少令牌(DECR token_bucket)并处理请求,否则限流。
  2. 漏桶算法实现
    • 同样使用Redis,设置一个键 leaky_bucket 表示桶中请求数量。
    • 设定漏桶流出速率,如每 m 秒允许处理 n 个请求。
    • 请求到达时,INCR leaky_bucket,同时根据当前时间和上次处理请求时间计算是否达到流出速率,若达到则减少桶中请求数量并处理请求,否则限流。
  3. Redis固定窗口限流实现
    • 利用Redis的 INCREXPIRE 命令。设置一个键 fixed_window_limit,每次请求 INCR fixed_window_limit
    • 设定时间窗口和阈值,如1分钟内最多允许100个请求。当 INCR 后的值超过阈值且时间窗口未过期则限流,同时在设置键时用 EXPIRE 设置时间窗口。

以下为示例代码(以Python和Redis为例):

import redis
import time

r = redis.Redis(host='localhost', port=6379, db=0)

# 令牌桶算法相关参数
token_rate = 10  # 每秒生成10个令牌
bucket_capacity = 100  # 桶容量100个令牌
last_token_generation = time.time()
current_tokens = bucket_capacity

# 漏桶算法相关参数
leak_rate = 5  # 每秒允许流出5个请求
last_leak_time = time.time()
leaky_bucket_count = 0

# 固定窗口限流相关参数
window_size = 60  # 1分钟
limit = 100  # 1分钟内最多100个请求

def token_bucket():
    global current_tokens, last_token_generation
    now = time.time()
    current_tokens = min(bucket_capacity, current_tokens + (now - last_token_generation) * token_rate)
    last_token_generation = now
    if current_tokens >= 1:
        current_tokens -= 1
        return True
    return False

def leaky_bucket():
    global leaky_bucket_count, last_leak_time
    now = time.time()
    if now - last_leak_time >= 1:
        leaky_bucket_count = max(0, leaky_bucket_count - leak_rate)
        last_leak_time = now
    if leaky_bucket_count < leak_rate:
        leaky_bucket_count += 1
        return True
    return False

def fixed_window_limit():
    key = 'fixed_window_limit'
    r.incr(key)
    r.expire(key, window_size)
    if int(r.get(key) or 0) > limit:
        return False
    return True

def combined_limit():
    if not token_bucket():
        return False
    if not leaky_bucket():
        return False
    if not fixed_window_limit():
        return False
    return True