【Bug已解决】openclaw: "rate limit exceeded" / 429 Too Many Requests — OpenClaw 请求频率限制解决方案

1. 问题描述

在使用 OpenClaw 频繁调用外部 API 或 AI 模型时,系统报出请求频率限制或 429 错误:

# API 频率限制
$ openclaw "批量调用AI API"
Error: rate limit exceeded
HTTP 429: Too Many Requests
Rate limit: 60 requests/minute
Retry after: 45s
Current window: 60/60 requests used

# 并发请求过多
$ openclaw --parallel 20 "并行处理"
Error: 429 Too Many Requests
Concurrent request limit: 10
Active requests: 20
Exceeded by: 10

# Token 速率限制
$ openclaw "分析大型文档"
Error: token rate limit exceeded
Token limit: 150000 TPM (tokens per minute)
Used: 150000 TPM
Retry after: 30s

# 日配额耗尽
$ openclaw "继续执行任务"
Error: daily quota exceeded
Daily limit: 1000000 requests
Used: 1000000 requests
Resets at: 2024-07-08T00:00:00Z

这个问题在以下场景中特别常见:

  • 批量处理大量文件或任务
  • 高并发并行调用 API
  • 脚本循环调用未加限流
  • 多个进程共享同一 API Key
  • 免费计划配额较低
  • 突发流量超过限制

2. 原因分析

OpenClaw发起请求
    ↓
API网关检查频率 ←──── 滑动窗口/令牌桶
    ↓
超过限制 ←──── RPM/TPM/并发数
    ↓
返回429 ←──── "Too Many Requests"
    ↓
需要等待/重试
原因分类 具体表现 占比
RPM超限 每分钟请求过多 约 35%
并发超限 同时请求过多 约 25%
TPM超限 每分钟Token过多 约 20%
日配额耗尽 每日上限 约 10%
多Key冲突 共享Key 约 5%
突发流量 瞬间高并发 约 5%

深层原理

API 限流通常使用三种算法:固定窗口计数器(在固定时间窗口内计数请求)、滑动窗口(在滚动时间窗口内计数,更精确)、令牌桶(以固定速率生成令牌,请求消耗令牌)。429 状态码是 HTTP 标准的"Too Many Requests"响应,通常附带 Retry-After 头告知客户端等待时间。API 供应商通常设置多个维度的限制:RPM(Requests Per Minute,每分钟请求数)、TPM(Tokens Per Minute,每分钟 Token 数)、并发请求数(同时进行的请求)、日配额(每日总请求量)。当任何一个维度超限时,API 返回 429 错误。

3. 解决方案

方案一:实现请求限流器(最推荐)

# 创建令牌桶限流器
import time
import threading
from collections import deque
from functools import wraps

class TokenBucketRateLimiter:
    """令牌桶限流器"""
    
    def __init__(self, rate=60, capacity=60):
        """
        rate: 每秒生成的令牌数(60/分钟 = 1/秒)
        capacity: 桶容量(允许突发)
        """
        self.rate = rate / 60  # 转换为每秒
        self.capacity = capacity
        self.tokens = capacity
        self.last_refill = time.time()
        self.lock = threading.Lock()
    
    def acquire(self, timeout=None):
        """获取一个令牌"""
        start_time = time.time()
        
        while True:
            with self.lock:
                # 补充令牌
                now = time.time()
                elapsed = now - self.last_refill
                self.tokens = min(self.capacity, self.tokens + elapsed * self.rate)
                self.last_refill = now
                
                if self.tokens >= 1:
                    self.tokens -= 1
                    return True
                
                # 计算需要等待的时间
                wait_time = (1 - self.tokens) / self.rate
            
            # 检查超时
            if timeout and (time.time() - start_time + wait_time) > timeout:
                return False
            
            time.sleep(min(wait_time, 0.1))

