Lazy loaded image
网关demo项目05丨LLM Access Gateway 的分层基准测试
字数 2557阅读时长 7 分钟
2025-11-19
type
Post
status
Published
date
Nov 19, 2025
slug
gateway005
summary
tags
gateway
category
icon
password

摘要

这篇要先立一条很基础的判断:压网关,先要测对层次。后面再看 fallback、/readyz 波动、stream 中断、provider timeout 和 500,这些数字才有解释力。这篇先给后面的故障分析立一条健康基线。
我这次只保留两条对照线:
  • 一条更接近 gateway internal path
  • 一条更接近 gateway + HTTP adapter 的本地真实代码路径
后面无论写 failure-path benchmark,还是解释故障成本,都会拿这两条线做参照。

一、先把测量对象分开

“网关性能”这四个字很容易把几层开销混在一起。
我一开始也容易把“网关性能”当成一个总数字。再往下拆才发现,这里面至少混着 authgovernanceroutingJSON shapingHTTP request constructionsocket I/Oresponse parsingJSON encode / decode 这些东西。不先分层,后面的数字基本没法解释。
在这个项目里,我先把测量对象收成两条路径:
  • in-process mock path:更接近测 gateway internal path
  • local HTTP adapter path:更接近测 gateway internal path + real adapter cost
这篇只回答一句话:先把 gateway internal pathgateway + adapter path 分开,后面的性能数字才有解释力。
这张图只想说明一件事:同样叫“压网关”,你可能测到的是两种完全不同的成本。

二、这组 benchmark 先拿来干什么

因为基线是为了后面解释故障成本。
这组 benchmark 先干三件事:
  • 给后面的故障路径压测提供对照线
  • 帮我区分“网关内部开销”和“adapter 路径开销”
  • 避免后面把 fallback、timeout、stream interruption 的成本误判成网关本体突然变差
这篇当前要建立的,就是健康状态下两条路径各自的正常时间成本
为了把这两条路径稳定跑出来,我做了一个只服务本网关的轻量 benchmark driver:cmd/loadtest。它现在能压 POST /v1/chat/completions,支持 non-stream 和 stream,能输出 success / failure / P50 / P95 / max,stream 还能额外输出 TTFT 和 chunk 总数。这个工具现在先服务“分层测清楚”,不追求通用压测平台那种大而全。

三、先看 non-stream:adapter 多出来的成本在哪

这组 non-stream benchmark 回答的问题很直接:同样一条 chat completion,请求还没进真实 HTTP adapter 之前,和已经进了之后,成本差多少。这一组先拿来拆 internal path 和 adapter path。
运行参数:
  • requests = 100
  • concurrency = 10
结果如下:
路径
Success
Failure
Approx QPS
P50
P95
Max
In-process mock
100
0
740.7 req/s
11 ms
22 ms
24 ms
Local HTTP adapter
100
0
609.8 req/s
14 ms
34 ms
37 ms
我这里只先收一个判断:adapter path 比 mock path 慢一些,而且这个差异是稳定可见的。mock path 主要覆盖的是 auth / governance / routing / JSON shaping,adapter path 在这上面又加进了 HTTP request construction / socket I/O / response parsing / JSON encode / decode这里看到的额外成本,先理解成 adapter 自己带进来的本地代码路径成本。
这也是这组数据最有价值的地方。它把 gateway internal pathgateway + adapter path 拆开了。后面如果 failure path 的数字偏离很多,我才知道是在偏离哪条健康基线。

non-stream 结果图

这张图只想让人一眼看到:mock path 更接近 internal baseline,adapter path 额外多了一层本地真实 adapter 成本。后面再看故障路径时,默认对照的就是这里这条 adapter baseline。

四、再看 stream:TTFT 和整条流不是一回事

stream 不能只看总时延,至少还要把 TTFT 单独拎出来。
stream benchmark 跟 non-stream 不一样。它不只是在问“整条请求慢了多少”,还在问“第一个 chunk 多久出来”和“整条流多久结束”。这两个数字如果混在一起看,stream 的结论会很容易偏。stream 至少要拆成 TTFT 和 full completion 两段。
运行参数:
  • requests = 50
  • concurrency = 5
