RPM、TPM 与并发:中转站限流的四种真实形态,以及怎么压出真实上限

避坑指南 发布于 2026-10-19 65 次阅读 API指南针
限流不一定返回 429。静默排队、静默降级、中途断流都不报错,却会让业务在晚高峰莫名变慢。本文给出阶梯压测方法与四类曲线的判读方式。

限流为什么比报错更难查

报错至少会告诉你「出事了」。限流不会 —— 它有四种形态,其中三种不返回错误码。

形态表面现象实际发生的事
一、硬 429请求被拒,429 Too Many Requests网关计数器满了,直接拒绝
二、静默排队请求成功,但延迟从 2 秒涨到 60 秒请求进了队列,等前面的空位
三、静默降级请求成功,返回 200超限后被路由到更便宜的上游或更小的模型
四、中途断流流式响应提前结束,没有 [DONE]连接被主动切断,或上游配额耗尽

第二种最容易被误判成「网络慢」,第三种最容易被误判成「模型变笨」,第四种最容易被误判成「客户端 bug」。

这三种误判的共同后果是:你去优化了错误的地方。

三个独立的维度

限流不是「一个上限」,而是三个独立的上限,任一触发即生效:

  • RPM(requests per minute):每分钟请求数,对短请求敏感;
  • TPM(tokens per minute):每分钟 token 数,对长上下文敏感 —— 一个 100K 上下文的请求,可能一次就打满整分钟的配额;
  • 并发连接数:同时在飞的请求数。它和 RPM 完全独立:你可以 RPM 很低但并发很高,只要每个请求都很慢。

这就是「我明明没超 RPM 却被限」经常发生的原因 —— 你超的是 TPM 或并发,而中转站的后台只显示 RPM。

阶梯压测:唯一能测出真实上限的方法

不要一上来就压 50 并发。你会同时触发所有限制,拿到一条无法解释的曲线。

正确的做法是阶梯加压,每档跑 60 秒,记录四个数字:成功率、P50 延迟、P95 延迟、首字延迟。

import time, statistics, concurrent.futures as cf, urllib.request, json

BASE, KEY, MODEL = "https://<base_url>", "<key>", "<model>"

def one():
    body = json.dumps({"model": MODEL, "max_tokens": 64,
        "messages": [{"role": "user", "content": "Reply with the single word: ok"}]}).encode()
    req = urllib.request.Request(BASE + "/v1/chat/completions", data=body,
        headers={"Authorization": "Bearer " + KEY, "Content-Type": "application/json"})
    t0 = time.time()
    try:
        with urllib.request.urlopen(req, timeout=120) as r:
            r.read()
            return (time.time() - t0, r.status)
    except Exception as e:
        return (time.time() - t0, getattr(e, "code", 0))

for conc in (1, 2, 4, 8, 16):
    lat, codes = [], []
    with cf.ThreadPoolExecutor(max_workers=conc) as ex:
        deadline, futs = time.time() + 60, []
        while time.time() < deadline:
            futs.append(ex.submit(one))
            time.sleep(0.5)          # 固定到达速率,避免「突发」污染结果
        for f in cf.as_completed(futs):
            d, c = f.result(); lat.append(d); codes.append(c)
    ok = sum(1 for c in codes if c == 200) / len(codes)
    print(conc, "ok=%.0f%%" % (ok * 100), "p50=%.1fs" % statistics.median(lat),
          "p95=%.1fs" % sorted(lat)[max(0, int(len(lat) * 0.95) - 1)],
          "429=", codes.count(429))

要点有三条:

  • 固定到达速率(上面是每 0.5 秒一个),否则你测的是「突发抗性」,不是「持续上限」;
  • 每档足够长(60 秒),短于 30 秒的测试会被瞬时配额掩盖;
  • 先测单并发基线。基线自己就不稳(P95 抖动超过 2 倍),后面的数据全部无效。

四类曲线的判读

曲线形状判读下一步
P50 平稳,无 429,P95 无拐点还没到上限继续加压,直到出现拐点
P50 随并发线性上升,无 429静默排队你已经在限流里了,只是它不报错
某个并发点后 429 突增硬限流点这就是真实 RPM 上限
P50 平稳但成功率下降(空响应、断流)中途断流查上游配额或网关连接数上限

第三行是唯一「友好」的情况,因为你看得见。前两行才是绝大多数人踩的坑。

怎么问清分级规则

中转站的限流几乎都是按用户等级给的:充值档位、是否实名、是否企业客户,直接决定 RPM 和并发。

要问的四个问题,建议按这个顺序:

  1. 限流是按 RPM、TPM 还是并发算?三个都给数值最好。
  2. 超限时返回什么?429 还是排队? 如果对方说「我们不会限流」,基本等于「超了会排队」。
  3. 限流是每 Key 还是每账号?多 Key 能不能叠加?
  4. 晚高峰(20:00~23:00)的配额与白天是否一致?

第 4 条尤其关键。很多站白天一切正常,晚上 429 频发 —— 不是你的量涨了,是共享池被更多人分了。

一个反直觉的结论

限流阈值低不是问题,「阈值不透明加上超限静默」才是问题。

一家明说「你的档位是 60 RPM,超了返回 429」的站,你可以写重试和队列;一家说「我们不限流」但超限后把请求排队 40 秒的站,你连监控该看什么指标都不知道 —— 因为延迟上涨可以有无穷多种解释。

所以在选型清单里,「能否明确说出限流口径」应该单独占一项,和可用率同级。

小结

  1. 限流有四种形态,只有硬 429 会告诉你出事了;
  2. RPM、TPM、并发是三个独立维度,超的往往不是你以为的那个;
  3. 阶梯加压(1、2、4、8、16,每档 60 秒,固定到达速率)才能测出真实上限;
  4. 先测单并发基线,基线不稳则全部数据无效;
  5. P50 线性上升而没有 429,就是静默排队,这已经是限流状态;
  6. 选型时把「限流口径是否透明」当独立指标,比阈值高低更重要。

这篇文章对你有帮助吗?如果你在实际使用中发现了文中没提到的情况,欢迎 提交纠错或补充,我们会核验后更新到正文里。

相关文章

模型下线不是意外:把 deprecation 变成计划内的事

2026-10-19

只看排行榜选站会踩的三个坑

2026-10-07

晚高峰抖动:是上游、线路还是你的并发

2026-10-07

「官方直连」「纯官转」宣传的四种核实方法

2026-10-06

API Key 泄露后的 30 分钟应急清单

2026-10-05

充值前的五个条款:余额、退款与期限,逐条怎么问

2026-10-04

顺着看下去:本站收录的部分中转站

本文没有指向特定模型系列,下面是本站收录站点中综合评分靠前的几家,供你顺着看下去。排序依据是评分与可用率,不构成推荐。

完整清单见 中转站导航,或按 模型系列 浏览。