class SlidingWindowRateLimiter:
    """滑动窗口限流器"""
    
    def __init__(self, max_requests=60, window_seconds=60):
        self.max_requests = max_requests
        self.window = window_seconds
        self.requests = deque()
        self.lock = threading.Lock()
    
    def acquire(self, timeout=None):
        """尝试发送请求"""
        start_time = time.time()
        
        while True:
            with self.lock:
                now = time.time()
                
                # 移除过期请求
                while self.requests and self.requests[0] < now - self.window:
                    self.requests.popleft()
                
                if len(self.requests) < self.max_requests:
                    self.requests.append(now)
                    return True
                
                # 计算需要等待的时间
                wait_time = self.requests[0] + self.window - now
            
            if timeout and (time.time() - start_time + wait_time) > timeout:
                return False
            
            time.sleep(min(wait_time, 0.1))

# 并发限制器
class ConcurrencyLimiter:
    """并发请求限制器"""
    
    def __init__(self, max_concurrent=10):
        self.max_concurrent = max_concurrent
        self.current = 0
        self.lock = threading.Lock()
        self.condition = threading.Condition(self.lock)
    
    def acquire(self, timeout=None):
        with self.condition:
            start_time = time.time()
            while self.current >= self.max_concurrent:
                remaining = timeout - (time.time() - start_time) if timeout else None
                if remaining is not None and remaining <= 0:
                    return False
                self.condition.wait(timeout=remaining)
            self.current += 1
            return True
    
    def release(self):
        with self.condition:
            self.current -= 1
            self.condition.notify()

# 组合限流器
class RateController:
    """组合限流管理器"""
    
    def __init__(self, rpm=60, tpm=150000, max_concurrent=10):
        self.rpm_limiter = SlidingWindowRateLimiter(rpm, 60)
        self.tpm_limiter = TokenBucketRateLimiter(tpm / 1000, tpm / 1000)
        self.concurrent_limiter = ConcurrencyLimiter(max_concurrent)
    
    def acquire(self, estimated_tokens=1000, timeout=60):
        """获取请求许可"""
        # 获取并发许可
        if not self.concurrent_limiter.acquire(timeout):
            return False, "并发限制超时"
        
        # 获取 RPM 许可
        if not self.rpm_limiter.acquire(timeout):
            self.concurrent_limiter.release()
            return False, "RPM限制超时"
        
        # 获取 TPM 许可
        tokens_needed = estimated_tokens / 1000
        for _ in range(int(tokens_needed)):
            if not self.tpm_limiter.acquire(timeout):
                self.concurrent_limiter.release()
                return False, "TPM限制超时"
        
        return True, "OK"
    
    def release(self):
        """释放并发许可"""
        self.concurrent_limiter.release()

if __name__ == "__main__":
    manager = RateController(rpm=60, max_concurrent=10)
    
    for i in range(20):
        success, msg = manager.acquire(estimated_tokens=500)
        if success:
            print(f"  请求 {i+1}: ✅ {msg}")
            # 模拟请求
            time.sleep(0.1)
            manager.release()
        else:
            print(f"  请求 {i+1}: ❌ {msg}")
            time.sleep(1)

方案二:配置自动重试和退避

# 配置 OpenClaw 的重试策略
python3 -c "
import json
with open('.openclaw/config.json', 'r') as f:
    config = json.load(f)

config['rateLimit'] = {
    'retryOn429': True,             # 429时自动重试
    'maxRetries': 5,                # 最大重试5次
    'retryStrategy': 'exponential', # 指数退避
    'baseDelay': 1000,              # 基础延迟1秒
    'maxDelay': 60000,              # 最大延迟60秒
    'jitter': True,                 # 添加随机抖动
    'jitterRange': 0.3,             # 30%抖动范围
    'respectRetryAfter': True,      # 遵守Retry-After头
    'failOnMaxRetries': True,       # 超过重试次数则失败
    'logRetries': True,             # 记录重试日志
    'circuitBreaker': {
        'enabled': True,            # 断路器
        'threshold': 10,            # 10次429触发
        'resetTime': 60000,         # 60秒后重试
        'halfOpenRequests': 3       # 半开状态允许3个请求
    }
}

with open('.openclaw/config.json', 'w') as f:
    json.dump(config, f, indent=2)
print('重试策略: 指数退避+抖动+断路器+遵守Retry-After')
"

