触发限流RPM / TPM / 并发 / 重试
429 Too Many Requests:API 限流、并发过高怎么解决
429 表示请求频率、Token 速率、并发数或短时间突发量超过平台限制。正确做法是降并发、排队、指数退避和减少单次 Token。
排查结论
先用最小请求隔离问题:只保留 API Key、Base URL、模型名和一条 ping 消息。最小请求失败,先修配置;最小请求成功,再回到业务代码排查并发、上下文和部署环境。
1. 问题现象
- HTTP 状态码返回 429,错误包含 rate limit、Too Many Requests、requests per minute 或 tokens per minute。
- 单次请求能成功,批量并发、循环任务或多用户同时使用时失败。
- 短时间连续重试后错误更严重,甚至触发更长时间的冷却。
- 流式输出前几秒正常,后续大量任务一起启动时开始报错。
2. 最可能原因
并发请求太多,没有队列或限速器。
单次 prompt 太长,输出 max_tokens 太大,触发 TPM 限制。
失败后立即重试,所有请求在同一时间再次打到 API。
免费额度或低等级套餐的 RPM/TPM 很低,不适合批量任务。
多个服务实例共用同一个 Key,但每个实例都以为自己没有超过限制。
3. 快速排查清单
1
区分 RPM 和 TPM
请求次数限制和 Token 限制是两件事;短文本高并发看 RPM,长文本任务通常先撞 TPM。
2
限制并发
先把并发降到 1 到 3 跑通,再逐步增加,记录失败点。
3
使用指数退避和随机抖动
不要固定每秒重试;等待 1s、2s、4s 并加入随机抖动,避免请求同时恢复。
4
减少 max_tokens 和上下文
摘要、分块、缓存系统提示词,避免每个请求都带完整历史。
5
需要稳定吞吐就升级额度
业务确实需要更高吞吐时,升级套餐或申请更高限额,比多 Key 硬绕更可靠。
4. 对应 API 的特殊情况
OpenAI
错误信息可能区分 rate_limit_exceeded 和 insufficient_quota。前者是限速,后者是余额或额度问题。
Gemini
免费层通常同时有分钟级和日级限制;开发环境要把批量任务拆小。
DeepSeek / Kimi / 通义千问
国内平台常按套餐、账号等级或资源包设置并发和速率,控制台一般能看到限制说明。
Claude
长上下文和大输出更容易撞 Token 速率限制;建议开启流式输出并减少无用上下文。
5. 可复制的修复示例
TypeScript 指数退避重试 429
typescript
async function callWithRetry<T>(fn: () => Promise<T>, maxRetries = 4): Promise<T> {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await fn();
} catch (error: any) {
const status = error?.status ?? error?.response?.status;
if (status !== 429 || attempt === maxRetries) throw error;
const jitterMs = Math.floor(Math.random() * 300);
const waitMs = 1000 * 2 ** attempt + jitterMs;
await new Promise(resolve => setTimeout(resolve, waitMs));
}
}
throw new Error("unreachable");
}只对 429 做退避重试,不要对所有错误盲目重试。
用队列把并发限制到 2
typescript
async function runLimited<T>(tasks: (() => Promise<T>)[], concurrency = 2) {
const results: T[] = [];
let index = 0;
async function worker() {
while (index < tasks.length) {
const current = index++;
results[current] = await tasks[current]();
}
}
await Promise.all(Array.from({ length: concurrency }, worker));
return results;
}批量任务先限并发,再加退避,通常能解决大部分 429。