多模型 API 网关压测:并发、延迟与计费的三角平衡

去年底我们系统只接了一个模型,没什么感知。今年陆续加了三个之后问题来了——同一个 Prompt,DeepSeek 返回 800ms,Kimi 那边能拖到 2 秒,各自的 Token 计费方式还不一样,月底看账单的时候心态直接崩了。后来在器灵模型广场上把四个模型统一接入,第一件事就是拉出来做一次完整压测——不搞清楚每个模型在高并发下到底什么表现,后面出问题就是线上的事故。

“QPS 够了就行”——这句话在单模型场景下勉强成立,在多模型 API 网关里是危险的简化。同样 100 QPS 的流量,打到 DeepSeek 和打到 Qwen 上,P99 延迟可以差 3 倍。同样的 Prompt,在 Kimi 上消耗的 Token 可能是 GLM 的 1.5 倍。网关做得好不好,不能只看能不能通——要看它能不能在高并发下,让延迟、吞吐和 Token 消耗三者同时可控。

这篇拆一次在器灵模型广场上的完整压测过程,从方法到数据,再到优化策略。


压测目标与环境

被测网关:一个聚合了 DeepSeek V4、Qwen3.6 Max、Kimi K2.7、GLM-5.2 四个模型的统一 API 网关。外部调用者使用同一套 Base URL 和 API Key,由网关根据 model 参数路由到对应的下游模型。压测跑在器灵模型广场的统一 API 网关上,调的就是平台聚合的这四个模型。

压测环境

参数
压测工具 wrk2 + 自定义 Lua 脚本(长连接)
并发模型 固定 RPS(Rate),逐步爬坡
请求 Prompt 固定 200 Token 的翻译任务
输出限制 max_tokens=150
网络 同机房内网,RTT < 1ms
时长 每轮 300s,预热 30s

指标采集:延迟(P50 / P95 / P99)、错误率、各模型实际 Token 消耗、连接池排队时长。数据来源是网关内置的 Prometheus metrics。


单模型延迟基准:先搞清楚每个模型有多快

压测的第一步不是测网关。先测每个下游模型的裸延迟——绕过网关,直接调各模型的官方 API,拿到基准线。后面网关多出来的每一毫秒延迟,都必须在基准线上能解释。

测试条件:单请求并发(1 RPS),Prompt 固定,max_tokens=150,连续跑 200 次取分布。

模型 P50 P95 P99 平均首 Token 延迟
DeepSeek V4 1182ms ± 286ms 2734ms ± 512ms 3618ms ± 742ms 423ms ± 87ms
Qwen3.6 Max 1537ms ± 341ms 3182ms ± 604ms 4092ms ± 891ms 512ms ± 103ms
Kimi K2.7 2074ms ± 518ms 4516ms ± 903ms 5783ms ± 1245ms 689ms ± 156ms
GLM-5.2 1831ms ± 429ms 3922ms ± 751ms 5108ms ± 1067ms 574ms ± 132ms

三个观察:

  1. DeepSeek 在短 Prompt 下最稳定,P50→P99 的劣化幅度最小(1182ms→3618ms,约 3 倍),标准差也控制得最好,适合对延迟敏感的实时场景。
  2. Kimi 的长尾最严重,P99 达到 5783ms,标准差 1245ms 远超其他模型,说明偶发性的服务端排队或 GC 延迟波动很大。
  3. 首 Token 延迟 Qwen 和 Kimi 偏高,这意味着如果做打字机式流式渲染,用户体感会比 DeepSeek 慢半拍。

这条基准线是后续所有优化的参照系。 网关增加的任何延迟,必须小于下游模型自身 P95 波动的量级——否则网关本身就成了瓶颈。


网关在不同 RPS 下的延迟分布

在网关层做 10→50→100→200 RPS 爬坡测试,请求均匀分配到四个下游模型(各 25%)。观察网关引入的额外开销。

wrk2 -t4 -c100 -d300s -R100 --latency \
  -s chat_completions.lua \
  http://gateway:8080/v1/chat/completions