# 指数退避重试实现
cat > .openclaw/retry_handler.js << 'JEOF'
// 429 重试处理器
class RetryHandler {
    constructor(options = {}) {
        this.maxRetries = options.maxRetries || 5;
        this.baseDelay = options.baseDelay || 1000;
        this.maxDelay = options.maxDelay || 60000;
        this.jitter = options.jitter !== false;
    }
    
    async execute(requestFn) {
        let lastError;
        
        for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
            try {
                const result = await requestFn();
                return result;
            } catch (error) {
                if (error.response?.status !== 429) {
                    throw error; // 非429错误直接抛出
                }
                
                lastError = error;
                
                if (attempt === this.maxRetries) {
                    throw new Error(`达到最大重试次数: ${this.maxRetries}`);
                }
                
                // 计算429后的等待时间
                const retryAfter = error.response.headers['retry-after'];
                let delay;
                
                if (retryAfter) {
                    // 遵守 Retry-After 头
                    delay = parseInt(retryAfter) * 1000;
                } else {
                    // 指数退避
                    delay = Math.min(
                        this.baseDelay * Math.pow(2, attempt),
                        this.maxDelay
                    );
                    
                    // 添加抖动
                    if (this.jitter) {
                        delay = delay * (1 + Math.random() * 0.3 - 0.15);
                    }
                }
                
                console.warn(
                    `429 限流,${delay.toFixed(0)}ms 后重试 ` +
                    `(尝试 ${attempt + 1}/${this.maxRetries})`
                );
                
                await new Promise(resolve => setTimeout(resolve, delay));
            }
        }
        
        throw lastError;
    }
}

module.exports = RetryHandler;
JEOF

方案三:配置请求队列和批处理

# 配置请求队列
python3 -c "
import json
with open('.openclaw/config.json', 'r') as f:
    config = json.load(f)

config['requestQueue'] = {
    'enabled': True,
    'maxQueueSize': 1000,           # 最大队列1000
    'maxWaitTime': 300000,          # 最大等待5分钟
    'priority': {
        'levels': 3,                # 3个优先级
        'preempt': False            # 不抢占
    },
    'batching': {
        'enabled': True,            # 启用批处理
        'maxBatchSize': 20,         # 每批最多20个
        'batchTimeout': 1000,       # 1秒超时
        'maxBatchTokens': 50000     # 每批最大Token
    },
    'scheduling': {
        'strategy': 'fifo',         # 先进先出
        'fairShare': True,          # 公平共享
        'perUserLimit': 10          # 每用户最多10个/分钟
    }
}

with open('.openclaw/config.json', 'w') as f:
    json.dump(config, f, indent=2)
print('请求队列: 最大1000+批处理20+公平共享')
"

# 使用批处理减少请求数
openclaw --batch "批量处理文件" --batch-size 20 --batch-delay 1000

# 查看队列状态
openclaw --queue-status
# 输出:
# 队列长度: 15
# 处理中: 8
# 已完成: 142
# 平均等待: 2.3s
# 预计完成: 45s

方案四:多 API Key 轮换

# 创建 API Key 轮换管理器
import time
import threading
from collections import deque

class APIKeyRotationManager:
    """API Key 轮换管理器"""
    
    def __init__(self, keys):
        self.keys = keys
        self.key_status = {}
        self.lock = threading.Lock()
        
        for key in keys:
            self.key_status[key] = {
                'requests': 0,
                'last_used': 0,
                'rate_limited_until': 0,
                'daily_count': 0,
                'daily_reset': time.time() + 86400
            }
        
        self.key_queue = deque(keys)
    
    def get_available_key(self):
        """获取可用的 API Key"""
        with self.lock:
            now = time.time()
            
            # 重置每日计数
            for key in self.keys:
                status = self.key_status[key]
                if now > status['daily_reset']:
                    status['daily_count'] = 0
                    status['daily_reset'] = now + 86400
            
            # 尝试找到可用的 Key
            tried = 0
            while tried < len(self.keys):
                key = self.key_queue[0]
                self.key_queue.rotate(-1)  # 轮换
                tried += 1
                
                status = self.key_status[key]
                
                # 检查是否被限流
                if now < status['rate_limited_until']:
                    continue
                
                # 检查每日配额
                if status['daily_count'] >= 1000000:  # 假设每日100万
                    continue
                
                # 使用此 Key
                status['requests'] += 1
                status['daily_count'] += 1
                status['last_used'] = now
                return key
            
            # 所有 Key 都不可用
            # 计算最短等待时间
            min_wait = min(
                self.key_status[k]['rate_limited_until']
                for k in self.keys
            )
            wait_seconds = max(0, min_wait - now)
            return None, wait_seconds
    
    def mark_rate_limited(self, key, retry_after=60):
        """标记 Key 被限流"""
        with self.lock:
            self.key_status[key]['rate_limited_until'] = time.time() + retry_after
            print(f"  ⚠️ Key {key[:8]}... 被限流,{retry_after}s 后恢复")
    
    def get_stats(self):
        """获取统计"""
        with self.lock:
            now = time.time()
            stats = {}
            for key in self.keys:
                s = self.key_status[key]
                stats[key[:8] + '...'] = {
                    'total_requests': s['requests'],
                    'daily_count': s['daily_count'],
                    'rate_limited': now < s['rate_limited_until'],
                    'last_used': time.ctime(s['last_used']) if s['last_used'] else 'never'
                }
            return stats

