代理健康检查是绿的,用户请求也可能很慢。最后还是要回到真实路径、日志、耗时字段和同机测试。
openclaw-sub2api/latency-diagnosis
代理服务最容易出现一种误判:健康检查是绿的,所以服务没问题。
这句话只对了一半。健康检查通常只能证明进程还在、端口还通、某个简单路径能返回。它不能证明用户真实请求路径正常,也不能解释为什么用户要等很久才看到第一段响应。
我之前排查过一次类似问题,最后得到的教训很直接:先别急着猜 DNS、浏览器、反向代理或模型速度,先把证据接起来。
健康检查不是用户路径
一个代理服务至少有几条路径:
- 健康检查路径。
- 浏览器或客户端实际请求路径。
- 兼容第三方工具的探测路径。
- 查询额度、账单或用量的辅助路径。
- 服务内部访问上游的路径。
这些路径可能走同一个进程,但不一定经过同一段逻辑。健康检查返回正常,只说明最短路径没坏;用户请求慢,可能慢在路由、鉴权、上游连接、流式响应、代理节点或兼容层。
所以排查时我会把“服务活着”和“用户路径正常”分开看。
先看真实请求有没有进入应用
第一步先确认真实请求是否已经进入应用,再看浏览器瀑布图。
如果应用日志里根本没有这次请求,那问题大概率在更前面:DNS、边缘入口、反向代理、TLS、跨域预检或路径转发。
如果应用日志里已经有了请求,而且能看到完整耗时,那排查范围就缩小了:浏览器不是主因,边缘入口也未必是主因。下一步应该看应用内部怎么选择上游、什么时候开始收到上游数据、最后状态是什么。
这个判断能省很多时间。否则很容易在“可能是网络问题”里打转。
持久化耗时字段比口头感觉可靠
用户说“等了很久”很重要,但要定位问题,还是得看落库的耗时字段。
我更愿意看这些数据:
- 请求开始时间。
- 首段响应等待时间。
- 总耗时。
- 上游选择结果。
- 错误类型。
- 重试次数。
- 请求最后状态。
我最关心的是首段响应等待时间。它能回答一个问题:慢是在应用收到请求之前,还是应用转发到上游之后?
如果首段响应等待时间本身就很长,说明应用已经接到请求,只是迟迟没有从上游拿到可返回内容。这时继续盯着浏览器渲染或页面加载,方向就偏了。
同机基准测试要尽量小
定位模型或上游速度时,我会做同机基准测试。
同机的意思是:在部署服务所在机器上,直接请求本地代理入口,绕开公网链路和浏览器差异。基准请求也要小,最好只要求返回一个极短结果。
这样做主要是为了隔离变量;绝对值好不好看反而没那么重要:
- 同一个入口。
- 同一个机器。
- 同一个提示。
- 同一组账号或上游池策略。
- 连续多次采样。
如果短请求也经常等待很久,就不是前端展示问题;如果只有某些上游或某类账号慢,就要继续看路由策略和池状态。
兼容路径也要验证
很多代理服务不只被自己使用,还会被其他客户端或切换工具探测。
这些工具可能会访问一些看起来不重要的路径,例如探测接口、用量接口、历史账单兼容接口,甚至先发一个预检请求。主请求能跑,不代表这些辅助路径都正常。
所以上线修复时,我会列一张用户可见路径清单,不只测健康检查和一个主接口:
- 主请求路径能返回正确格式。
- 探测路径不会被误判为失败。
- 用量或余额路径能返回兼容格式。
- 预检请求能拿到必要头部。
- 错误时返回可解释的 JSON,而不是 HTML 或空响应。
这些检查看起来琐碎,但它们决定下游工具是不是会把一个可用服务判成不可用。
重启不是部署证明
还有一个常见误区:服务重启了,所以新代码已经部署了。
这也不一定成立。重启只能证明进程重新开始,不证明镜像版本、迁移状态、配置文件和数据库结构都变成了你以为的样子。
我更信这些证据:
- 当前运行版本。
- 迁移记录。
- 数据库字段是否存在。
- 新配置是否被进程读取。
- 新逻辑对应的接口行为是否出现。
如果只看健康检查,旧版本也可能是绿的。部署验证要看新行为有没有出现,不能停在服务是否存活。
我现在的排查顺序
现在遇到代理延迟,我会按这个顺序走:
- 复现用户可见路径。
- 查边缘入口有没有收到请求。
- 查应用日志有没有收到同一个请求。
- 看持久化耗时字段,尤其是首段响应等待时间。
- 看上游选择、错误、重试和池状态。
- 做同机小请求基准测试。
- 验证第三方客户端会访问的兼容路径。
- 如果改了代码或配置,用新行为证明部署成功。
这个顺序不保证一次就能找到根因,但它能避免最浪费时间的猜测。
先把慢在哪里量出来
个人项目里做代理服务,最容易低估的是可观测性。
服务小,也不能只靠“我试了一下能打开”。健康检查覆盖不了所有用户路径,流式响应和第三方客户端兼容尤其如此。体验差通常卡在更具体的位置:第一段结果什么时候出现,辅助路径是否兼容,部署后的新逻辑有没有生效。
这种问题没有捷径。把证据排清楚,比多重启几次更有效。
继续读