RPS P50 P95 P99 错误率 网关 CPU
10 1406ms ± 312ms 3074ms ± 618ms 4012ms ± 852ms 0% 8%
50 1638ms ± 407ms 3817ms ± 743ms 5224ms ± 1061ms 0% 22%
100 1921ms ± 493ms 5083ms ± 1127ms 7816ms ± 1834ms 0.2% 45%
200 3184ms ± 1026ms 11472ms ± 3802ms 18253ms ± 6218ms 2.1% 78%

关键转折点在 100 RPS → 200 RPS:P99 从 7816ms 飙升到 18253ms,错误率从 0.2% 跳到 2.1%。翻看网关日志,这个阶段的下游连接池开始出现排队——请求在网关内部等可用连接的时间超过了实际的模型推理时间。

结论:以当前配置(4 个下游,每下游 20 连接池),网关的安全水位在 100 RPS 左右。超过这个量,瓶颈不在网关的处理性能,在连接池的排队深度。


连接池:被低估的延迟放大器

HTTP 连接池的配置直接影响尾延迟。两个极端:

  • 池太小:请求排队等连接,延迟飙升。
  • 池太大:下游模型被超额连接打满,触发限流或 OOM,反而更慢。

做一个对照实验:固定在 100 RPS,变化下游连接池大小(per downstream),看延迟和错误率的变化。

每下游连接数 P50 P99 网关排队占比 下游限流次数
5 5823ms ± 1406ms 14208ms ± 3817ms 72% 0
10 2405ms ± 592ms 9134ms ± 2602ms 38% 0
20 1921ms ± 493ms 7816ms ± 1834ms 15% 0
40 1847ms ± 458ms 8472ms ± 2051ms 6% 12
80 2106ms ± 713ms 12263ms ± 3948ms 3% 47

最优区间在 20-40 连接/下游。40 连接时 P99 反而略高于 20 连接——下游模型开始触发频率限制,部分请求被 429 拦截,重试拉高了尾延迟。到 80 连接时限制次数飙升,得不偿失。

实用规则:连接池大小 ≈ 目标 RPS × 模型平均响应时间 ÷ 下游实例数 × 1.5(留 50% 余量)。对本场景:100 × 1.92s ÷ 1 × 1.5 ≈ 29 连接,落在实测的最优区间内。


跨模型延迟方差:路由策略影响有多大

前面是均匀分配流量。如果把 100 RPS 全部路由到最快的 DeepSeek 和最慢的 Kimi,延迟分布差多少?

路由策略 目标模型 P50 P99 100 RPS 下的错误率
全部 DeepSeek DeepSeek V4 1317ms ± 304ms 4235ms ± 961ms 0%
全部 Kimi Kimi K2.7 2486ms ± 627ms 7129ms ± 1804ms 0.8%

同样 100 RPS,全跑 DeepSeek 的 P99(4235ms)只有全跑 Kimi(7129ms)的 59%。但 DeepSeek 也有自己的水位线——到 150 RPS 时 P99 跳到 6814ms,已经接近 Kimi 在 100 RPS 的水平了。

这引出一个工程决策:灰度路由。 不是把所有流量扔给最快的模型,而是在延迟可接受的前提下,按模型当前的实时延迟做加权分配。伪代码:

def select_model(models: list[ModelStats]) -> str:
    """基于实时延迟的加权路由"""
    weights = []
    for m in models:
        if m.error_rate > 0.05:  # 错误率超 5% 的模型直接降权
            weights.append(0.1)
        else:
            # 延迟越低权越高
            weights.append(1.0 / max(m.p99_latency, 0.1))
    
    total = sum(weights)
    probs = [w / total for w in weights]
    return random.choices([m.name for m in models], weights=probs)[0]

灰度路由让网关能自动把流量从变慢的模型上撤走,而不是等到报错再切——这对 P99 的改善比连接池优化更直接。


Token 计费和延迟的取舍:快不等于便宜

延迟最低的模型,Token 消耗不一定最低。同一个翻译任务在四个模型上的 Token 消耗:

