限流与配额

三种常见限流维度
| 维度 | 全称 | 含义 | 类比 |
|---|---|---|---|
| RPM | Requests Per Minute | 每分钟可发起的请求次数 | 收银台「每分钟接待多少人」 |
| TPM | Tokens Per Minute | 每分钟可处理的 token 总量 | 厨房「每分钟能出多少菜量」 |
| Concurrency | 并行任务数 | 同一时刻正在执行中的任务数量上限 | 餐厅「同时在做的菜不超过 N 份」 |
简单记忆:RPM 关心「叫了几次」,TPM 关心「工作量多大」,Concurrency 关心「同时有几个在跑」。
核心区别
RPM / TPM 是速率类限流:关注一段时间内累计的请求次数或数据量,请求发完就释放,时间窗口一过就重置。
Concurrency 是存量类限流:关注「同一瞬间有多少任务正在占用资源」,只要任务还没结束,就会持续占用名额。
举例(假设配额 Concurrency = 2):
- 10:00:00 提交任务 A(预计耗时 5 分钟)
- 10:00:01 提交任务 B(预计耗时 5 分钟)
- 10:00:02 提交任务 C → 被拒绝或排队,因为 A、B 仍在跑
- 10:05:00 任务 A 完成,名额释放,C 才能开始
为什么生图 / 生视频等异步任务用 Concurrency 限流
文本对话类接口通常秒级返回,一次请求占用算力的时间很短,用 RPM / TPM 足以反映真实负载。但生图、生视频等任务不同:
- 单任务耗时长:一张图几秒到几十秒,一段视频可能几分钟,「每分钟几次」无法反映 GPU 的实际占用时间。
- 资源是 GPU 显存而非 token 带宽:任务一旦开始就独占一块 GPU 资源直到结束——算力是「被占住的」,不是「流过的」。
- RPM 会严重失真:若只限 RPM=60,一分钟内提交 60 个各跑 3 分钟的视频任务,瞬间就有 60 个任务堆在 GPU 上争抢,导致服务过载。
- 异步任务天然适配并发模型:接口提交后立即返回任务 ID,真实执行在后台排队,Concurrency 正好描述「后台 worker 池的容量」。
一句话总结:Chat 类是「流量型」负载,按速率限流合理;生图 / 生视频类是「占用型」负载,按并发限流才合理。
如何在客户端控制 Concurrency
- 维护一个「在途任务计数器」:每次提交 +1,收到最终状态(成功/失败/超时)-1。
- 用信号量或任务队列:例如 Python 的
asyncio.Semaphore(N)、Go 的带缓冲 channel,或 Redis / RabbitMQ 做削峰。 - 收到 429 / 并发超限错误时指数退避:不要硬重试,否则会把后端越压越死。
- 轮询任务状态要有节制:查询本身也算请求,建议间隔 ≥ 3–5 秒,能用 Webhook 回调就不要轮询。
- 提前规划配额:按业务峰值 QPS × 平均任务耗时估算所需并发数。例如高峰每秒 2 个任务、每个平均 30 秒,则至少需要 Concurrency = 60。
常见误区:
- ❌ 「RPM 够大就不会被限」:长耗时任务会先撞到 Concurrency 上限。
- ❌ 「任务提交成功就等于立刻执行」:异步接口的 202 只代表「已接收」,实际开始时间取决于队列和可用名额。
- ❌ 「把客户端改成多线程就能提速」:如果服务端 Concurrency 是 5,开 50 个线程也只有 5 个在跑,其余全在排队。
各字段的实际生效范围
同一用户的限流配额按以下顺序匹配,命中即停止下探:用户专属配置(按模型)→ 用户默认配置 → 系统兜底配置 + 充值档位。
RPM(每分钟请求数)
适用于以下三类接口:
- 对话类(同时受 RPM + TPM 控制):
/v1/chat/completions、/bypass/openai/v1/chat/completions、/bypass/openai/v1/responses、/bypass/anthropic/v1/messages、/bypass/vertex/v1/*、/bypass/vertex/v1beta/*。 - 图片 / 搜索类(同时受 RPM + TPM 控制):
/v1/images/generations、/v1/images/edits、/v1/search/web。 - 视频生成类(仅按「每次提交计 1 次请求」参与 RPM):
/v1/videos、/v1/videos/{id}/remix、/v3/contents/generations/tasks,以及/queue/**下所有异步视频提交类接口(含 Seedance、Vidu、Kling、Veo、Dreamina 等 Fal / 火山格式队列;不含.../requests/{id}状态/结果查询)。
TPM(每分钟 Token 数)
只在真正会预估 Token 消耗的接口上有约束意义:
- 全部对话类接口:消耗估算 ≈ 输入字符数 / 3 +
max_tokens(未传时默认按 4096 预留)。 - 图片生成与编辑接口:消耗估算 ≈ prompt 字符数 / 3 + 张数 × 1000。
预估公式、与 usage.total_tokens 的差异及优化建议详见 TPM 是如何计算的。
⚠️ 视频生成接口和搜索接口虽然也走该限流通道,但每次只计 1 个 Token,对 TPM 没有实际限制效果,请不要把 TPM 当作视频/搜索接口的额度控制手段。
Concurrency(异步任务并发数)
仅对异步视频生成类接口生效,按 (用户, 模型) 维度独立计数:/v1/videos、/v1/videos/{id}/remix、/v3/contents/generations/tasks,以及 /queue/** 下所有异步视频提交类接口(规则同 RPM 段落;不含任务状态/结果查询)。槽位会在任务终态(成功 / 失败 / 取消)时自动释放,并配有兜底超时回收。
不受这三个字段约束的接口
以下接口不会消耗 RPM / TPM / Concurrency 中的任意一项(可能仍受 RPS、API Key 额度、订阅套餐等其它机制约束):文本补全 /v1/completions、MCP / Agent 系列 /v1/mcp/...、/v1/agent/...、批量推理 /v1/batchjob/...、/v2/batchjob/...。
速查表
| 接口分类 | RPM | TPM | Concurrency |
|---|---|---|---|
| 对话(chat / bypass / anthropic-bypass / responses / vertex-bypass) | ✅ | ✅ | — |
| 图片生成 / 编辑 | ✅ | ✅ | — |
| 联网搜索 | ✅ | ⚠️ 名义生效 | — |
| 视频生成(含 Seedance / Veo / 标准 videos) | ✅ | ⚠️ 名义生效 | ✅ |
| 独立 Anthropic / FIM 补全 / 语音 / OCR / MCP / 批量推理 / 部分队列任务 | — | — | — |
✅ = 真实生效;⚠️ = 接口走了限流通道但增量为 1,对该字段实际无约束效果;— = 不受该字段约束。
Gemini 限流
Vertex AI 上的 Gemini 模型资源是全球共享的,并没有披露明确的每分钟请求数上限(RPM,与并发数 Concurrency 是不同概念)。当模型被大量请求、系统资源不足时,会返回 429 错误(Resource exhausted / Model resources busy)。建议在业务侧实现请求重试机制。