在国内调用 AI 大模型 API 时,限流是每个工程师必须面对的挑战。无论是 立即注册 的 HolySheep 中转服务,还是直接对接 OpenAI/Anthropic 官方接口,429 Too Many Requests 错误都像家常便饭。本文从实战角度出发,深入对比令牌桶(Token Bucket)和滑动窗口(Sliding Window)两种主流限流算法的实现、性能和适用场景,附带完整的生产级 Python 代码和 benchmark 数据。

为什么 AI API 限流如此重要

我曾在一次为金融客户构建量化分析系统的项目中,遭遇了严重的限流问题。当时团队日均调用量超过 50 万次,峰值 QPS 达到 200+,但上游 API 的限制只有 100 QPS。由于缺乏有效的限流机制,系统频繁触发 429 错误,导致下游量化模型训练中断,最终造成了数小时的业务损失。

那次教训让我深刻认识到:限流不仅是保护上游 API 的手段,更是保障系统稳定性的核心防线。一个设计良好的限流方案,可以让你在充分利用 API 配额的同时,避免因超限导致的请求失败、重试风暴和用户体验下降。

令牌桶算法:允许适度突发

算法原理

令牌桶的核心思想是:系统以固定速率向桶中添加令牌,桶的容量有上限。每个请求需要消耗一个(或多个)令牌,当桶中有足够令牌时请求通过,否则被拒绝或等待。这种设计允许在令牌充足时处理突发流量,同时长期来看请求速率被平滑限制在令牌添加速率。

生产级 Python 实现

import time
import threading
from typing import Optional, Dict
from dataclasses import dataclass, field


@dataclass
class TokenBucketConfig:
    """令牌桶配置"""
    rate: float  # 每秒添加的令牌数
    capacity: int  # 桶的最大容量
    name: str = "default"


class TokenBucket:
    """线程安全的令牌桶限流器
    
    适用场景:
    - 允许适度突发流量的业务
    - 需要平滑限制长期请求速率
    - 批量任务和异步处理场景
    """
    
    def __init__(self, config: TokenBucketConfig):
        self.rate = config.rate
        self.capacity = config.capacity
        self.name = config.name
        self._tokens: float = float(config.capacity)
        self._last_refill_time: float = time.monotonic()
        self._lock = threading.RLock()
        self._total_requests: int = 0
        self._total_allows: int = 0
        self._total_rejects: int = 0
    
    def _refill(self) -> None:
        """补充令牌"""
        now = time.monotonic()
        elapsed = now - self._last_refill_time
        self._tokens = min(
            self.capacity,
            self._tokens + elapsed * self.rate
        )
        self._last_refill_time = now
    
    def allow_request(self, tokens_needed: int = 1) -> bool:
        """检查是否允许请求(不阻塞)"""
        with self._lock:
            self._refill()
            self._total_requests += 1
            
            if self._tokens >= tokens_needed:
                self._tokens -= tokens_needed
                self._total_allows += 1