在 AI API 服务中,限流是保护上游服务和控制成本的关键环节。传统的单机令牌桶在分布式场景下无法满足需求,本文介绍如何借助 Redis + Lua 脚本实现精确的分布式限流。
令牌桶 vs 滑动窗口
令牌桶算法允许一定程度的突发流量,更适合 API 调用场景。而滑动窗口则适合需要严格平均的场景。我们选择令牌桶的原因是:用户使用 AI API 通常具有突发性(比如批量推理),适当允许突发能提升用户体验。
Redis Lua 原子操作
核心是通过 Lua 脚本保证"检查剩余令牌 → 扣减令牌"的原子性:
local key = KEYS[1]
local rate = tonumber(ARGV[1])
local capacity = tonumber(ARGV[2])
local now = tonumber(ARGV[3])
local requested = tonumber(ARGV[4])
local last_tokens = tonumber(redis.call("hget", key, "tokens") or capacity)
local last_refreshed = tonumber(redis.call("hget", key, "ts") or now)
local delta = math.max(0, now - last_refreshed)
local filled = math.min(capacity, last_tokens + delta * rate)
if filled >= requested then
redis.call("hset", key, "tokens", filled - requested)
redis.call("hset", key, "ts", now)
redis.call("expire", key, math.ceil(capacity / rate) * 2)
return 1
end
return 0
这段 Lua 脚本在 Redis 中原子执行,即使在高并发场景下也不会出现竞态条件。配合 Go 的 go-redis 客户端,可以轻松集成到中间件中。
多维度限流
实际业务中我们需要支持多维度限流:按用户、按模型、按 IP 等。通过组合 Key 的方式实现:ratelimit:{user_id}:{model},每个维度独立计算,任一维度触发限流即拒绝请求。