面试题答案
一键面试设计思路
- 固定窗口限流:Redis固定窗口限流简单地统计在一个固定时间窗口内的请求次数,当达到设定的阈值时进行限流。但它存在临界问题,在窗口切换瞬间可能出现突发流量高峰。
- 漏桶算法:漏桶算法将请求看作水,以固定速率从桶中流出。无论请求产生速率如何,处理速率恒定。可用于平滑流量,避免瞬间大量请求。
- 令牌桶算法:令牌桶以固定速率生成令牌放入桶中,请求到来时尝试从桶中获取令牌,有令牌则处理请求,无令牌则限流。能在限制平均速率同时允许一定程度突发流量。
结合方式:可先使用令牌桶算法处理突发流量,允许一定程度的流量高峰,然后用漏桶算法平滑流量,确保整体流量稳定,最后再用Redis固定窗口限流做兜底,防止长时间流量超出预期。
实现方案
- 令牌桶算法实现:
- 使用Redis的
INCR
命令模拟令牌生成。例如,设置一个键token_bucket
,每次生成令牌时执行INCR token_bucket
。 - 设定令牌生成速率和桶的容量。可以通过计算两次生成令牌的时间间隔来控制速率,如每
x
秒生成y
个令牌。 - 请求到达时,通过
GET
命令获取当前令牌数量,若大于0则减少令牌(DECR token_bucket
)并处理请求,否则限流。
- 使用Redis的
- 漏桶算法实现:
- 同样使用Redis,设置一个键
leaky_bucket
表示桶中请求数量。 - 设定漏桶流出速率,如每
m
秒允许处理n
个请求。 - 请求到达时,
INCR leaky_bucket
,同时根据当前时间和上次处理请求时间计算是否达到流出速率,若达到则减少桶中请求数量并处理请求,否则限流。
- 同样使用Redis,设置一个键
- Redis固定窗口限流实现:
- 利用Redis的
INCR
和EXPIRE
命令。设置一个键fixed_window_limit
,每次请求INCR fixed_window_limit
。 - 设定时间窗口和阈值,如1分钟内最多允许100个请求。当
INCR
后的值超过阈值且时间窗口未过期则限流,同时在设置键时用EXPIRE
设置时间窗口。
- 利用Redis的
以下为示例代码(以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