Lazy loaded image
网关demo项目07丨failure path benchmark 与结果解读
字数 3224阅读时长 9 分钟
2025-11-20
type
Post
status
Published
date
Nov 20, 2025
slug
gateway007
summary
tags
gateway
category
icon
password

摘要

故障路径不能只看请求有没有成功返回,请求成功的情况下,调用的时间消耗可能远超正常情况,
文章 005 建立的是 healthy path 基线。这篇继续往下看 failure path:当网关进入 timeout fallback、upstream 500 fallback、stream 中断、backend unhealthy 这些场景时,客户端可能还能拿到 200/readyz 也可能还是绿的,但请求成本(消耗的时间)已经明显偏离正常基线。200 只能说明系统还活着,不能说明这次调用的成本还健康。
这篇只回答一句话:
failure path 还能返回 200,但时间成本可能已经远远偏离 healthy baseline。
这组 benchmark 的用处很直接:
  • 给后续故障场景压测提供对照线
  • 给监控和告警提供判断标准
  • 避免把“还能返回 200”误判成“系统成本仍然正常”
  • 帮后面判断该优先优化 timeout、retry、fallback、cooldown recovery,还是 healthy path 本身

一、先把 healthy baseline 拿出来

如果没有正常基线,failure path 的数字只能凭感觉判断。
这篇只拿文章 005 里最关键的 local HTTP adapter baseline 做参照。它已经包含 gateway 路径、HTTP request construction、socket I/O、response parsing 等本地 adapter 成本,但还没有进入故障路径。这条线就是后面所有 failure cost 的参照物。

non-stream baseline

  • P95 = 34 ms

stream baseline

  • TTFT P95 = 28 ms
  • Latency P95 = 98 ms
单独看 213.8 ms1.017s,只能感觉“有点慢”。放回这条 baseline 之后,就能看出来它们已经不在正常路径的量级里。baseline 的价值就在这里:它让故障成本有了判断尺度。

二、先看这次要测的几类故障路径

failure path 不是一种路径,混在一起看会把结论写糊。
这篇只看四类路径:healthy path、fallback path、interrupted stream path、not-ready edge。它们都和故障有关,但代价结构完全不同。故障路径要先分层,后面数字才有解释力。
这张图想说清楚三件事:
  • healthy path 对应文章 005 的正常基线
  • fallback path 保住了可用性,但请求代价变了
  • stream 首包后中断属于协议生效后的终止,不能按“慢一点的成功”理解
failure path 的重点不只是慢,它们的语义也不一样。

三、timeout fallback:可用性保住了,代价接近 timeout budget

timeout 是最容易把“还返回 200”和“成本正常”混在一起的场景。
这个场景里,primary synthetic upstream sleep 2.5s,adapter timeout 是 1s,secondary backend 保持 healthy。请求最后仍然由 secondary 接住,客户端还能拿到 200/readyz 也还是 200系统没有挂,但这次调用已经付出了接近 timeout budget 的等待成本。

场景配置

  • primary synthetic upstream sleep 2.5s
  • adapter timeout 1s
  • secondary backend 保持 healthy

观察结果

  • 客户端仍然拿到 200
  • 返回内容来自 secondary backend
  • 端到端耗时大约 1.017s
  • /readyz 仍然是 200

和基线对比

healthy non-stream adapter baseline:
  • P95 = 34 ms
timeout fallback 单次代价:
  • 1017 ms
这组结果说明得很直接:timeout fallback 的可用性,是用等待成本换回来的。/readyz = 200 和“请求成本健康”不是同一件事。

四、upstream 500 fallback:代价小于 timeout,但已经明显偏离正常路径

500 fallback 看起来比 timeout 轻,但它仍然进入了新的成本路径。
这个场景里,synthetic upstream 持续返回 500,adapter 允许一次 retry,retry 失败后 router fallback 到 secondary。客户端最后还是拿到 200,但 traced request 总耗时约 213.8 ms,upstream 记录到两次 500这不是简单“多慢了一点”,而是 adapter retry 和 router reroute 都进来了。

场景配置

  • synthetic upstream 持续返回 500
  • adapter 允许一次 retry
  • retry 失败后 router fallback 到 secondary

观察结果

  • 客户端仍然拿到 200
  • traced request 总耗时约 213.8 ms
  • upstream 记录到两次 500
  • /readyz 仍然是 200
两次 500 是这里最有价值的细节。它说明请求没有直接切 secondary,而是 adapter 先 retry,随后 router 才接管 fallback。这条路径的成本来自 retry + reroute + secondary 补位。

和基线对比

healthy adapter baseline:
  • P95 = 34 ms
当前故障路径:
  • 213.8 ms
这组结果说明,500 fallback 的代价比 timeout fallback 低,但它已经明显偏离正常路径。fallback 成功不代表成本还在 healthy range 里。

五、stream 首包前失败:完整输出还能保住,但成本已经升高

stream failure 先要看协议边界,再看时间成本。
首包前失败还在 fallback 窗口里。router 仍然可以切到 secondary,客户端最后能收到完整 SSE,末尾也有 data: [DONE]。这一类故障还能恢复成完整输出,但恢复本身已经有代价。

场景配置

  • upstream 在 first chunk 之前失败
  • router 仍然有时间 fallback 到 secondary

观察结果

  • 客户端仍然收到完整 SSE 响应
  • 最终仍然有 data: [DONE]
  • request duration 约 209.7 ms
  • metrics 中有 provider_request_failed
  • metrics 中也有 provider_fallback_succeeded
healthy stream baseline:
  • TTFT P95 = 28 ms
  • Latency P95 = 98 ms