# 使用示例
if __name__ == "__main__":
    import os
    
    # 从环境变量加载多个 Key
    keys = [
        os.environ.get('OPENAI_API_KEY_1', 'key1'),
        os.environ.get('OPENAI_API_KEY_2', 'key2'),
        os.environ.get('OPENAI_API_KEY_3', 'key3'),
    ]
    
    manager = APIKeyRotationManager(keys)
    
    # 模拟请求
    for i in range(20):
        result = manager.get_available_key()
        if isinstance(result, tuple):
            key, wait = result
            if key is None:
                print(f"  请求 {i+1}: ❌ 所有Key不可用,等待 {wait:.0f}s")
                time.sleep(min(wait, 5))
                continue
        else:
            key = result
        
        print(f"  请求 {i+1}: 使用 Key {key[:8]}...")
        
        # 模拟429
        if i == 10:
            manager.mark_rate_limited(key, 60)
    
    print("\n统计:")
    for k, v in manager.get_stats().items():
        print(f"  {k}: {v}")

方案五:自适应限流

# 创建自适应限流器
import time
import threading

class AdaptiveRateLimiter:
    """自适应限流器 - 根据API响应动态调整"""
    
    def __init__(self, initial_rpm=60):
        self.current_rpm = initial_rpm
        self.max_rpm = 200
        self.min_rpm = 10
        self.success_streak = 0
        self.error_streak = 0
        self.lock = threading.Lock()
        self.last_adjust = time.time()
        
        # 请求历史
        self.recent_requests = []
        self.window_size = 60  # 60秒窗口
    
    def can_send(self):
        """检查是否可以发送请求"""
        with self.lock:
            now = time.time()
            
            # 清理过期记录
            self.recent_requests = [
                t for t in self.recent_requests 
                if t > now - self.window_size
            ]
            
            # 检查当前窗口内的请求数
            if len(self.recent_requests) >= self.current_rpm:
                return False
            
            self.recent_requests.append(now)
            return True
    
    def on_success(self):
        """请求成功时调用"""
        with self.lock:
            self.success_streak += 1
            self.error_streak = 0
            
            # 连续成功后逐步提高限制
            if self.success_streak >= 10:
                self._increase_limit()
                self.success_streak = 0
    
    def on_rate_limit(self, retry_after=None):
        """遇到429时调用"""
        with self.lock:
            self.error_streak += 1
            self.success_streak = 0
            
            # 降低限制
            self._decrease_limit(retry_after)
    
    def _increase_limit(self):
        """提高限制"""
        old = self.current_rpm
        self.current_rpm = min(self.max_rpm, int(self.current_rpm * 1.2))
        if self.current_rpm != old:
            print(f"  📈 限流提高: {old} -> {self.current_rpm} RPM")
    
    def _decrease_limit(self, retry_after=None):
        """降低限制"""
        old = self.current_rpm
        factor = 0.5 if self.error_streak >= 3 else 0.8
        self.current_rpm = max(self.min_rpm, int(self.current_rpm * factor))
        print(f"  📉 限流降低: {old} -> {self.current_rpm} RPM")
        
        if retry_after:
            print(f"     Retry-After: {retry_after}s")
    
    def get_current_limit(self):
        """获取当前限制"""
        with self.lock:
            return self.current_rpm

