[深度]API限流算法完全解析:令牌桶、漏桶与滑动窗口的选择

阿里云推广

限流:保护系统稳定的最后一道防线

不管你的系统设计多么完美,总有人会用你意想不到的方式调用API——爬虫、压测、BUG循环请求或者恶意攻击。限流是保护下游服务、防止系统雪崩的必备手段。

一、四种限流算法对比

算法 原理 优点 缺点 适用场景
固定窗口计数 每N秒重置计数器 实现简单 边界突刺问题 粗粒度限制
滑动窗口计数 按时间戳滑动统计 平滑,无突刺 内存占用较高 精确限流
漏桶 固定速率流出 绝对平滑输出 不允许突发流量 匀速处理
令牌桶 匀速生产令牌,可积累 允许合理突发 实现稍复杂 API限流首选

二、令牌桶算法:最实用的限流方案

令牌桶模型:桶以固定速率添加令牌(如每秒100个),每个请求消耗1个令牌;桶满时新令牌丢弃;桶空时请求被拒绝。这允许短时间内的突发流量(利用积累的令牌),同时保证长期平均速率。

import time
import threading

class TokenBucket:
    def __init__(self, rate, burst):
        """rate=每秒令牌数, burst=桶容量(最大突发)"""
        self.rate = rate
        self.burst = burst
        self.tokens = burst    # 初始满桶
        self.last_refill = time.time()
        self.lock = threading.Lock()

    def acquire(self):
        with self.lock:
            now = time.time()
            # 计算应补充的令牌数
            elapsed = now - self.last_refill
            self.tokens = min(self.burst,
                              self.tokens + elapsed * self.rate)
            self.last_refill = now

            if self.tokens >= 1:
                self.tokens -= 1
                return True    # 通过
            return False       # 限流

# 使用:每秒100请求,最大突发200
limiter = TokenBucket(rate=100, burst=200)

三、分布式限流:Redis实现

单机令牌桶无法跨节点共享状态,多实例部署需要基于Redis的分布式限流:

-- Lua脚本(原子操作,Redis执行)
local key = KEYS[1]          -- 限流key
local capacity = tonumber(ARGV[1])   -- 桶容量
local rate = tonumber(ARGV[2])       -- 每秒令牌数
local now = tonumber(ARGV[3])        -- 当前时间戳(毫秒)

local last_tokens = tonumber(redis.call('HGET', key, 'tokens') or capacity)
local last_time = tonumber(redis.call('HGET', key, 'ts') or now)

-- 补充令牌
local elapsed = (now - last_time) / 1000.0
local new_tokens = math.min(capacity, last_tokens + elapsed * rate)

if new_tokens >= 1 then
    redis.call('HSET', key, 'tokens', new_tokens - 1, 'ts', now)
    redis.call('EXPIRE', key, 60)
    return 1   -- 通过
else
    redis.call('HSET', key, 'tokens', new_tokens, 'ts', now)
    return 0   -- 限流
end

四、限流实践建议

  • 多维度限流:同时按IP、用户ID、接口维度限流,防止单点突破
  • 限流响应设计:返回429状态码,响应头加入Retry-After、X-RateLimit-Remaining告知客户端
  • 白名单机制:内部服务、监控探针需要跳过限流
  • 限流指标监控:记录被限流的请求量,突增时告警,可能是攻击或BUG
  • 降级优于拒绝:被限流的请求返回缓存数据比直接503用户体验更好

总结:令牌桶是API限流的首选算法,兼顾平滑限流和合理突发。生产环境用Redis Lua脚本实现分布式令牌桶,配合Nginx层的初级限流(limit_req)做双重防护,基本能应对绝大多数流量攻击和误用场景。

发表评论