首包前失败这条路径已经高于 healthy stream baseline。当前仓库还没有把这类故障场景单独做成完整 TTFT 分布表,所以这里先用 request duration 和 fallback signals 来判断。这一类 stream 故障能恢复,但已经不是接近 healthy stream 的成本。
这组结果说明:首包前失败可以 fallback,完整输出也能保住,但成本已经抬高。所以完整返回和低成本不能混成一个结论。

六、stream 首包后中断:协议已经生效,后面只能诚实终止

为什么?因为首包后中断不是 fallback 成功,也不是慢一点的成功。
这个场景里,stream 已经开始输出,上游随后中断。客户端先收到 partial chunk,之后连接结束,没有 data: [DONE]。metrics 里有 provider_stream_interrupted,没有 provider_fallback_succeeded。我想说的是,首包发出后,这条流已经对客户端生效,网关不能再拼一条新的 fallback stream。

场景配置

  • stream 已经开始输出
  • 上游随后中断

观察结果

  • 客户端先收到 partial chunk
  • 之后连接结束
  • 没有 data: [DONE]
  • metrics 中有 provider_stream_interrupted
  • 没有 provider_fallback_succeeded
这组信号放在一起,意义很明确:首包后中断属于协议生效后的诚实终止。网关没有把两条不同 provider 的输出拼成一次响应。stream failure 必须先分清首包前和首包后,这两个场景的语义完全不同。

七、/readyz = 200 只说明还能接流量,不说明成本健康

因为 readiness 是整体可用性信号,不是单请求成本信号。
这批结果里很容易被误判的一点是:很多故障场景下,/readyz 仍然保持 200。在当前网关设计里,只要还有一个 healthy backend,/readyz 就可能继续返回 200。primary 进入 cooldown,不会立刻让 gateway not ready。所有 backend 都 unhealthy 时,/readyz 才会变成 503/readyz 回答的是“还能不能接流量”,不是“这次请求成本正不正常”。
下面这些事情都可能发生在 /readyz 仍然是绿的时候:
  • timeout fallback 已经把请求拉到 1s 级别
  • 500 fallback 已经把请求拉到 200ms 级别
  • stream 首包前恢复已经明显高于 healthy stream baseline
所以 /readyz = 200 不能被读成“系统请求成本接近正常”。availability signal 和 cost signal 必须分开看。

八、把结果放在一张表里

这一篇的价值不在单个数字,而在几类 failure path 的代价结构。
把当前仓库里已经成立的 failure drill 和 healthy baseline 放在一起,可以得到这张表:
场景
客户端结果
关键代价
readiness
说明
healthy non-stream adapter path
200 JSON
P95 34 ms
200
正常 non-stream 基线
timeout fallback
200 JSON
1.017s
200
可用性保住,但代价接近 timeout budget
upstream 500 fallback
200 JSON
213.8 ms
200
明显偏离 healthy baseline
stream 首包前失败
完整 SSE + [DONE]
209.7 ms
200
仍可 fallback,完整输出被保住
stream 首包后中断
partial SSE
[DONE]、无 fallback success
200 或恢复中
协议边界上的终止
all backends unhealthy
/readyz = 503
readiness lost
503
aggregate readiness 真正翻红
这张表最有价值的地方,是把几类 failure path 的代价结构拆开了。timeout 是等待成本,500 fallback 是 retry + reroute 成本,stream 首包前失败是完整输出保住但成本升高,stream 首包后中断是协议生效后的终止,all backends unhealthy 才是 readiness 真正翻红。

九、边界说明

本地 synthetic upstream 能说明 failure cost,但不能替代完整 chaos benchmark。
本文里的对比不是 hosted provider benchmark,也不是完整的 chaos benchmark。当前用的是本地 synthetic upstream,所以这里测到的是 gateway path、adapter path、fallback path、interruption path 和本地 upstream 交互开销。我想说的是,这篇能说明 failure path 的代价结构,但不能抢真实 provider 和长时间故障窗口的结论。
这篇没有覆盖:
  • 高并发、长时间故障窗口下的尾延迟分布
  • probe recovery 抖动期间的波动统计
  • 真实 hosted provider 的 WAN jitter
  • 公网环境下更复杂的 latency 分布
所以这组结果的定位很清楚:它已经能量化几类 failure path 的代价,但还不能替代完整故障压测报告。这篇先把可重复的本地故障基线立住。

十、怎么复现实验

这些数字要能复现,后面才能继续拿来做优化对照。
保持和文章 005 相同的 local HTTP adapter 配置,只切换 synthetic upstream mode 就能复现这组场景。这组实验它可以反复作为 failure path 对照线使用。

总结

文章 005 建立的是 healthy path 性能基线。这篇 007 接着把 failure path 的代价拉出来看:
healthy non-stream adapter baseline 大约是 P95 34 ms
upstream 500 fallback 会升到约 213.8 ms
timeout fallback 会升到约 1.017s
而这些场景下 /readyz 仍然可能是 200。我想说的是,网关可用性和单请求成本必须分开判断。
这组结果给后面的工程判断立了一条标准线:
  • 不能只用 /readyz 判断系统成本是否正常
  • healthy benchmark 和 failure-path benchmark 必须分开看
  • 后续优化优先级要更多看 timeout、retry、fallback、cooldown recovery 这些 failure-path 机制
对 LLM Gateway 来说,200 只说明系统还活着。failure path 的时间成本已经偏离正常基线多少,才是更有用的信息。因此,healthy cost 和 failure cost 要分开测、分开看、分开优化。
 
回到首页