模型 输出 Token 输入 Token 总 Token 成本(按 2026.06 定价) P99 延迟
DeepSeek V4 142 215 ¥0.0032 4235ms ± 961ms
Qwen3.6 Max 138 215 ¥0.0048 4812ms ± 1073ms
Kimi K2.7 168 220 ¥0.0061 7129ms ± 1804ms
GLM-5.2 145 218 ¥0.0052 5926ms ± 1458ms

DeepSeek 最快也最便宜。但注意 Kimi 的 输出 Token 比 DeepSeek 多了 18%——同样的任务,Kimi 的回复更"啰嗦",这多出来的 Token 提升了成本,也延长了生成时间(更多 Token = 更长的流式输出)。

如果网关不做 Token 计量和统计,你永远不知道同样的任务在不同模型上产生的成本差异。聚合网关上的一道 Token 计数聚合,能让你按模型、按时间段、按任务类型拆解消耗——这是单独对接各模型 API 时做不到的全局视角。

但计费有个容易踩的坑。如果按直觉在请求进来的 middleware 里读 request.body() 统计 Token,你拿到的只是 prompt tokens(输入的 Token 数),而 completion tokens(模型输出的 Token 数)——通常才是计费的大头——根本拿不到。正确做法是在流式响应完全返回后,从响应的 usage 字段里提取两个值。这里用 FastAPI 的 BackgroundTasks,在响应发回给用户之后再异步统计,不阻塞主链路:

from fastapi import BackgroundTasks, Request
from starlette.responses import StreamingResponse
import json

async def billing_task(model: str, prompt_tokens: int, completion_tokens: int):
    """响应返回给用户之后异步执行,不增加请求延迟"""
    cost = calculate_cost(model, prompt_tokens, completion_tokens)
    await db.insert_billing_record(model, prompt_tokens, completion_tokens, cost)
    metrics.token_total.labels(model=model, type="prompt").inc(prompt_tokens)
    metrics.token_total.labels(model=model, type="completion").inc(completion_tokens)

@app.post("/v1/chat/completions")
async def chat_completions(request: Request, background_tasks: BackgroundTasks):
    body = await request.json()
    model = body.get("model", "default")
    
    # 流式调用下游模型
    response = await proxy_to_model(body)
    
    # 等待流式响应完整接收后,提取 usage(此时 prompt + completion 都有)
    usage = response.get("usage", {})
    prompt_tokens = usage.get("prompt_tokens", 0)
    completion_tokens = usage.get("completion_tokens", 0)
    
    # 注册后台任务:响应已经发给用户了,计费在后台异步完成
    background_tasks.add_task(billing_task, model, prompt_tokens, completion_tokens)
    
    return StreamingResponse(generate_sse_stream(response), media_type="text/event-stream")

上面这段就是我在器灵上调用时用的计费逻辑,响应返回后异步统计,不拖主链路。实际跑下来,加了 BackgroundTasks 之后请求延迟几乎没有感知变化。


压测结论与工程建议

这次压测的三个核心发现:

  1. 网关的安全水位取决于连接池配置,不是 CPU 或内存。连接池大小与目标 RPS、模型响应时间成线性关系,每下游 20-40 连接是黄金区间。

  2. 灰度路由比固定分配对 P99 的改善更显著。按实时延迟加权把流量从慢模型上撤走,P99 可降低 30-40%。

  3. 延迟和 Token 成本不是正相关。最快的模型不一定最省 Token,网关层必须同时监控延迟和 Token 消耗,才能做出正确的路由和选型决策。

如果你管理的多模型 API 调用量达到每天百万级,上述任何一个指标的轻微劣化,都会被放大成可观的成本或用户体验损失。在网关上统一做延迟监控、连接池管理、Token 计费聚合,比每个服务各自维护一套指标系统高效得多。

统一聚合网关的核心价值,其实不在"多一个模型选项",而在把性能可观测性和成本控制从下游模型的黑盒里拉出来,变成网关层可控的变量——延迟能看到、费用能拆开、流量能按实时状态调度。对于有规模要求的工程团队,这才是选聚合方案时最该关注的指标。

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