很多人第一次调接口不是卡在代码写不出来,而是卡在「不知道哪一步断了」:Key 到底发出去没有、请求到底走到哪一层、超时是本地网络还是上游排队。这篇把一次调用的链路拆成几个可观察的节点,每个节点给你一个能验的动作和一个能看的日志字段,画成流程图照着走,失败时能直接定位到是哪一跳。
很多人第一次调接口不是卡在代码写不出来,而是卡在「不知道哪一步断了」。Key 到底发出去没有、请求到底走到哪一层、超时是本地网络还是上游排队,这三件事只要有一个说不清,排查就会变成盲猜。这篇把一次调用拆成几个能观察的节点,每个节点给你一个可验证的动作和一个能看的日志字段,照着流程走,失败时能直接定位到是哪一跳,而不是从头重写一遍代码。
下面这张链路图是通用版本,实际部署里多一层自建反代或少一层客户端代理都正常。重点不是记住有几个节点,而是每个节点你手上有没有对应的信号。画出你自己的版本,标上谁负责加 Key、谁负责改路径、谁负责重试,责任不清才是排查里最贵的成本。
先把链路拆成 6 跳,别一上来就看代码
一次调用大致是:客户端组装请求、本地代理或网关出站、目标网关鉴权、路由选后端、上游推理、响应逐层回传。任何一跳都可能是断点,但每跳都能留一个可观察信号。先画图再排错,顺序反了会把时间全花在改代码上。
| 链路节点 | 可观察信号 | 出问题时的第一反应 |
|---|---|---|
| 客户端组装 | 本地日志的发起时间戳与请求体 | 看参数和字段名,不碰网络 |
| 出站(DNS/代理) | 连接是否成功、证书是否报错 | 查本机网络与代理配置 |
| 目标网关鉴权 | 状态码与响应头里的请求 ID | 区分没带凭证还是凭证无效 |
| 路由选后端 | 网关返回的排队或限流提示 | 看并发额度与上游状态 |
| 上游推理 | 首字节耗时与总耗时 | 判断是排队慢还是生成慢 |
| 响应回传 | 流是否被中途截断 | 确认是断流还是业务错误 |
- 先画图再写排查脚本:把节点按你实际部署画出来,多一层反代就多画一层,否则状态码会全是网关层错误看不出所以然
- 每个节点只认一个信号:本地时间戳、响应头请求 ID、状态码、首字节耗时,四个够用了,记太多反而没人看
- 标清责任归属:谁加 Key、谁改路径、谁做重试,这三件事没写清楚,排查会变成互相甩锅
- 别跳过出站那一跳:大量「接口挂了」其实是 DNS、代理或证书问题,跟模型和 Key 完全没关系
鉴权那一跳:Key 是怎么被带上又怎么被拒的
鉴权失败分两种,处理动作完全不同。一种是「没带凭证」,通常是字段位置或字段名写错;另一种是「凭证无效」,多半是 Key 本身、额度或权限的问题。前者改代码,后者换 Key 或查账户,混在一起处理就会来回改半天。
- 没带凭证
- 请求里根本没有 Key 字段,或放错了位置,网关认为你没打算鉴权
- 凭证无效
- Key 带上了但不被接受,方向是 Key 本身、额度或权限范围
- 请求 ID
- 响应头里用于跨链路对齐的标识,缺了它排查基本靠猜
- 先确认 Key 放在哪:请求头还是查询串,两套协议不一样,放错位置会被当成没鉴权而不是格式错
- 区分状态码再动手:先看是哪一类拒绝,再决定是改代码还是换凭证,方向错了重试只会白烧配额
- 中转站场景要额外确认:Key 是给中转站的还是给原厂的,混用是最典型的「本地能跑线上挂」
- 不要对鉴权失败做客户端重试:这类错误重试一次和一百次结果一样,直接失败并记录请求 ID
判断标准很简单:如果换成一把确定可用的 Key 就通了,问题在凭证;如果换了还不行,问题在字段位置或请求路径。这个对照动作能省掉大量来回试探。想核对各家在鉴权字段和错误码上的差异,可以到 /models 的模型资料区对照当时的说明。
超时与重试:链路里最容易雪上加霜的一跳
超时最怕只设一个值。连接超时、首字节或读取超时、整体超时是三层,混成一个值会让「网关慢」和「模型慢」看起来一模一样。至少把连接和读取分开,你才能从日志里看出是连不上还是连上了不出字。
- 分开设三层超时连接超时管握手,读取超时管多久没收到数据,整体超时管这次调用最长允许多久。三者在日志里分别落一行。
- 给重试划边界只对连接失败和网关层错误退避重试,参数错误和鉴权错误不重试。设次数上限和总耗时上限。
- 流式单独处理断流发生在首字节之后要不要重发,取决于业务能不能接受重复输出。不能接受就别重发,标记为部分成功。
- 压测一次上限用最坏情况跑一遍,确认并发额度不会被自己的重试放大吃掉。
重试不是万能药,它只对「再试一次可能就好了」的错误成立,对确定性错误只会放大成本。
什么情况下不适用:如果你做的是离线批处理,超时可以设得很宽松,重试策略也可以更激进;但如果是面向用户的实时交互,宁可早失败也不要让用户干等,这时整体超时应当优先于重试次数。具体到不同服务的额度与限流规则,去 /price-watch 看当时的追踪数据更靠谱。
日志该记哪几个字段,让这张图能复盘
日志的目标不是记录一切,而是让一次慢请求事后能被完整还原。字段多了没人看,少了下不了结论。下面这几个是能直接回答「是排队、是上游慢、还是本地网络抖」的最小集合。
| 字段 | 回答什么问题 | 注意事项 |
|---|---|---|
| 本地发起时间 | 这次调用是什么时候开始的 | 用统一时区,别混本地时间和 UTC |
| 目标地址 | 请求实际打到了哪 | 中转场景下这个值可能和你写的不一样 |
| 请求 ID | 这次调用对应上游哪一次 | 响应头里取,缺失要显式记成空 |
| 状态码 | 被哪一层拒了或放行了 | 区分网关层和业务层 |
| 首字节耗时 | 是排队慢还是生成慢 | 和总耗时一起看才有意义 |
| 重试次数 | 这次结果是不是重试换来的 | 重试成功的请求也要记 |
- 请求 ID 是跨链路对齐的唯一抓手,网关和中转站一般会透传或生成,拿不到就要在客户端自己生成并透传
- 日志里不要落完整 Key 和敏感正文,只留前后几位或哈希,既方便比对又不出事
- 把慢请求按上面的字段拉一条时间线,能直接看到时间耗在哪一跳,不需要复现现场
- 日志级别别一刀切:排查期开详细,稳定期降下来,否则成本和噪音都会失控
把常见症状对到流程图上的位置
排查最快的路径不是读日志,而是先按症状定位到节点,再去那个节点看信号。下面这张对照表把常见症状直接映射到链路位置,按顺序走一遍基本能收敛。
| 症状 | 大概率所在节点 | 先查什么 |
|---|---|---|
| 本地就报错 | 出站那一跳 | DNS、代理、证书,跟模型和 Key 无关 |
| 很快返回 4xx | 鉴权或请求组装 | Key 位置、请求路径、请求体字段 |
| 等很久才 5xx 或超时 | 路由或上游 | 并发额度、上游状态、重试是否叠加 |
| 有响应但内容不对 | 请求组装 | 参数、格式、上下文是否按预期拼装 |
| 响应中途断掉 | 响应回传 | 是否流式、是否被中途截断、要不要重发 |
适用边界也要说清:这张表假设你的链路里至少有一个能返回状态码的网关节点。如果链路全通但结果就是不对,那已经不是链路问题,而是参数和业务逻辑问题,回到组装那一跳看。想做一次端到端的连通性验证,可以用 /tools/speed-test 跑一遍,确认延迟到底出在哪一段;需要对照不同服务商的接口资料,去 /sites 按类别筛一遍当时的说明。
下一步把你的链路按上面 6 跳画出来,标上每跳的信号和责任人,之后遇到失败先对症状再进节点。想核对当下各家的接口口径和额度规则,到 /models 和 /price-watch 看当时的数字。→常见问题
接口调用流程图要不要画给团队看?
值得画,但要画成「你这套部署实际长什么样」,不是网上的通用示意图。标清每一跳谁负责、失败时看哪个日志字段,新人接手和半夜排障都省事。团队还没稳定链路的话,先画最小可用版本,等接入中转或自建反代再补节点。
为什么按图排查还是定位不到问题?
常见原因是缺请求 ID,导致本地日志和网关日志对不上。先确认响应头里有没有透传请求 ID,没有就自己在出站那一层生成一个并带上,以后所有排查都靠它串。另外把重试次数也记上,很多「偶发失败」其实是重试把错误覆盖掉了。
链路里哪一跳最该优先加监控?
优先监控出站那一跳和首字节时间。出站失败说明是本地或网络问题,跟模型无关;首字节时间突然变长通常意味着上游排队或额度吃紧。这两个指标能覆盖大部分「接口忽然变慢」的场景,比盯着总耗时有用。