结果如下:
路径
Success
Failure
Throughput
Latency P95
TTFT P95
Chunks
In-process mock
50
0
549.5 req/s
19 ms
12 ms
200
Local HTTP adapter
50
0
61.4 req/s
98 ms
28 ms
150
这里最该先看的不是“adapter path 更慢”,而是 TTFT 和总时延的差距。adapter path 的 TTFT P95 还是 28 ms,但 Latency P95 已经到了 98 ms。这说明首个 chunk 出来得不算慢,整条流真正被拉长的是首 chunk 之后那一段。stream 的大头不一定在开头,很可能在后半段。
这背后的原因也比较直接。当前 synthetic upstream 会通过多次写出,把整条流式过程刻意拉长。所以 adapter path 的主要增量,不是在“首 token 出来之前”,而是在“首 token 之后整条流被拉长”。这组数据先告诉我:stream 不能只拿总时延说话。

stream 结果图:TTFT vs 总时延

这张图最想说明的是:首个 chunk 很快出来,不代表整条流完成得也快。后面分析 stream 故障路径时,TTFT 和 full completion 必须分开看。

五、为什么 benchmark 前先把 tenant 限额调高

因为不先把 limiter 影响隔开,测出来的就不是 healthy path,而是治理层拒绝行为。
本地 seed tenant 默认是:
  • 60 req/min
这个值适合 smoke test,不适合 benchmark。如果 benchmark 前不先把限额调高,请求会先被 limiter 挡住。那时你测到的是 quota 策略,不是 gateway internal path,也不是 adapter path调高 tenant 是为了把治理层噪声先隔离出去。
这一步本身就是 benchmark 方法的一部分。当前这一篇要看的,是健康状态下网关路径的正常成本,不是 rate limiter 的拒绝成本。基线要先干净,后面的故障成本才有地方落。

六、资源快照先补一眼:本地依赖里谁最重

前面的请求太短,单次 benchmark 看不出本地 stack 的资源主导项是谁。
前面的 100 请求 benchmark 结束得很快,一次性进程采样意义不大。所以我又补了一次更长的观测:
观测期间的资源快照如下:
Component
Approx Memory
Gateway
28 MiB
MySQL
587.8 MiB
Redis
9.953 MiB
这组快照我先不把它讲成“生产结论”。它当前只回答一个很实际的问题:本地 benchmark 环境里的资源 footprint 主导项是谁。结果很明确,当前主导项是 MySQL,不是 Redis,也不是网关进程本身。

七、这组结果先收到哪里

本地 adapter path 不等于 hosted provider benchmark,这个边界先说清楚,后面的结论才不会飘。
这里的 adapter path 对比,不是 hosted provider benchmark。当前用的是本地 synthetic OpenAI-compatible upstream,所以这里测到的是:
  • 网关路径
  • adapter 路径
  • 本地 upstream 交互开销
它没有覆盖:
  • WAN 抖动
  • 真实 provider 侧波动
  • 公网环境下更复杂的时延分布
所以这组 benchmark 的定位很明确:它建立了 gateway healthy path 的分层基线,但它不能替代真实 hosted-provider latency study。

八、怎么复现实验

这篇的价值之一就在于这些基线可以稳定复现,不是一次性的截图数字。
当前复现实验分两条路径:
  • in-process mock path:更接近测 gateway internal path
  • local HTTP adapter path:更接近测 gateway + adapter 的本地真实代码路径
这里最重要的不是命令本身,而是复现路径本身是稳定的。后面再写 failure-path benchmark 时,我可以回到这里重新对照。这条基线后面是可以反复拿来用的。

总结

压网关先要测对层次。只有把 gateway internal pathgateway + adapter path 的健康基线先分开,后面的故障成本才有解释力。现在已经能先收住这几条数字:
  • non-stream 的 in-process mock path740.7 req/sP95 22 ms
  • non-stream 的 local HTTP adapter path609.8 req/sP95 34 ms
  • stream 场景下,adapter path 的 TTFT P95 = 28 ms
  • 同一条路径的 Latency P95 会到 98 ms
本文确定了一条后面能继续拿来解释 failure path 的健康对照线。
回到首页