[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"blog-strapi-article:zh:ai-api-error-network-quota-model-access":3,"blog-article:\u002Fblog\u002Fai-api-error-network-quota-model-access":1078,"blog-related:\u002Fblog\u002Fai-api-error-network-quota-model-access":1079,"blog-translations:\u002Fblog\u002Fai-api-error-network-quota-model-access":1080},{"available":4,"article":5,"relatedArticles":290,"translations":880},true,{"source":6,"id":7,"documentId":8,"translationKey":8,"locale":9,"slug":10,"path":11,"_path":11,"title":12,"description":13,"excerpt":13,"image":14,"publishedAt":15,"updatedAt":16,"author":17,"tags":18,"featured":19,"sortOrder":20,"readingTime":21,"bodyType":22,"bodyBlocks":23,"bodyHtml":289},"strapi",3890,"nxcuwvpmpqq4i92lla63i04t","zh","ai-api-error-network-quota-model-access","\u002Fblog\u002Fai-api-error-network-quota-model-access","AI API 返回错误时，如何判断问题出在网络、配额还是模型权限","AI API 报错时，先记录连接阶段和响应来源，再结合 429 限流头、账户用量及模型权限排查；代理 IP 出口对照只能提供线索，不能单独确定故障层级。","https:\u002F\u002Fcms.quanqiudaili.com\u002Fuploads\u002Flarge_AI_API_cover_ccc08a3b64.webp","2026-11-03T02:57:06.346Z","2026-11-03T02:57:06.323Z","Global Proxies Team",[],false,0,3,"blocks",[24,30,36,40,65,69,73,77,99,103,107,111,125,129,141,145,149,163,167,171,175,189,199,203,207,241,245,254,258,265,269,275,279,285],{"type":25,"children":26},"paragraph",[27],{"text":28,"type":29},"同样的报错文本可能来自链路、配额或权限。先看连接阶段是否完成，再结合响应来源、错误体和限流信息判断；只有证据指向出口时，才做受控的代理 IP 对照。具体错误码以目标服务文档为准，测试使用自有账号和测试额度。","text",{"type":31,"level":32,"children":33},"heading",2,[34],{"text":35,"type":29},"怎么先判断错误发生在连接阶段还是 API 响应阶段？",{"type":25,"children":37},[38],{"text":39,"type":29},"先确认请求走到哪一步，再读报错。",{"type":41,"format":42,"children":43},"list","unordered",[44,49,53,57,61],{"type":45,"children":46},"list-item",[47],{"text":48,"type":29},"现象：无法解析域名 ｜ 停在哪一段：DNS ｜ 说明：可能尚未形成到目标 API 的 HTTP 请求；DNS 查询本身可能已发出，或由代理侧完成",{"type":45,"children":50},[51],{"text":52,"type":29},"现象：连接超时或被拒绝 ｜ 停在哪一段：TCP ｜ 说明：代理或目标不可达",{"type":45,"children":54},[55],{"text":56,"type":29},"现象：TLS 握手失败 ｜ 停在哪一段：TLS ｜ 说明：证书、SNI、中间设备",{"type":45,"children":58},[59],{"text":60,"type":29},"现象：收到 4xx\u002F5xx 或 request ID ｜ 停在哪一段：某一层返回了 HTTP 响应 ｜ 说明：核对响应来源、网关日志及服务方 ID 定义，才能判断上游是否处理",{"type":45,"children":62},[63],{"text":64,"type":29},"现象：长时间无响应后断开 ｜ 停在哪一段：传输 ｜ 说明：需看首字节时间",{"type":25,"children":66},[67],{"text":68,"type":29},"拿到 HTTP 响应只说明某一层返回了响应。应结合响应来源、网关日志、错误体、连接阶段记录及服务方对 request ID 的定义，判断请求是否到达并由上游处理。若目标域名解析失败，可能尚未形成到目标 API 的 HTTP 请求，但 DNS 查询可能已出机或由代理完成。",{"type":31,"level":32,"children":70},[71],{"text":72,"type":29},"不同错误码和症状分别该查什么？",{"type":25,"children":74},[75],{"text":76,"type":29},"先用上一节判断连接是否完成，再用本表核对响应状态和服务方错误定义。",{"type":41,"format":42,"children":78},[79,83,87,91,95],{"type":45,"children":80},[81],{"text":82,"type":29},"症状：429，含 Retry-After 或限流 header ｜ 常见线索 \u002F 需核验的原因：配额、速率或并发限制 ｜ 关键证据：读限流头，再查账户、项目与用量控制台",{"type":45,"children":84},[85],{"text":86,"type":29},"症状：401，提示鉴权失败 ｜ 常见线索 \u002F 需核验的原因：密钥错误、过期或作用域不足 ｜ 关键证据：错误体与密钥配置；换密钥能否恢复取决于服务方",{"type":45,"children":88},[89],{"text":90,"type":29},"症状：403，提示地区或模型不可用 ｜ 常见线索 \u002F 需核验的原因：权限或可用范围 ｜ 关键证据：换出口是否无效、换地区或换模型是否有效",{"type":45,"children":92},[93],{"text":94,"type":29},"症状：404，模型不存在 ｜ 常见线索 \u002F 需核验的原因：模型名称或版本错误 ｜ 关键证据：以服务方模型列表接口与错误体为准",{"type":45,"children":96},[97],{"text":98,"type":29},"症状：5xx，含 request id ｜ 常见线索 \u002F 需核验的原因：上游服务异常或策略拦截 ｜ 关键证据：同一请求在多地一致失败",{"type":25,"children":100},[101],{"text":102,"type":29},"状态码含义因服务方和网关而异，应以对应文档、错误体及限流头为准；同时核对账户、项目、模型权限和用量。证据仍不足时，记录缺少的对照条件再补测。",{"type":31,"level":32,"children":104},[105],{"text":106,"type":29},"连接失败、超时或 TLS 报错时先查什么？",{"type":25,"children":108},[109],{"text":110,"type":29},"连接超时、重置或 TLS 握手失败时，先根据失败阶段检查代理可达性、目标端口、证书链、SNI 和系统时间。",{"type":41,"format":42,"children":112},[113,117,121],{"type":45,"children":114},[115],{"text":116,"type":29},"连接超时：先看代理是否可达，再看目标端口是否放行；",{"type":45,"children":118},[119],{"text":120,"type":29},"连接被重置：常见于代理 IP 路径中断或中间设备干预；",{"type":45,"children":122},[123],{"text":124,"type":29},"TLS 握手失败：核对证书链、SNI 与系统时间，抓包看握手停在哪一步。",{"type":25,"children":126},[127],{"text":128,"type":29},"若连接已建立但首字节很慢，归入响应耗时观察：TTFB 同时包含连接路径与上游处理，先记录现象，再结合服务端响应和同条件对照定位。更换出口后的变化只能提示出口或路径相关，不能单独确认原因。",{"type":25,"children":130},[131,133,139],{"text":132,"type":29},"网络故障和产品缺陷难以区分时，可参考",{"text":134,"type":29,"marks":135},"多地区测试发现问题，如何判断是网络故障还是产品缺陷？",[136],{"href":137,"type":138},"https:\u002F\u002Fwww.quanqiudaili.com\u002Fblog\u002Fregional-network-vs-product-defect","link",{"text":140,"type":29},"。",{"type":31,"level":32,"children":142},[143],{"text":144,"type":29},"收到 429 时，怎么区分配额和限流？",{"type":25,"children":146},[147],{"text":148,"type":29},"部分短窗口限流会在等待窗口后缓解；余额不足、硬配额耗尽或项目权限问题通常需要相应配置或账务处理，不能假定等待即可恢复。",{"type":41,"format":42,"children":150},[151,155,159],{"type":45,"children":152},[153],{"text":154,"type":29},"账号或项目级限制候选：同一账号在多个出口重复出现；这不能单独证明账号配额耗尽。",{"type":45,"children":156},[157],{"text":158,"type":29},"出口相关限制候选：固定账号、请求和时间窗口后，多个配对样本随出口变化；恢复也不能单独证明 IP 限流。",{"type":45,"children":160},[161],{"text":162,"type":29},"并发限制候选：低并发与高并发结果不同；还需核对实际并发、限流头和服务端错误体。",{"type":25,"children":164},[165],{"text":166,"type":29},"更换出口只能作为初筛：它可能同时改变网络路径、IP 和服务端状态。应固定账号、请求、并发和时间窗口做重复对照，并结合错误体、限流头、控制台用量与项目配置判断。换 IP 无效不单独证明账号限额，换 IP 后恢复也不单独证明 IP 限流；证据不足时标记为未确认。并发限制还需记录请求是否同时发出，不能只看总量。",{"type":31,"level":32,"children":168},[169],{"text":170,"type":29},"收到 403 或模型不可用提示时怎么排查？",{"type":25,"children":172},[173],{"text":174,"type":29},"收到 403 或模型不可用提示时，按错误体区分地区范围、模型名称与账号授权，并核对服务方文档和账号配置。错误稳定复现仍只是线索，不能单独证明权限问题；继续换代理也不能替代配置核验。",{"type":41,"format":42,"children":176},[177,181,185],{"type":45,"children":178},[179],{"text":180,"type":29},"403 且提示地区不支持：核对公布的可用范围；",{"type":45,"children":182},[183],{"text":184,"type":29},"404 且提示模型不存在：核对模型名称、版本与账号可见列表；",{"type":45,"children":186},[187],{"text":188,"type":29},"403 且提示无模型权限：检查账号授权。",{"type":25,"children":190},[191,193,198],{"text":192,"type":29},"需要进一步核对地区功能可用性与权限配置时，可参考",{"text":194,"type":29,"marks":195},"SaaS 功能地区可用性提示不准确，如何检查权限与配置？",[196],{"href":197,"type":138},"https:\u002F\u002Fwww.quanqiudaili.com\u002Fblog\u002Fsaas-feature-availability-by-region-validation",{"text":140,"type":29},{"type":31,"level":32,"children":200},[201],{"text":202,"type":29},"怎样记录排查结论，方便后续复现？",{"type":25,"children":204},[205],{"text":206,"type":29},"为便于复现，按下表记录每次排查使用的条件和结果：",{"type":41,"format":42,"children":208},[209,213,217,221,225,229,233,237],{"type":45,"children":210},[211],{"text":212,"type":29},"字段：时间 ｜ 记录内容：UTC 时间，必要时标注本地时区",{"type":45,"children":214},[215],{"text":216,"type":29},"字段：环境 ｜ 记录内容：服务商与 API、模型、账号（按组织策略脱敏）",{"type":45,"children":218},[219],{"text":220,"type":29},"字段：出口 ｜ 记录内容：地区与脱敏后的出口标识",{"type":45,"children":222},[223],{"text":224,"type":29},"字段：过程 ｜ 记录内容：DNS、TCP、TLS、首字节各阶段耗时",{"type":45,"children":226},[227],{"text":228,"type":29},"字段：响应 ｜ 记录内容：状态码、错误码、request ID（按策略处理）、限流头",{"type":45,"children":230},[231],{"text":232,"type":29},"字段：尝试 ｜ 记录内容：重试次数与每次的结果",{"type":45,"children":234},[235],{"text":236,"type":29},"字段：核对 ｜ 记录内容：账号、配额、模型与地区权限的检查结果",{"type":45,"children":238},[239],{"text":240,"type":29},"字段：结论 ｜ 记录内容：判断与置信度；未确认时写出下一步要补的对照",{"type":25,"children":242},[243],{"text":244,"type":29},"结论可写成：“服务方返回 429 和 request ID；同账号多出口复测仍复现，暂倾向账号\u002F项目级限制，待控制台用量确认。”若证据不足以区分限流层级，标记为未确认，并注明待补的对照。",{"type":25,"children":246},[247,252],{"text":248,"type":29,"marks":249},"HTTP Semantics（RFC 9110）",[250],{"href":251,"type":138},"https:\u002F\u002Fwww.rfc-editor.org\u002Frfc\u002Frfc9110.html",{"text":253,"type":29},"说明 HTTP 响应状态的通用语义；具体 API 错误、限流与 request ID 含义仍应以服务方文档为准。",{"type":31,"level":32,"children":255},[256],{"text":257,"type":29},"FAQ",{"type":25,"children":259},[260],{"text":261,"type":29,"marks":262},"Q：429 一定代表账号配额用尽吗？",[263],{"type":264},"bold",{"type":25,"children":266},[267],{"text":268,"type":29},"A：不一定，也可能是短时限流或并发限制；应结合响应头、错误体和账号控制台判断。",{"type":25,"children":270},[271],{"text":272,"type":29,"marks":273},"Q：403 一定是模型权限问题吗？",[274],{"type":264},{"type":25,"children":276},[277],{"text":278,"type":29},"A：不一定，地区限制、组织策略或资源状态也可能导致 403，需以服务方错误说明为准。",{"type":25,"children":280},[281],{"text":282,"type":29,"marks":283},"Q：没有 request ID 还能提交问题吗？",[284],{"type":264},{"type":25,"children":286},[287],{"text":288,"type":29},"A：可以提供时间窗口、脱敏请求特征和响应码，并注明服务端未返回 request ID。","",[291,478,653],{"source":6,"id":292,"documentId":293,"translationKey":293,"locale":9,"slug":294,"path":295,"_path":295,"title":296,"description":297,"excerpt":297,"image":298,"publishedAt":299,"updatedAt":300,"author":17,"tags":301,"featured":19,"sortOrder":20,"readingTime":32,"bodyType":22,"bodyBlocks":302,"bodyHtml":289},3897,"y568ae3jjjs4yuaunluzwsqs","ai-api-egress-region-network-policy-diagnosis","\u002Fblog\u002Fai-api-egress-region-network-policy-diagnosis","测试 AI API 地区切换时，如何判断变化来自网络还是服务策略","切换代理 IP 出口地区后结果变化，如何区分网络与服务策略？本文用配对测试对照链路耗时、出口身份和服务端响应，避免把地区差异当成故障。","https:\u002F\u002Fcms.quanqiudaili.com\u002Fuploads\u002Flarge_AI_API_cover_0df5f0b27f.webp","2026-11-03T03:01:09.178Z","2026-11-03T03:01:09.154Z",[],[303,307,311,315,329,333,337,341,345,349,353,357,361,365,375,379,383,391,395,399,417,421,435,445,448,454,458,464,468,474],{"type":25,"children":304},[305],{"text":306,"type":29},"换地区之后结果变了，可能是链路、出口身份或服务策略，三者要分开取证。做法是固定请求只改地区，用分阶段耗时、IP 画像和响应证据各查一层。换地区这一步通常由代理 IP 完成，所以“换了地区”和“换了 IP”要当成两个变量记录。测试限制在自有账号和测试额度内，不通过反复请求真实付费接口制造样本。",{"type":31,"level":32,"children":308},[309],{"text":310,"type":29},"地区切换后，哪些因素可能导致结果变化？",{"type":25,"children":312},[313],{"text":314,"type":29},"地区切换可能同时改变链路、出口身份和服务策略相关条件；测试时分别记录这三类证据。",{"type":41,"format":42,"children":316},[317,321,325],{"type":45,"children":318},[319],{"text":320,"type":29},"链路质量：RTT、丢包、TLS 握手、首字节时间。",{"type":45,"children":322},[323],{"text":324,"type":29},"出口身份：IP 归属、ASN、IP 类型与信誉。",{"type":45,"children":326},[327],{"text":328,"type":29},"服务策略：限流、地区可用性、账号权限、模型可用列表。",{"type":25,"children":330},[331],{"text":332,"type":29},"如果同时换地区、出口和请求条件，结果无法区分来自哪项变化，因此对照时每轮只改变一个目标变量。",{"type":31,"level":32,"children":334},[335],{"text":336,"type":29},"怎么设计只改变出口地区的对照测试？",{"type":25,"children":338},[339],{"text":340,"type":29},"固定模型、prompt、客户端、超时和重试；按测试目的只改变地区或出口，并记录实际命中的版本和时间。",{"type":25,"children":342},[343],{"text":344,"type":29},"请求次数与观察时段应按接口波动和测试预算设定；尽量在相近时间重复对照，并避免用单次结果判断地区策略。",{"type":25,"children":346},[347],{"text":348,"type":29},"对照组至少三组：原地区、目标地区、以及目标地区的第二个出口 IP。第二组用来区分“地区本身”和“这个 IP”。",{"type":31,"level":32,"children":350},[351],{"text":352,"type":29},"怎么从 DNS、连接和响应耗时判断网络问题？",{"type":25,"children":354},[355],{"text":356,"type":29},"用 curl 对照时，必须显式使用与被测 API 相同的代理、绕过和凭据配置；凭据应从受限环境注入，不写进命令历史。下列字段是从请求开始累计的时间里程碑，不是独立 TCP\u002FTLS 耗时。代理 CONNECT、代理侧 DNS、连接复用和重定向都会影响解释；跳过的阶段标记为未观测。",{"type":25,"children":358},[359],{"text":360,"type":29},"```bash\ncurl -o \u002Fdev\u002Fnull -s -x \"$PROXY_URL\" -w 'namelookup_cumulative=%{time_namelookup} connect_cumulative=%{time_connect} appconnect_cumulative=%{time_appconnect} ttfb_cumulative=%{time_starttransfer}\\n' \\\nhttps:\u002F\u002Fapi.example.com\u002Fv1\u002Fmodels\n```",{"type":25,"children":362},[363],{"text":364,"type":29},"固定客户端、目标接口和时间窗口建立自身基线，再比较阶段分布；样本不足时只记录现象。`time_connect` 与 `time_appconnect` 不能直接当作到源站的独立 TCP\u002FTLS 时长。首字节慢也可能来自上游处理。",{"type":25,"children":366},[367,369,374],{"text":368,"type":29},"若要建立可比较的地区延迟基线，可参考",{"text":370,"type":29,"marks":371},"SaaS API 地区延迟基线怎么建？请求口径与分位数",[372],{"href":373,"type":138},"https:\u002F\u002Fwww.quanqiudaili.com\u002Fblog\u002Fsaas-api-region-latency-baseline",{"text":140,"type":29},{"type":31,"level":32,"children":376},[377],{"text":378,"type":29},"怎么确认出口 IP 的地区和类型？",{"type":25,"children":380},[381],{"text":382,"type":29},"对照多个 IP 数据库与目标服务看到的源 IP\u002F地区标签，并记录来源和查询时间。ASN、组织、城市和 IP 类型标签只能提供线索，不能单独证明出口的物理所在地或住宅\u002F机房属性；平台地区识别、购买地区与物理位置应分开记录。",{"type":25,"children":384},[385,387,390],{"text":386,"type":29},"同一地区用第二个出口复测。结果变化只说明出口或路径相关因素可能参与；固定账号、请求和时间窗口重复对照，再结合链路与服务端证据判断。来源冲突时标记为不确定。相关归因思路可参考",{"text":134,"type":29,"marks":388},[389],{"href":137,"type":138},{"text":140,"type":29},{"type":31,"level":32,"children":392},[393],{"text":394,"type":29},"哪些响应信息能提示账号或地区策略限制？",{"type":25,"children":396},[397],{"text":398,"type":29},"结合错误体、限流头、账号\u002F模型可用范围和传输记录判断；即使链路不稳定，有明确来源的服务端错误仍可作为证据，但不应单独结束排查。下表列出可继续核验的线索：",{"type":41,"format":42,"children":400},[401,405,409,413],{"type":45,"children":402},[403],{"text":404,"type":29},"证据：429 与 Retry-After ｜ 候选方向：限流或配额 ｜ 下一步核对：查限流头、控制台用量及重复样本",{"type":45,"children":406},[407],{"text":408,"type":29},"证据：403 与地区或模型提示 ｜ 候选方向：可用范围 ｜ 下一步核对：核对服务方文档、账号和模型配置",{"type":45,"children":410},[411],{"text":412,"type":29},"证据：request ID 与限流 header ｜ 候选方向：服务端处理 ｜ 下一步核对：按服务方定义与网关日志核验",{"type":45,"children":414},[415],{"text":416,"type":29},"证据：链路指标稳定但持续失败 ｜ 候选方向：服务策略候选 ｜ 下一步核对：重复请求并核对错误体和账号范围",{"type":31,"level":32,"children":418},[419],{"text":420,"type":29},"确认原因后，下一步分别怎么处理？",{"type":41,"format":42,"children":422},[423,427,431],{"type":45,"children":424},[425],{"text":426,"type":29},"候选方向：链路异常 ｜ 对应证据：阶段耗时反复偏离自身基线 ｜ 下一步动作：换路径或时段复测，再看服务端响应",{"type":45,"children":428},[429],{"text":430,"type":29},"候选方向：出口相关 ｜ 对应证据：固定其余条件后结果随出口变化 ｜ 下一步动作：增加配对样本，核对路径与出口来源",{"type":45,"children":432},[433],{"text":434,"type":29},"候选方向：服务策略候选 ｜ 对应证据：错误体、账号\u002F模型范围和重复样本相互吻合 ｜ 下一步动作：核对配置或向服务方确认",{"type":25,"children":436},[437,439,444],{"text":438,"type":29},"证据不一致时，记录缺少的对照条件并保持未决。curl 参数和时间字段的含义可查",{"text":440,"type":29,"marks":441},"curl 手册",[442],{"href":443,"type":138},"https:\u002F\u002Fcurl.se\u002Fdocs\u002Fmanpage.html",{"text":140,"type":29},{"type":31,"level":32,"children":446},[447],{"text":257,"type":29},{"type":25,"children":449},[450],{"text":451,"type":29,"marks":452},"Q：测试时 API 返回内容不稳定，会影响地区归因吗？",[453],{"type":264},{"type":25,"children":455},[456],{"text":457,"type":29},"A：会。应固定模型、输入和采样窗口；无法固定时，将内容波动作为单独限制记录。",{"type":25,"children":459},[460],{"text":461,"type":29,"marks":462},"Q：同一地区的两个出口结果不同，能直接认定是 IP 信誉吗？",[463],{"type":264},{"type":25,"children":465},[466],{"text":467,"type":29},"A：不能，先排除网络路径、账号状态和上游时间差，再把结论标为待验证。",{"type":25,"children":469},[470],{"text":471,"type":29,"marks":472},"Q：地区策略由服务方临时调整时，旧测试结论还有效吗？",[473],{"type":264},{"type":25,"children":475},[476],{"text":477,"type":29},"A：只对当时的账号、地区和时间窗口有效，策略变化后应重新验证。",{"source":6,"id":479,"documentId":480,"translationKey":480,"locale":9,"slug":481,"path":482,"_path":482,"title":483,"description":484,"excerpt":484,"image":485,"publishedAt":486,"updatedAt":487,"author":17,"tags":488,"featured":19,"sortOrder":20,"readingTime":32,"bodyType":22,"bodyBlocks":489,"bodyHtml":289},3896,"a6t066253yf747tqsxn0ts4x","http-client-proxy-retry-duplicate-submit-test","\u002Fblog\u002Fhttp-client-proxy-retry-duplicate-submit-test","HTTP 客户端升级后，如何检查代理重试是否导致重复提交","HTTP 客户端升级后，如何检查重试是否导致重复提交？本文用请求计数区分自动重试、重定向重放和连接中断，并说明幂等验证要点。","https:\u002F\u002Fcms.quanqiudaili.com\u002Fuploads\u002Flarge_HTTP_cover_6e441939e4.webp","2026-11-03T03:00:50.977Z","2026-11-03T03:00:50.965Z",[],[490,494,498,502,516,520,524,528,532,550,554,564,568,572,576,590,594,598,602,606,610,620,623,629,633,639,643,649],{"type":25,"children":491},[492],{"text":493,"type":29},"重复提交往往不是业务代码写错，而是客户端在代理 IP 链路上自动重试了非幂等请求。要验证这一点，得让“请求被执行了几次”变成可观测的数字，不靠肉眼核对结果。下面用一个计数接口做靶子，配合注入失败来暴露重试路径。",{"type":31,"level":32,"children":495},[496],{"text":497,"type":29},"怎么判断请求被重复处理了？",{"type":25,"children":499},[500],{"text":501,"type":29},"业务代码调用一次后，客户端可能发送多次网络请求；这不等于业务副作用也执行多次。对照三类记录：客户端发送尝试、服务端接收次数、同一业务键对应的实际副作用次数。再检查幂等键是否一致。只看最终结果不够，因为重复写入可能互相覆盖而不显错。常见重放入口如下：",{"type":41,"format":42,"children":503},[504,508,512],{"type":45,"children":505},[506],{"text":507,"type":29},"重试机制：状态码重试 ｜ 触发条件：503、502、连接超时 ｜ 注入方式：让服务端对部分请求返回 503",{"type":45,"children":509},[510],{"text":511,"type":29},"重试机制：重定向重放 ｜ 触发条件：307、308 通常要求保留方法与请求体 ｜ 注入方式：测试网关让 A 重定向到 B，仅跳转一次；循环作为单独负例并限制跳数",{"type":45,"children":513},[514],{"text":515,"type":29},"重试机制：连接重置重试 ｜ 触发条件：客户端认为请求未发出 ｜ 注入方式：代理侧主动断开连接",{"type":31,"level":32,"children":517},[518],{"text":519,"type":29},"怎么搭建一个可观察请求次数的测试接口？",{"type":25,"children":521},[522],{"text":523,"type":29},"测试接口按业务键统计接收次数和副作用次数，并返回请求序号。日志只保留脱敏关联键、必要状态和计数，过滤认证头、Cookie、URL 凭据与业务正文。测试请求应经过被测客户端和代理路径，以便对照客户端发送、服务端接收及业务处理记录。",{"type":31,"level":32,"children":525},[526],{"text":527,"type":29},"客户端会在什么情况下自动重试？",{"type":25,"children":529},[530],{"text":531,"type":29},"客户端的重试策略由具体库、版本、状态码和请求配置决定，不能只按 HTTP 方法推断。下表列的是需要核验的风险：",{"type":41,"format":42,"children":533},[534,538,542,546],{"type":45,"children":535},[536],{"text":537,"type":29},"方法：GET \u002F HEAD ｜ 方法语义：幂等 ｜ 待核验的风险：通常可安全重试，仍需核对客户端策略与服务端实现",{"type":45,"children":539},[540],{"text":541,"type":29},"方法：PUT \u002F DELETE ｜ 方法语义：幂等 ｜ 待核验的风险：客户端可能重试，需确认服务端确实做过幂等实现",{"type":45,"children":543},[544],{"text":545,"type":29},"方法：POST ｜ 方法语义：非幂等 ｜ 待核验的风险：重试即可能重复创建，需核对客户端策略、幂等键与服务端处理",{"type":45,"children":547},[548],{"text":549,"type":29},"方法：PATCH ｜ 方法语义：非幂等 ｜ 待核验的风险：部分客户端仍会重试，需按具体版本核对",{"type":25,"children":551},[552],{"text":553,"type":29},"升级后要特别确认：新版本是否把 POST 也纳入了可重试范围。这正是重复提交最常见的来源。",{"type":25,"children":555},[556,558,563],{"text":557,"type":29},"需要继续核对投递、签名与重试行为，可参考",{"text":559,"type":29,"marks":560},"SaaS Webhook 地区测试怎么做？到达、签名与重试",[561],{"href":562,"type":138},"https:\u002F\u002Fwww.quanqiudaili.com\u002Fblog\u002Fsaas-webhook-delivery-retry-region-testing",{"text":140,"type":29},{"type":31,"level":32,"children":565},[566],{"text":567,"type":29},"307 和 308 为什么可能重复发送请求？",{"type":25,"children":569},[570],{"text":571,"type":29},"客户端若自动跟随 307\u002F308，通常会尝试保留方法和请求体重新发送，但请求体是否可重放、原请求是否已被处理、服务端是否有副作用，都取决于客户端实现与应用设计。",{"type":25,"children":573},[574],{"text":575,"type":29},"测试时检查客户端是否自动跟随、请求体能否重放，以及 A、B 两端分别收到几次请求：",{"type":41,"format":42,"children":577},[578,582,586],{"type":45,"children":579},[580],{"text":581,"type":29},"是否携带同一个幂等键；",{"type":45,"children":583},[584],{"text":585,"type":29},"是否重复执行了创建逻辑；",{"type":45,"children":587},[588],{"text":589,"type":29},"是否存在重定向循环。",{"type":25,"children":591},[592],{"text":593,"type":29},"仅在隔离的测试环境中，可将 `Location` 指向计数接口，观察客户端是否再次发送请求。请求次数增加说明发生了重放；还需检查服务端是否重复执行了业务操作。若方法或请求体发生变化，应记录具体客户端行为，并对照其版本文档。",{"type":31,"level":32,"children":595},[596],{"text":597,"type":29},"代理连接中断时，写请求会不会被重新发送？",{"type":25,"children":599},[600],{"text":601,"type":29},"断开位置决定服务端是否处理过请求。可在受控环境中分别模拟发送前失败、服务端接收后失败和业务提交后丢弃响应，再对照客户端日志、接收次数与副作用记录。连接重置本身无法说明请求是否已执行。",{"type":31,"level":32,"children":603},[604],{"text":605,"type":29},"怎么模拟失败并确认修复有效？",{"type":25,"children":607},[608],{"text":609,"type":29},"在隔离环境中逐项注入故障：发送前断开、业务提交后丢弃响应、返回 503、A 到 B 的一次 307，以及受控超时。重定向循环另作负例并限制跳数。每轮只改一个条件，记录客户端发送、服务端接收和业务副作用次数。修复后重跑相同用例；只有业务记录没有重复副作用且各层计数符合预期，才算通过。",{"type":25,"children":611},[612,614,618],{"text":613,"type":29},"307\u002F308 的 HTTP 语义见 ",{"text":615,"type":29,"marks":616},"RFC 9110",[617],{"href":251,"type":138},{"text":619,"type":29},"；客户端是否自动跟随及请求体能否重放，应按具体版本验证。",{"type":31,"level":32,"children":621},[622],{"text":257,"type":29},{"type":25,"children":624},[625],{"text":626,"type":29,"marks":627},"Q：幂等键能避免所有重复写入吗？",[628],{"type":264},{"type":25,"children":630},[631],{"text":632,"type":29},"A：不能，需确认服务端确实支持幂等键，并验证键的作用范围和保留时间。",{"type":25,"children":634},[635],{"text":636,"type":29,"marks":637},"Q：读请求和写请求可以使用相同重试策略吗？",[638],{"type":264},{"type":25,"children":640},[641],{"text":642,"type":29},"A：不应默认相同；写请求需评估服务端处理状态未知时的重复执行风险。",{"type":25,"children":644},[645],{"text":646,"type":29,"marks":647},"Q：客户端没有收到响应，能否判断服务端未处理请求？",[648],{"type":264},{"type":25,"children":650},[651],{"text":652,"type":29},"A：不能。连接中断时处理状态可能未知，应通过服务端记录或业务状态核对。",{"source":6,"id":654,"documentId":655,"translationKey":655,"locale":9,"slug":656,"path":657,"_path":657,"title":658,"description":659,"excerpt":659,"image":660,"publishedAt":661,"updatedAt":662,"author":17,"tags":663,"featured":19,"sortOrder":20,"readingTime":664,"bodyType":22,"bodyBlocks":665,"bodyHtml":289},3895,"tf37dm2ee9mh0u8xbco8p5ch","go-transport-proxy-connection-regression","\u002Fblog\u002Fgo-transport-proxy-connection-regression","Go 版本或 Transport 配置变更后，如何重新检查代理连接","Go 版本或 Transport 配置变更后，如何检查代理连接？本文核对代理配置来源，并对照连接复用、TLS、HTTP\u002F2 和分阶段耗时。","https:\u002F\u002Fcms.quanqiudaili.com\u002Fuploads\u002Flarge_Go_Transport_cover_4e51fe43de.webp","2026-11-03T03:00:34.422Z","2026-11-03T03:00:34.401Z",[],4,[666,670,674,678,692,696,700,704,708,712,716,720,724,728,732,736,746,750,754,768,772,776,780,794,798,802,806,810,814,836,840,847,850,856,860,866,870,876],{"type":25,"children":667},[668],{"text":669,"type":29},"Go 的代理 IP 行为由版本、环境变量和 `Transport` 配置共同决定，任何一项变化都可能改变实际走向。回归的关键是把“代理从哪来”和“请求走到哪一步”都变成可观测的。下面用标准库的 `httptrace` 与会话复用指标做示范，测试在自有服务与测试出口上进行。",{"type":31,"level":32,"children":671},[672],{"text":673,"type":29},"升级后先确认 Go 版本和哪些配置变了",{"type":25,"children":675},[676],{"text":677,"type":29},"先记录 Go 版本、构建选项和代理环境配置，作为升级前后对照基线：",{"type":41,"format":42,"children":679},[680,684,688],{"type":45,"children":681},[682],{"text":683,"type":29},"Go 版本：`go version`，大版本升级可能改变默认 TLS 与 HTTP\u002F2 行为；",{"type":45,"children":685},[686],{"text":687,"type":29},"GOFLAGS 与构建标签：是否启用或关闭了某些默认行为；",{"type":45,"children":689},[690],{"text":691,"type":29},"环境变量：`HTTP_PROXY`、`HTTPS_PROXY`、`NO_PROXY` 是否被设置，以及大小写写法。",{"type":25,"children":693},[694],{"text":695,"type":29},"`HTTP_PROXY` 与 `http_proxy` 的处理及 `NO_PROXY` 匹配规则应按 Go 版本核实。回归前记录变量是否设置、脱敏后的主机\u002F端口与绕过规则，再和升级前记录对比。不要输出含 URL userinfo、口令或完整凭据的代理环境变量。",{"type":31,"level":32,"children":697},[698],{"text":699,"type":29},"Go 请求最终使用了哪种代理配置？",{"type":25,"children":701},[702],{"text":703,"type":29},"最终用哪套配置，以当前请求实际使用的 `Transport` 和绕过规则为准。除了 `Proxy`，还要检查自定义 `DialContext`、`NO_PROXY`、重定向和连接池；环境变量是否被读取还取决于读取时机，不能只看代码或环境变量就下判断。",{"type":25,"children":705},[706],{"text":707,"type":29},"```go\ntr := &http.Transport{\nProxy: http.ProxyFromEnvironment, \u002F\u002F 读环境变量\n\u002F\u002F Proxy: http.ProxyURL(proxyURL), \u002F\u002F 显式指定\nTLSHandshakeTimeout: 5 * time.Second,\n}\nclient := &http.Client{Transport: tr, Timeout: 30 * time.Second}\nreq, _ := http.NewRequest(\"GET\", \"https:\u002F\u002Fapi.example.com\u002Fv1\u002Fmodels\", nil)\nreq.Header.Set(\"Authorization\", \"Bearer \"+os.Getenv(\"API_KEY\"))\nresp, err := client.Do(req)\n```",{"type":25,"children":709},[710],{"text":711,"type":29},"若 `Transport.Proxy` 显式设置了代理函数或地址，请求将按该配置处理，不会再由另一套环境变量配置替代。使用标准库 `ProxyFromEnvironment` 时，代理环境配置存在进程级缓存行为；按目标 Go 版本核对实现。修改环境变量后，应在设置好环境的新进程中测试，只新建 Transport 不保证刷新缓存。需要运行时动态选择代理时，用明确管理的 `Proxy` 函数或 `ProxyURL`，并单独管理连接池。",{"type":25,"children":713},[714],{"text":715,"type":29},"在隔离测试环境中，可将代理地址指向不可用端口作为负向测试。请求仍成功只说明该请求没有依赖这个代理配置；还应排查 `NO_PROXY`、其他代理设置、连接复用等因素，并核对最终出口。",{"type":25,"children":717},[718],{"text":719,"type":29},"`NO_PROXY` 命中时请求会绕过代理；应把匹配结果与实际出口一起核对。",{"type":31,"level":32,"children":721},[722],{"text":723,"type":29},"怎么用 `httptrace` 定位连接耗时？",{"type":25,"children":725},[726],{"text":727,"type":29},"`httptrace` 提供的是阶段事件，不是现成的耗时报表；需要在事件回调里记时间戳、算差值，再和总耗时对照。",{"type":25,"children":729},[730],{"text":731,"type":29},"```go\nstart := time.Now()\nvar dnsStart, connStart, tlsStart time.Time\ntrace := &httptrace.ClientTrace{\nDNSStart:          func(httptrace.DNSStartInfo) { dnsStart = time.Now() },\nDNSDone:           func(httptrace.DNSDoneInfo) { log.Printf(\"dns %v\", time.Since(dnsStart)) },\nConnectStart:      func(network, addr string) { connStart = time.Now(); log.Printf(\"connect start %s %s\", network, addr) },\nConnectDone:       func(network, addr string, err error) { log.Printf(\"connect done %s %v %v\", addr, time.Since(connStart), err) },\nTLSHandshakeStart: func() { tlsStart = time.Now() },\nTLSHandshakeDone: func(cs tls.ConnectionState, err error) {\nlog.Printf(\"tls %v proto=%s\", time.Since(tlsStart), cs.NegotiatedProtocol)\n},\nGotFirstResponseByte: func() { log.Printf(\"ttfb %v\", time.Since(start)) },\n}\nreq = req.WithContext(httptrace.WithClientTrace(req.Context(), trace))\n```",{"type":25,"children":733},[734],{"text":735,"type":29},"开始事件与完成事件要成对记录，才能计算 DNS、拨号、TLS 等阶段时长；只注册起点不会得到 duration。示例中的单一时间变量只适用于串行、单次尝试；Happy Eyeballs 等情况下可能并发或多次触发回调，应按连接尝试关联时间戳，并用互斥锁或并发安全结构保护。若连接复用，可能没有新的拨号事件，缺事件不等于零耗时。代理下 `ConnectStart` 的目标通常是代理地址；经 HTTP 代理时 DNS 也可能由代理完成。",{"type":25,"children":737},[738,740,745],{"text":739,"type":29},"移动应用场景的 DNS、连接和 TLS 分阶段定位方法，可参考",{"text":741,"type":29,"marks":742},"移动 App 网络兼容测试：五个失败阶段定位法",[743],{"href":744,"type":138},"https:\u002F\u002Fwww.quanqiudaili.com\u002Fblog\u002Fmobile-app-network-egress-compatibility-testing",{"text":140,"type":29},{"type":31,"level":32,"children":747},[748],{"text":749,"type":29},"怎么确认连接复用和超时设置仍然生效？",{"type":25,"children":751},[752],{"text":753,"type":29},"检查连接池配置与 `GotConn` 事件，比较同一负载下的复用结果：",{"type":41,"format":42,"children":755},[756,760,764],{"type":45,"children":757},[758],{"text":759,"type":29},"`DisableKeepAlives: true` 会让每个请求重新建连；",{"type":45,"children":761},[762],{"text":763,"type":29},"`TLSHandshakeTimeout` 过短会在跨境链路上频繁失败；",{"type":45,"children":765},[766],{"text":767,"type":29},"`MaxIdleConnsPerHost` 过低会导致高并发时连接被丢弃重建。",{"type":25,"children":769},[770],{"text":771,"type":29},"观测方式：看 `httptrace.GotConn` 回调里 `info.Reused` 的取值，统计连续请求中的复用比例。复用比例受请求模式、连接池和服务端行为影响，不设统一阈值；将升级前后相同负载下的结果对照，异常时再检查配置和默认值。",{"type":31,"level":32,"children":773},[774],{"text":775,"type":29},"升级后 HTTP\u002F2 或 TLS 协商有什么变化？",{"type":25,"children":777},[778],{"text":779,"type":29},"Go 版本升级可能改变 HTTP\u002F2 启用条件和 TLS 默认值，判断时以 Go 客户端自己的结果为准。",{"type":41,"format":42,"children":781},[782,786,790],{"type":45,"children":783},[784],{"text":785,"type":29},"自定义 `TLSClientConfig` 可能关闭 HTTP\u002F2；",{"type":45,"children":787},[788],{"text":789,"type":29},"`ForceAttemptHTTP2` 的设置会影响协商结果；",{"type":45,"children":791},[792],{"text":793,"type":29},"最低 TLS 版本提升后，旧代理或中间设备可能握手失败。",{"type":25,"children":795},[796],{"text":797,"type":29},"以 Go 的 `resp.Proto`、`resp.TLS.NegotiatedProtocol`（TLS 响应时）及客户端日志作为 Go 实际协商结果。curl 只能作显式相同代理、目标和路径下的独立对照，不能代表 Go 客户端结果：",{"type":25,"children":799},[800],{"text":801,"type":29},"```bash\ncurl -sI -o \u002Fdev\u002Fnull -w '%{http_version}\\n' https:\u002F\u002Fapi.example.com\u002F\n```",{"type":25,"children":803},[804],{"text":805,"type":29},"该命令只显示 curl 自己的协商结果；Go 的结果以 `resp.Proto` 与 TLS 状态为准。两者不一致时，先确认代理、目标和请求路径相同，再检查 Go 版本、`tls.Config` 与代理隧道能力。",{"type":31,"level":32,"children":807},[808],{"text":809,"type":29},"怎么判断代理连接回归通过？",{"type":25,"children":811},[812],{"text":813,"type":29},"可按下表对照代理来源、分阶段耗时、连接复用、协议协商及超时重试。",{"type":41,"format":42,"children":815},[816,820,824,828,832],{"type":45,"children":817},[818],{"text":819,"type":29},"检查项：代理来源 ｜ 观测点：检查 `Transport.Proxy`、绕过规则及实际出口 ｜ 正常表现：与预期代理配置和出口一致",{"type":45,"children":821},[822],{"text":823,"type":29},"检查项：分阶段耗时 ｜ 观测点：`httptrace` 成对事件算出的各段时长 ｜ 正常表现：各段与升级前同一量级",{"type":45,"children":825},[826],{"text":827,"type":29},"检查项：连接复用 ｜ 观测点：`GotConn` 的 `Reused` ｜ 正常表现：与升级前相同负载下的基线对照",{"type":45,"children":829},[830],{"text":831,"type":29},"检查项：Go 协商协议 ｜ 观测点：`resp.Proto` 与 TLS 状态 ｜ 正常表现：按目标服务、代理路径和升级前基线核对",{"type":45,"children":833},[834],{"text":835,"type":29},"检查项：超时与重试 ｜ 观测点：三类超时的触发日志 ｜ 正常表现：各按预期触发，次数不变",{"type":25,"children":837},[838],{"text":839,"type":29},"如果实际出口与配置不符，先检查 `Transport.Proxy` 和绕过规则；若某阶段耗时变化，检查对应的连接与超时配置；HTTP\u002F2 协商结果变化时，再核对 Go 版本、TLS 配置和代理支持情况。",{"type":25,"children":841},[842],{"text":843,"type":29,"marks":844},"Go net\u002Fhttp 官方文档",[845],{"href":846,"type":138},"https:\u002F\u002Fpkg.go.dev\u002Fnet\u002Fhttp",{"type":31,"level":32,"children":848},[849],{"text":257,"type":29},{"type":25,"children":851},[852],{"text":853,"type":29,"marks":854},"Q：环境变量代理设置会自动覆盖自定义 Transport 吗？",[855],{"type":264},{"type":25,"children":857},[858],{"text":859,"type":29},"A：取决于 `Transport.Proxy` 是否显式设置及 Go 版本行为，应以实际配置和版本文档为准。",{"type":25,"children":861},[862],{"text":863,"type":29,"marks":864},"Q：测试关闭连接复用会代表线上表现吗？",[865],{"type":264},{"type":25,"children":867},[868],{"text":869,"type":29},"A：不会。它适合隔离连接问题，结果应与默认复用配置分开记录。",{"type":25,"children":871},[872],{"text":873,"type":29,"marks":874},"Q：只在一个 Go 版本通过是否足够？",[875],{"type":264},{"type":25,"children":877},[878],{"text":879,"type":29},"A：若生产使用多个版本，应覆盖实际部署版本及升级目标版本。",[881],{"source":6,"id":7,"documentId":8,"translationKey":8,"locale":9,"slug":10,"path":11,"_path":11,"title":12,"description":13,"excerpt":13,"image":14,"publishedAt":15,"updatedAt":16,"author":17,"tags":882,"featured":19,"sortOrder":20,"readingTime":21,"bodyType":22,"bodyBlocks":883,"bodyHtml":289},[],[884,887,890,893,910,913,916,919,936,939,942,945,956,959,966,969,972,983,986,989,992,1003,1010,1013,1016,1042,1045,1051,1054,1059,1062,1067,1070,1075],{"type":25,"children":885},[886],{"text":28,"type":29},{"type":31,"level":32,"children":888},[889],{"text":35,"type":29},{"type":25,"children":891},[892],{"text":39,"type":29},{"type":41,"format":42,"children":894},[895,898,901,904,907],{"type":45,"children":896},[897],{"text":48,"type":29},{"type":45,"children":899},[900],{"text":52,"type":29},{"type":45,"children":902},[903],{"text":56,"type":29},{"type":45,"children":905},[906],{"text":60,"type":29},{"type":45,"children":908},[909],{"text":64,"type":29},{"type":25,"children":911},[912],{"text":68,"type":29},{"type":31,"level":32,"children":914},[915],{"text":72,"type":29},{"type":25,"children":917},[918],{"text":76,"type":29},{"type":41,"format":42,"children":920},[921,924,927,930,933],{"type":45,"children":922},[923],{"text":82,"type":29},{"type":45,"children":925},[926],{"text":86,"type":29},{"type":45,"children":928},[929],{"text":90,"type":29},{"type":45,"children":931},[932],{"text":94,"type":29},{"type":45,"children":934},[935],{"text":98,"type":29},{"type":25,"children":937},[938],{"text":102,"type":29},{"type":31,"level":32,"children":940},[941],{"text":106,"type":29},{"type":25,"children":943},[944],{"text":110,"type":29},{"type":41,"format":42,"children":946},[947,950,953],{"type":45,"children":948},[949],{"text":116,"type":29},{"type":45,"children":951},[952],{"text":120,"type":29},{"type":45,"children":954},[955],{"text":124,"type":29},{"type":25,"children":957},[958],{"text":128,"type":29},{"type":25,"children":960},[961,962,965],{"text":132,"type":29},{"text":134,"type":29,"marks":963},[964],{"href":137,"type":138},{"text":140,"type":29},{"type":31,"level":32,"children":967},[968],{"text":144,"type":29},{"type":25,"children":970},[971],{"text":148,"type":29},{"type":41,"format":42,"children":973},[974,977,980],{"type":45,"children":975},[976],{"text":154,"type":29},{"type":45,"children":978},[979],{"text":158,"type":29},{"type":45,"children":981},[982],{"text":162,"type":29},{"type":25,"children":984},[985],{"text":166,"type":29},{"type":31,"level":32,"children":987},[988],{"text":170,"type":29},{"type":25,"children":990},[991],{"text":174,"type":29},{"type":41,"format":42,"children":993},[994,997,1000],{"type":45,"children":995},[996],{"text":180,"type":29},{"type":45,"children":998},[999],{"text":184,"type":29},{"type":45,"children":1001},[1002],{"text":188,"type":29},{"type":25,"children":1004},[1005,1006,1009],{"text":192,"type":29},{"text":194,"type":29,"marks":1007},[1008],{"href":197,"type":138},{"text":140,"type":29},{"type":31,"level":32,"children":1011},[1012],{"text":202,"type":29},{"type":25,"children":1014},[1015],{"text":206,"type":29},{"type":41,"format":42,"children":1017},[1018,1021,1024,1027,1030,1033,1036,1039],{"type":45,"children":1019},[1020],{"text":212,"type":29},{"type":45,"children":1022},[1023],{"text":216,"type":29},{"type":45,"children":1025},[1026],{"text":220,"type":29},{"type":45,"children":1028},[1029],{"text":224,"type":29},{"type":45,"children":1031},[1032],{"text":228,"type":29},{"type":45,"children":1034},[1035],{"text":232,"type":29},{"type":45,"children":1037},[1038],{"text":236,"type":29},{"type":45,"children":1040},[1041],{"text":240,"type":29},{"type":25,"children":1043},[1044],{"text":244,"type":29},{"type":25,"children":1046},[1047,1050],{"text":248,"type":29,"marks":1048},[1049],{"href":251,"type":138},{"text":253,"type":29},{"type":31,"level":32,"children":1052},[1053],{"text":257,"type":29},{"type":25,"children":1055},[1056],{"text":261,"type":29,"marks":1057},[1058],{"type":264},{"type":25,"children":1060},[1061],{"text":268,"type":29},{"type":25,"children":1063},[1064],{"text":272,"type":29,"marks":1065},[1066],{"type":264},{"type":25,"children":1068},[1069],{"text":278,"type":29},{"type":25,"children":1071},[1072],{"text":282,"type":29,"marks":1073},[1074],{"type":264},{"type":25,"children":1076},[1077],{"text":288,"type":29},null,[],[]]