# 使用示例
if __name__ == "__main__":
    limiter = AdaptiveRateLimiter(initial_rpm=60)
    
    for i in range(100):
        if limiter.can_send():
            # 模拟请求
            if i % 15 == 0 and i > 0:
                # 模拟429
                limiter.on_rate_limit(retry_after=30)
                print(f"  请求 {i}: ❌ 429")
            else:
                limiter.on_success()
                print(f"  请求 {i}: ✅ (限制: {limiter.get_current_limit()} RPM)")
        else:
            print(f"  请求 {i}: ⏳ 等待")
            time.sleep(1)

方案六:监控和告警

# 配置限流监控
python3 -c "
import json
with open('.openclaw/config.json', 'r') as f:
    config = json.load(f)

config['rateLimit']['monitoring'] = {
    'enabled': True,
    'trackByEndpoint': True,         # 按端点跟踪
    'trackByKey': True,              # 按Key跟踪
    'logFile': '.openclaw/logs/rate_limit.json',
    'alertThreshold': 0.8,          # 80%使用率告警
    'criticalThreshold': 0.95,      # 95%严重告警
    'metrics': {
        'totalRequests': True,
        'rateLimitedCount': True,
        'retryCount': True,
        'averageLatency': True,
        'circuitBreakerTrips': True
    },
    'dailyReport': True,
    'reportFile': '.openclaw/logs/rate_daily.json'
}

with open('.openclaw/config.json', 'w') as f:
    json.dump(config, f, indent=2)
print('限流监控: 按端点/Key跟踪, 80%告警, 每日报告')
"

# 查看限流统计
openclaw --rate-limit-stats
# 输出:
# === 限流统计 ===
# 总请求: 5000
# 被限流: 150 (3%)
# 平均重试: 1.2次
# 断路器触发: 2次
# 
# 按端点:
#   /api/chat: 3000请求, 100限流 (3.3%)
#   /api/analyze: 1500请求, 40限流 (2.7%)
#   /api/search: 500请求, 10限流 (2%)
# 
# 当前限制: 55 RPM (已降低)
# 建议限制: 60 RPM

# 设置告警通知
python3 -c "
import json
with open('.openclaw/config.json', 'r') as f:
    config = json.load(f)
config['rateLimit']['alerts'] = {
    'webhook': os.getenv('ALERT_WEBHOOK', ''),
    'email': os.getenv('ALERT_EMAIL', ''),
    'onRateLimited': True,
    'onCircuitBreaker': True,
    'onQuotaExceeded': True,
    'cooldownMinutes': 30  # 30分钟冷却
}
import os
with open('.openclaw/config.json', 'w') as f:
    json.dump(config, f, indent=2)
print('告警通知已配置')
"

4. 各方案对比总结

方案 适用场景 推荐指数
方案一:限流器 通用防护 ⭐⭐⭐⭐⭐
方案二:自动重试 429恢复 ⭐⭐⭐⭐⭐
方案三:队列批处理 批量任务 ⭐⭐⭐⭐⭐
方案四:Key轮换 多Key环境 ⭐⭐⭐⭐
方案五:自适应 优化吞吐 ⭐⭐⭐⭐
方案六:监控告警 运维 ⭐⭐⭐⭐

5. 常见问题 FAQ

5.1 Windows 上定时器精度问题

Windows 定时器精度较低可能影响限流:

# Windows 默认定时器精度约15ms
# 提高精度
python3 -c "
import ctypes
# 设置高精度定时器
winmm = ctypes.windll.winmm
winmm.timeBeginPeriod(1)  # 1ms精度
print('定时器精度: 1ms')
"

# 配置限流器使用更高精度
python3 -c "
import json
with open('.openclaw/config.json', 'r') as f:
    config = json.load(f)
config['rateLimit']['timerPrecision'] = 'high'  # high | normal
config['rateLimit']['minInterval'] = 50  # 最小间隔50ms
with open('.openclaw/config.json', 'w') as f:
    json.dump(config, f, indent=2)
