设计与实现
- 数据结构选择:使用Redis的有序集合(Sorted Set)来记录请求的时间戳。每个请求的时间戳作为有序集合的分数,请求标识作为成员。
- 批量操作技巧:
- 添加请求:使用
ZADD
命令批量添加请求时间戳到有序集合中。例如,一次处理多个请求时,将这些请求的时间戳和对应的标识一次性添加到有序集合。
- 计算窗口内请求数:通过
ZRANGEBYSCORE
命令获取指定时间窗口内的请求成员,然后使用ZCARD
命令获取窗口内的请求数量。可以先通过ZRANGEBYSCORE
获取窗口内的成员列表,再批量处理这些成员(如删除过期请求等操作),最后用ZCARD
获取计数。
- 示例代码(Python + Redis - Py):
import redis
import time
redis_client = redis.Redis(host='localhost', port=6379, db = 0)
def is_allowed(limit, window_seconds):
current_time = time.time()
# 批量添加请求时间戳
pipe = redis_client.pipeline()
pipe.zadd('request_timestamps', {str(int(current_time)): current_time})
pipe.execute()
# 获取窗口内请求数
start_time = current_time - window_seconds
count = redis_client.zcount('request_timestamps', start_time, current_time)
return count <= limit
可能遇到的问题及解决方案
- 时钟漂移问题:
- 问题:不同服务器时钟可能存在微小差异,导致限流不准确。
- 解决方案:使用NTP(网络时间协议)同步服务器时钟,确保各服务器时间一致。
- 缓存雪崩:
- 问题:如果大量请求集中在窗口边界,可能导致缓存中大量数据同时过期,瞬间请求量激增,超出限流阈值。
- 解决方案:
- 随机化窗口时间,使过期时间分散,避免集中过期。
- 采用二级缓存,在主缓存失效时,从二级缓存获取数据,减轻瞬间压力。
- Redis故障:
- 问题:Redis服务器故障可能导致限流功能无法正常工作。
- 解决方案:
- 采用Redis集群部署,提高系统的可用性和容错性。
- 增加本地缓存作为备用方案,在Redis不可用时,暂时使用本地缓存进行限流,但需注意数据一致性问题。