"

5.2 Docker 中限流不共享

多个容器各自限流导致超限:

# 使用 Redis 共享限流状态
python3 -c "
import json
with open('.openclaw/config.json', 'r') as f:
    config = json.load(f)
config['rateLimit']['sharedState'] = {
    'backend': 'redis',
    'url': 'redis://redis:6379',
    'keyPrefix': 'openclaw:ratelimit:',
    'syncInterval': 1000  # 1秒同步
}
with open('.openclaw/config.json', 'w') as f:
    json.dump(config, f, indent=2)
print('Redis 共享限流已配置')
"

# Docker Compose
services:
  openclaw:
    environment:
      - REDIS_URL=redis://redis:6379
      - RATE_LIMIT_SHARED=true
    depends_on:
      - redis
  redis:
    image: redis:alpine

5.3 CI/CD 中限流问题

CI 中突发请求可能触发限流:

# CI 中配置保守的限流
env:
  OPENCLAW_RPM: 30  # 保守30 RPM
  OPENCLAW_MAX_CONCURRENT: 5
steps:
  - name: Run with rate limiting
    run: |
      openclaw --rpm 30 --max-concurrent 5 "任务"
  
  - name: Handle 429
    run: |
      # 429时等待重试
      openclaw --retry-on-429 --max-retries 3 --backoff exponential "任务"

5.4 不同API限制差异

不同供应商限制不同:

# 配置多供应商限流
python3 -c "
import json
with open('.openclaw/config.json', 'r') as f:
    config = json.load(f)
config['rateLimit']['providers'] = {
    'openai': {
        'rpm': 60,
        'tpm': 150000,
        'maxConcurrent': 10,
        'dailyQuota': 1000000
    },
    'anthropic': {
        'rpm': 50,
        'tpm': 100000,
        'maxConcurrent': 5,
        'dailyQuota': 500000
    },
    'local': {
        'rpm': 1000,
        'tpm': 999999999,
        'maxConcurrent': 50,
        'dailyQuota': 999999999
    }
}
config['rateLimit']['autoDetect'] = True  # 自动检测供应商限制
with open('.openclaw/config.json', 'w') as f:
    json.dump(config, f, indent=2)
print('多供应商限流: OpenAI 60RPM, Anthropic 50RPM')
"

5.5 免费计划配额过低

免费API配额很快耗尽:

# 配置配额管理
python3 -c "
import json
with open('.openclaw/config.json', 'r') as f:
    config = json.load(f)
config['quotaManagement'] = {
    'dailyQuota': 1000,           # 每日1000请求
    'monthlyQuota': 30000,        # 每月3万
    'warnAt': 0.8,               # 80%警告
    'blockAt': 0.95,             # 95%阻止
    'reserveForCritical': 100,   # 保留100给关键任务
    'priorityAccess': {
        'critical': True,        # 关键任务优先
        'normal': True,
        'low': False             # 低优先级在配额紧张时阻止
    }
}
with open('.openclaw/config.json', 'w') as f:
    json.dump(config, f, indent=2)
print('配额管理: 每日1000+月3万+保留100给关键')
"

# 使用优先级
openclaw --priority critical "紧急任务"
openclaw --priority normal "普通任务"
openclaw --priority low "低优先级任务"

5.6 限流后任务积压

限流导致任务排队堆积:

# 配置任务积压处理
python3 -c "
import json
with open('.openclaw/config.json', 'r') as f:
    config = json.load(f)
config['rateLimit']['backlog'] = {
    'maxBacklog': 500,            # 最大积压500
    'dropPolicy': 'oldest',       # 满时丢弃最旧的
    'persistBacklog': True,       # 持久化积压
    'persistFile': '.openclaw/backlog.json',
    'processOnRecover': True,     # 恢复后处理
    'maxAge': 3600000,            # 最长1小时
    'compressBacklog': True       # 压缩积压
}
with open('.openclaw/config.json', 'w') as f:
    json.dump(config, f, indent=2)
print('积压处理: 最大500+持久化+1小时过期')
"

# 查看积压
openclaw --backlog-status
openclaw --backlog-process  # 手动处理积压
openclaw --backlog-clear    # 清除积压

5.7 WebSocket 长连接限流

WebSocket 连接的限流方式不同:

# WebSocket 限流配置
python3 -c "
import json
with open('.openclaw/config.json', 'r') as f:
    config = json.load(f)
config['rateLimit']['websocket'] = {
    'messagesPerMinute': 100,    # 每分钟100条消息
    'bytesPerMinute': 1048576,   # 每分钟1MB
    'maxConnections': 10,        # 最大10连接
    'idleTimeout': 300000,       # 5分钟空闲超时
    'pingInterval': 30000        # 30秒ping
}
with open('.openclaw/config.json', 'w') as f:
    json.dump(config, f, indent=2)
print('WebSocket限流: 100msg/min+1MB/min+10连接')
"

5.8 限流恢复后突发请求

恢复后一次性发送大量请求再次触发限流:

# 渐进恢复策略
class ProgressiveRecovery:
    """渐进恢复策略"""
    
    def __init__(self, target_rpm=60):
        self.target_rpm = target_rpm
        self.current_rpm = 0
        self.recovery_step = target_rpm // 10  # 每次增加10%
        self.recovery_interval = 5  # 每5秒增加
        self.last_recovery = 0
    
    def can_send(self):
        """检查是否可以发送"""
        now = time.time()
        
        # 渐进恢复
        if self.current_rpm < self.target_rpm:
            if now - self.last_recovery >= self.recovery_interval:
                self.current_rpm = min(
                    self.target_rpm,
                    self.current_rpm + self.recovery_step
                )
                self.last_recovery = now
                print(f"  📈 恢复中: {self.current_rpm}/{self.target_rpm} RPM")
        
        return self.current_rpm > 0
    
    def on_rate_limit(self):
        """再次限流时重置"""
        self.current_rpm = self.current_rpm // 2  # 减半
        print(f"  📉 再次限流,降至: {self.current_rpm} RPM")

排查清单速查表

□ 1. 检查 API 限制文档: RPM/TPM/并发/日配额
□ 2. 实现限流器: 令牌桶/滑动窗口
□ 3. 配置自动重试: 指数退避+抖动
□ 4. 遵守 Retry-After 头
□ 5. 使用请求队列和批处理
□ 6. 多 Key 轮换分散负载
□ 7. 配置断路器防止雪崩
□ 8. 部署自适应限流优化吞吐
□ 9. 监控限流统计和告警
□ 10. 配额管理: 保留给关键任务

6. 总结

  1. 最常见原因:每分钟请求数(RPM)超限(35%)和并发请求数超限(25%)
  2. 限流器:实现令牌桶+滑动窗口+并发限制三重限流,确保请求速率在限制内
  3. 自动重试:配置指数退避重试(1s→2s→4s→8s→16s),添加 30% 随机抖动避免雷同效应
  4. 请求队列:使用 FIFO 队列+批处理(每批20个)减少请求次数,支持优先级调度
  5. 最佳实践建议:部署自适应限流器根据 API 响应动态调整速率,多 API Key 轮换分散负载,配置断路器防止限流雪崩,监控使用率并在 80% 时告警

故障排查流程图

flowchart TD
    A[429限流错误] --> B[检查限制类型]
    B --> C[RPM/TPM/并发/日配额]
    C --> D{RPM超限?}
    D -->|是| E[实现限流器]
    D -->|否| F{并发超限?}
    E --> G[滑动窗口60RPM]
    G --> H[配置自动重试]
    F -->|是| I[并发限制器]
    F -->|否| J{TPM超限?}
    I --> H
    J -->|是| K[令牌桶TPM]
    J -->|否| L{日配额耗尽?}
    K --> H
    L -->|是| M[多Key轮换]
    L -->|否| N[检查突发流量]
    M --> H
    N --> O[渐进恢复]
    O --> H
    H --> P[指数退避+抖动]
    P --> Q[遵守Retry-After]
    Q --> R[openclaw测试]
    R --> S{成功?}
    S -->|是| T[✅ 问题解决]
    S -->|否| U[配置请求队列]
    U --> V[批处理20个/批]
    V --> W[自适应限流]
    W --> X[部署监控告警]
    X --> T
Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