[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"blog-strapi-article:zh:proxy-error-handling-mock-testing":3,"blog-article:\u002Fblog\u002Fproxy-error-handling-mock-testing":660,"blog-related:\u002Fblog\u002Fproxy-error-handling-mock-testing":661,"blog-translations:\u002Fblog\u002Fproxy-error-handling-mock-testing":662},{"available":4,"article":5,"relatedArticles":150,"translations":572},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":149},"strapi",3986,"g5ee4zs0pa8hyuc307nk5nwf","zh","proxy-error-handling-mock-testing","\u002Fblog\u002Fproxy-error-handling-mock-testing","如何在不依赖真实代理服务时测试应用的代理错误处理","用可注入的代理桩和故障矩阵覆盖超时、拒绝、认证失败，在受控环境验证重试、退避与降级；不接真实代理，也不记录任何真实凭据。","https:\u002F\u002Fcms.quanqiudaili.com\u002Fuploads\u002Flarge_cover_5ce09c130c.webp","2026-11-06T07:42:03.722Z","2026-11-06T07:42:03.708Z","Global Proxies Team",[],false,0,2,"blocks",[24,37,42,46,50,54,58,62,66,70,74,86,90,94,105,109,128,132,137,141,145],{"type":25,"children":26},"paragraph",[27,30,35],{"text":28,"type":29},"代理错误处理最适合用本地 mock 验证。407、超时、连接重置、TLS 失败、慢响应、半关闭六类故障都能稳定复现，测试不再依赖服务商侧的偶发故障。","text",{"text":31,"type":29,"marks":32},"代理IP",[33],{"type":34},"bold",{"text":36,"type":29}," 的真实链路不可控，mock 的价值就是把“偶发”变成“可重复”，但结论只对应用逻辑成立。",{"type":38,"level":21,"children":39},"heading",[40],{"text":41,"type":29},"代理错误测试需要覆盖的六种故障",{"type":25,"children":43},[44],{"text":45,"type":29},"可先覆盖六种常见故障：让 mock 返回带 `Proxy-Authenticate` 的 407，检查客户端是否重试或报告认证错误；延迟超过客户端超时时，检查超时处理；连接重置时，检查中断后的动作；TLS 负例一次只改一个变量（未受信 CA、可信 CA 但 SAN 不对、证书过期），并保持校验开启，看客户端实际报什么错；分段延迟发送时，记录首字节和完整响应耗时；测“响应不完整”时，用声明了 `Content-Length` 却提前 EOF、或缺少 chunked 终止块的响应做负例，完整的 close-delimited 响应只作正常对照——单纯关闭写方向并不等于响应不完整。之后再按应用实际支持的错误分支补充测试。\n矩阵按应用实际支持的错误分支建立，上表是起步集合，可以补代理 DNS 失败、认证挑战格式异常和重试上限三类用例。矩阵未覆盖的分支，不会被这一组测试验证。",{"type":38,"level":21,"children":47},[48],{"text":49,"type":29},"如何搭建可重复触发故障的 mock 代理",{"type":25,"children":51},[52],{"text":53,"type":29},"mitmproxy、toxiproxy 或自建 TCP 服务都可以用于 mock，但注入点要和故障所在层对应。407 需要能处理 CONNECT 并返回 `Proxy-Authenticate` 的受控代理；TCP 延迟和连接重置可在传输层注入；SOCKS 认证错误则要按 SOCKS 协议构造，不能用 HTTP 的 407 代替。记录工具、客户端版本、触发步骤和清理方法，其他人才能复现。",{"type":38,"level":21,"children":55},[56],{"text":57,"type":29},"除了错误码，还要验证应用如何处理故障",{"type":25,"children":59},[60],{"text":61,"type":29},"收到 407 只说明应用识别到了代理认证错误。还要检查重试次数和间隔、熔断或降级是否符合预期，以及日志是否隐藏凭据。407 和 401 要分开判断：前者由代理要求认证，后者由目标服务要求认证。",{"type":25,"children":63},[64],{"text":65,"type":29},"407 和 401 必须分开断言：`Proxy-Authenticate` 是代理要求认证，`WWW-Authenticate` 是目标服务要求认证。混在一起，会把代理认证失败记成应用登录失败。",{"type":38,"level":21,"children":67},[68],{"text":69,"type":29},"避免 mock 测试漏掉真实故障分支",{"type":25,"children":71},[72],{"text":73,"type":29},"要触发客户端超时，mock 延迟必须超过客户端配置的超时值；要测试请求中断后的处理，应在连接建立后再重置。mock 结果只说明应用对模拟故障的处理方式，不能直接推断真实代理链路表现。",{"type":25,"children":75},[76,78,84],{"text":77,"type":29},"mock 通过后若要转入真实环境验证，可按",{"text":79,"type":29,"marks":80},"全球代理使用流程",[81],{"href":82,"type":83},"https:\u002F\u002Fhelp.quanqiudaili.com\u002Fdocs\u002FProcess","link",{"text":85,"type":29},"依次核对产品、参数和接入步骤。",{"type":38,"level":21,"children":87},[88],{"text":89,"type":29},"记录 mock 故障参数和应用处理结果",{"type":25,"children":91},[92],{"text":93,"type":29},"每个用例记录故障类型、注入参数、期望动作、实际结果，以及日志是否隐藏凭据。\n用例边界是应用逻辑，不覆盖真实服务商的 SLA 和地区行为。出现真实凭据进入 mock 日志、或测试影响到共享环境时，立即停止并清理。",{"type":25,"children":95},[96,98,103],{"text":97,"type":29},"开始真实环境验证前，可用",{"text":99,"type":29,"marks":100},"代理设置帮助",[101],{"href":102,"type":83},"https:\u002F\u002Fhelp.quanqiudaili.com\u002Fdocs\u002Fproxy-Settings",{"text":104,"type":29},"核对协议、地址和认证参数。",{"type":25,"children":106},[107],{"text":108,"type":29},"故障注入工具与协议定义：",{"type":110,"format":111,"children":112},"list","unordered",[113,121],{"type":114,"children":115},"list-item",[116],{"text":117,"type":29,"marks":118},"toxiproxy（TCP 延迟、重置、带宽限制）",[119],{"href":120,"type":83},"https:\u002F\u002Fgithub.com\u002FShopify\u002Ftoxiproxy",{"type":114,"children":122},[123],{"text":124,"type":29,"marks":125},"RFC 9110《HTTP Semantics》（407 与 `Proxy-Authenticate`）",[126],{"href":127,"type":83},"https:\u002F\u002Fwww.rfc-editor.org\u002Frfc\u002Frfc9110.html",{"type":38,"level":21,"children":129},[130],{"text":131,"type":29},"常见问题",{"type":38,"level":133,"children":134},3,[135],{"text":136,"type":29},"mock 代理能替代真实代理测试吗？",{"type":25,"children":138},[139],{"text":140,"type":29},"不能。它验证应用逻辑，不验证真实链路的稳定性和地区行为。",{"type":38,"level":133,"children":142},[143],{"text":144,"type":29},"407 和 401 有什么区别？",{"type":25,"children":146},[147],{"text":148,"type":29},"407 是代理要求认证，401 是目标服务要求认证。","",[151,284,401],{"source":6,"id":152,"documentId":153,"translationKey":153,"locale":9,"slug":154,"path":155,"_path":155,"title":156,"description":157,"excerpt":157,"image":158,"publishedAt":159,"updatedAt":160,"author":17,"tags":161,"featured":19,"sortOrder":20,"readingTime":21,"bodyType":22,"bodyBlocks":162,"bodyHtml":149},3987,"ehyrf4ygtsshhwkd0o87xat6","tls-certificate-compatibility-proxy-testing","\u002Fblog\u002Ftls-certificate-compatibility-proxy-testing","应用发布前，如何检查代理环境下的 TLS 证书兼容性","先分清直连、隧道转发和代理中间人三类链路，再逐级核对证书链、信任库位置与 SAN；本机正常而容器失败的，多为 CA 没进镜像。","https:\u002F\u002Fcms.quanqiudaili.com\u002Fuploads\u002Flarge_TLS_cover_ad09ec52a5.webp","2026-11-06T07:43:24.293Z","2026-11-06T07:43:24.278Z",[],[163,172,176,180,189,193,197,201,205,209,213,217,221,225,229,233,237,241,252,256,265,268,272,276,280],{"type":25,"children":164},[165,167,170],{"text":166,"type":29},"代理环境下的 TLS 问题，先要分清证书由谁签发：直连看目标证书，隧道转发看目标证书，代理中间人看代理自签 CA。",{"text":31,"type":29,"marks":168},[169],{"type":34},{"text":171,"type":29}," 本身不改变 TLS 握手，但企业代理或抓包工具会替换证书链，发布前必须把这条链路完整验一遍。",{"type":38,"level":21,"children":173},[174],{"text":175,"type":29},"先确认代理是否会替换目标站的 TLS 证书",{"type":25,"children":177},[178],{"text":179,"type":29},"直连或 HTTP CONNECT 隧道通常保留目标站点的证书，应检查证书链、有效期和 SAN（证书允许使用的域名）。如果企业代理会解密并重新签发 TLS 流量，客户端看到的是企业代理签发的站点证书；此时要确认该证书能连到受信任的企业 CA。先判断代理是否替换了证书，再确定检查对象。",{"type":25,"children":181},[182,184,187],{"text":183,"type":29},"核对代理协议与客户端接入方式时，可参考",{"text":99,"type":29,"marks":185},[186],{"href":102,"type":83},{"text":188,"type":29},"。",{"type":38,"level":21,"children":190},[191],{"text":192,"type":29},"使用 OpenSSL 查看代理连接返回的证书链",{"type":25,"children":194},[195],{"text":196,"type":29},"```bash\nopenssl s_client -proxy proxy.example.com:8080 \\\n-connect target.example.com:443 \\\n-servername target.example.com -showcerts \\\n-verify_hostname target.example.com -verify_return_error \\\n-CAfile \u002Fpath\u002Fto\u002Fca-bundle.pem\n```",{"type":25,"children":198},[199],{"text":200,"type":29},"查看输出中的 `issuer`、有效期和 SAN，并重点看两件事：证书链能否构建到受信锚、主机名是否匹配。服务端通常只提供叶证书和中间证书，不要求它回传根证书；根证书应该来自你信任的本地信任库（`-CAfile` 或 `-CApath`），而不是从连接里“捡”到一个就信。验收看退出码和验证输出（`Verify return code`），不要只看“连接成功”；最后仍在真实应用运行时或容器里做一次正负对照。",{"type":25,"children":202},[203],{"text":204,"type":29},"`-proxy` 参数需要 OpenSSL 1.1.0 或更高版本，且默认只支持 HTTP CONNECT；SOCKS5 场景改用支持该协议的工具（如 curl 的 `--proxy socks5h:\u002F\u002F`）或对应的客户端验证。运行 `openssl version` 可先检查本机版本。",{"type":38,"level":21,"children":206},[207],{"text":208,"type":29},"检查系统、运行时和容器使用的证书信任库",{"type":25,"children":210},[211],{"text":212,"type":29},"信任库位置取决于应用使用的运行时：Linux 系统证书通常位于 `\u002Fetc\u002Fssl\u002Fcerts`，Java 使用自己的 `cacerts`，Node 可通过 `NODE_EXTRA_CA_CERTS` 指定额外 CA，Python 的 `requests` 常使用 `certifi`。容器还要单独检查镜像内的证书。本机正常而容器失败时，先比较两边实际加载的 CA。",{"type":38,"level":21,"children":214},[215],{"text":216,"type":29},"根据 TLS 错误定位证书链和主机名问题",{"type":25,"children":218},[219],{"text":220,"type":29},"出现 `unable to get local issuer certificate` 时，先检查证书链是否缺少中间 CA；出现 `self signed certificate` 时，确认它是否为预期的企业 CA 或测试证书。`hostname mismatch` 通常表示证书 SAN 不包含目标域名；`protocol version` 或 `cipher mismatch` 则需要核对客户端与服务端支持的 TLS 版本和加密套件。",{"type":38,"level":21,"children":222},[223],{"text":224,"type":29},"发布前检查证书有效期、信任链和轮换结果",{"type":38,"level":133,"children":226},[227],{"text":228,"type":29},"在预发布环境验证证书更新和轮换",{"type":25,"children":230},[231],{"text":232,"type":29},"发布前记录证书到期时间，证书轮换后复测一次；灰度环境的信任库要和生产一致，否则灰度通过、生产失败。",{"type":38,"level":133,"children":234},[235],{"text":236,"type":29},"记录证书签发者、有效期和信任库位置",{"type":25,"children":238},[239],{"text":240,"type":29},"发布前记录目标域名和端口、证书签发者与到期时间、信任库位置，以及代理是否会重新签发证书。\n检查清单四项：链能构建到受信锚、CA 在信任库、主机名匹配 SAN、证书未过期。任一项不满足就不能发布；发现证书替换影响生产流量时，立即回滚配置。",{"type":25,"children":242},[243,245,250],{"text":244,"type":29},"证书链异常仍无法归因时，可从",{"text":246,"type":29,"marks":247},"全球代理帮助中心",[248],{"href":249,"type":83},"https:\u002F\u002Fhelp.quanqiudaili.com\u002F",{"text":251,"type":29},"查找对应接入文档。",{"type":25,"children":253},[254],{"text":255,"type":29},"OpenSSL 参数以手册为准：",{"type":110,"format":111,"children":257},[258],{"type":114,"children":259},[260],{"text":261,"type":29,"marks":262},"`openssl s_client` 手册（`-proxy`、`-verify_hostname`、`-verify_return_error`）",[263],{"href":264,"type":83},"https:\u002F\u002Fdocs.openssl.org\u002F3.5\u002Fman1\u002Fopenssl-s_client\u002F",{"type":38,"level":21,"children":266},[267],{"text":131,"type":29},{"type":38,"level":133,"children":269},[270],{"text":271,"type":29},"代理环境下证书报错一定是代理问题吗？",{"type":25,"children":273},[274],{"text":275,"type":29},"不一定。先看证书链和信任库，主机名不符或 CA 缺失更常见。",{"type":38,"level":133,"children":277},[278],{"text":279,"type":29},"为什么本机正常、容器里失败？",{"type":25,"children":281},[282],{"text":283,"type":29},"容器镜像没有导入同一个 CA，信任库不一致。",{"source":6,"id":285,"documentId":286,"translationKey":286,"locale":9,"slug":287,"path":288,"_path":288,"title":289,"description":290,"excerpt":290,"image":291,"publishedAt":292,"updatedAt":293,"author":17,"tags":294,"featured":19,"sortOrder":20,"readingTime":21,"bodyType":22,"bodyBlocks":295,"bodyHtml":149},3985,"bpamp8c4gnpz3p9kslssa97y","ci-runner-proxy-build-impact-testing","\u002Fblog\u002Fci-runner-proxy-build-impact-testing","如何在 CI Runner 中验证代理不会影响构建步骤","在 Runner 内固定镜像、构建输入与测试时段，用出口 IP、DNS、TLS 和依赖清单作为证据，区分代理影响与构建环境波动；产物哈希只在构建可复现时才作对照。","https:\u002F\u002Fcms.quanqiudaili.com\u002Fuploads\u002Flarge_CI_Runner_cover_023a325e36.webp","2026-11-06T07:41:47.066Z","2026-11-06T07:41:47.049Z",[],[296,300,304,308,312,316,325,329,333,337,341,345,349,353,362,366,382,385,389,393,397],{"type":25,"children":297},[298],{"text":299,"type":29},"判断代理是否影响 CI，不能只看最终构建是否成功。应按实际流程检查代码拉取、依赖安装、单元测试、构建和产物上传。代理配置可能来自环境变量、包管理器或容器后台服务；配置不一致时，本地命令与流水线可能表现不同。",{"type":38,"level":21,"children":301},[302],{"text":303,"type":29},"如何判断代理没有影响 CI 构建",{"type":25,"children":305},[306],{"text":307,"type":29},"按顺序检查代码是否拉取到相同 commit、依赖校验是否通过、单元测试结果是否与基线一致。构建可复现时比较产物哈希；否则比较关键文件和依赖清单。上传环节要确认远端文件实际存在，并在支持时核对摘要或完整性校验。各阶段记录退出码和耗时；确需记录出口 IP 时，应使用受控日志并隐藏代理凭据。",{"type":38,"level":21,"children":309},[310],{"text":311,"type":29},"检查 CI 环境变量、包管理器和容器的代理配置",{"type":25,"children":313},[314],{"text":315,"type":29},"先检查构建进程是否读取 `HTTP_PROXY` 和 `HTTPS_PROXY`，再确认 `NO_PROXY` 中哪些地址会绕过代理。npm、pip 和 Maven 也可能分别读取 `.npmrc`、`pip.conf` 或 `settings.xml`；容器拉取镜像则由容器后台服务的配置决定。不要只凭宿主机环境变量判断代理已经生效。",{"type":25,"children":317},[318,320,323],{"text":319,"type":29},"配置入口和协议参数可对照",{"text":99,"type":29,"marks":321},[322],{"href":102,"type":83},{"text":324,"type":29},"逐项核实。",{"type":38,"level":21,"children":326},[327],{"text":328,"type":29},"对比启用代理和不启用代理时的构建结果",{"type":25,"children":330},[331],{"text":332,"type":29},"在相同 commit、Runner 镜像、依赖来源和资源规格下，分别运行启用代理与不启用代理的任务。冷缓存和暖缓存应分开比较，并使用隔离的缓存目录；每组可重复运行并保留原始结果。除代理开关外，其他条件尽量保持一致，便于判断差异来源。",{"type":38,"level":21,"children":334},[335],{"text":336,"type":29},"区分正常的耗时变化与构建产物差异",{"type":25,"children":338},[339],{"text":340,"type":29},"代理会让下载耗时发生变化，但不能预设“代理一定更慢”。判断是否影响构建，用各阶段耗时分布和事前设定的预算，不用单次耗时下结论；产物对照的前提是构建可复现、非确定性元数据已规范化；做不到时改比依赖清单、测试结果、关键文件和构建日志，不能仅凭整体哈希变化判定代理造成回归。",{"type":25,"children":342},[343],{"text":344,"type":29},"两类误判最常见：`NO_PROXY` 漏配，内部制品库被送进代理，出现 407 或超时；缓存掩盖失败，上一次的缓存让本次跳过了真实下载，清缓存复跑才暴露问题。",{"type":38,"level":21,"children":346},[347],{"text":348,"type":29},"记录 CI 代理测试结果并设定验收条件",{"type":25,"children":350},[351],{"text":352,"type":29},"记录 Runner 镜像、commit、实际生效的代理配置和各阶段结果，并注明缓存命中情况。被缓存跳过的步骤应标为未覆盖，必要时单独补跑。所有相关阶段通过后，再依据构建是否可复现判断产物差异。若影响其他流水线、日志出现凭据或耗时超过预算，应停止并回滚配置。",{"type":25,"children":354},[355,357,360],{"text":356,"type":29},"CI 中出现连接失败时，可从",{"text":246,"type":29,"marks":358},[359],{"href":249,"type":83},{"text":361,"type":29},"查找官方接入说明。",{"type":25,"children":363},[364],{"text":365,"type":29},"相关机制说明：",{"type":110,"format":111,"children":367},[368,375],{"type":114,"children":369},[370],{"text":371,"type":29,"marks":372},"`npm ci` 的安装与校验行为",[373],{"href":374,"type":83},"https:\u002F\u002Fdocs.npmjs.com\u002Fcli\u002Fv10\u002Fcommands\u002Fnpm-ci",{"type":114,"children":376},[377],{"text":378,"type":29,"marks":379},"GitHub Actions 依赖缓存",[380],{"href":381,"type":83},"https:\u002F\u002Fdocs.github.com\u002Factions\u002Fusing-workflows\u002Fcaching-dependencies-to-speed-up-workflows",{"type":38,"level":21,"children":383},[384],{"text":131,"type":29},{"type":38,"level":133,"children":386},[387],{"text":388,"type":29},"CI 里设了 HTTP_PROXY 为什么还是不走代理？",{"type":25,"children":390},[391],{"text":392,"type":29},"可能被 NO_PROXY 覆盖，或包管理器有独立配置，以实际生效的配置为准。",{"type":38,"level":133,"children":394},[395],{"text":396,"type":29},"构建成功就说明代理没影响吗？",{"type":25,"children":398},[399],{"text":400,"type":29},"不一定。缓存可能掩盖下载失败，需要清缓存复跑并比对产物。",{"source":6,"id":402,"documentId":403,"translationKey":403,"locale":9,"slug":404,"path":405,"_path":405,"title":406,"description":407,"excerpt":407,"image":408,"publishedAt":409,"updatedAt":410,"author":17,"tags":411,"featured":19,"sortOrder":20,"readingTime":21,"bodyType":22,"bodyBlocks":412,"bodyHtml":149},3984,"m0b3jqfcdf1s1zqgr35fnvwb","realtime-notification-end-to-end-latency-testing","\u002Fblog\u002Frealtime-notification-end-to-end-latency-testing","如何验证实时通知从服务端到客户端的端到端延迟","把延迟拆成 t0—t4 五个打点，跨机先校时钟，再按排队、传输、渲染分段归因；报告看 p95\u002Fp99，样本有限时给原始分布与中位数，不把 p99 当确定结论。","https:\u002F\u002Fcms.quanqiudaili.com\u002Fuploads\u002Flarge_cover_60978285a9.webp","2026-11-06T07:41:22.716Z","2026-11-06T07:41:22.699Z",[],[413,422,426,434,442,450,458,466,470,474,478,482,490,498,511,515,519,523,527,531,540,544,553,556,560,564,568],{"type":25,"children":414},[415,417,420],{"text":416,"type":29},"端到端延迟要先定义成一条时间线：事件生成、入队、发出、客户端收到、渲染完成五个点。只报“总耗时”无法判断慢在排队、传输还是渲染。",{"text":31,"type":29,"marks":418},[419],{"type":34},{"text":421,"type":29}," 只影响传输段，所以必须先把排队和渲染从总时延里拆出来，否则会把服务端排队误判成代理慢。",{"type":38,"level":21,"children":423},[424],{"text":425,"type":29},"用 t0 到 t4 标记实时通知的端到端耗时",{"type":25,"children":427},[428,432],{"text":429,"type":29,"marks":430},"t0：",[431],{"type":34},{"text":433,"type":29}," 表示事件生成，由服务端业务逻辑记录。",{"type":25,"children":435},[436,440],{"text":437,"type":29,"marks":438},"t1：",[439],{"type":34},{"text":441,"type":29}," 表示进入发送队列，由服务端记录。",{"type":25,"children":443},[444,448],{"text":445,"type":29,"marks":446},"t2：",[447],{"type":34},{"text":449,"type":29}," 表示写入连接，由服务端记录。",{"type":25,"children":451},[452,456],{"text":453,"type":29,"marks":454},"t3：",[455],{"type":34},{"text":457,"type":29}," 表示客户端收到，由客户端记录。",{"type":25,"children":459},[460,464],{"text":461,"type":29,"marks":462},"t4：",[463],{"type":34},{"text":465,"type":29}," 表示渲染完成，由客户端记录。\n端到端是 t4−t0，入队前是 t1−t0（事件生成到入队），排队是 t2−t1，传输是 t3−t2，渲染是 t4−t3；各段之和应当等于总时延。缺中间打点时，端到端仍可报，但要把总时延标为“未分解”；只有 t0 或 t4 缺失，才不能谈端到端，只能谈某一段。",{"type":38,"level":21,"children":467},[468],{"text":469,"type":29},"测量跨服务器延迟前先校准系统时钟",{"type":25,"children":471},[472],{"text":473,"type":29},"服务端和客户端的时钟不同步时，t3−t2 可能算出负数。先同步 NTP，或记录两台机的 offset 及测量不确定度；只有不确定度显著低于目标延迟时，才报跨机单向时延，否则用同机时钟或往返时延，并在结论里标注限制。",{"type":38,"level":21,"children":475},[476],{"text":477,"type":29},"分别测量通知排队、网络传输和客户端渲染时间",{"type":25,"children":479},[480],{"text":481,"type":29},"排队突增看服务端队列长度和消费速度；传输突增看 RTT、代理链路和带宽；渲染突增看客户端主线程和长任务。三段一起涨，通常是服务端或网络整体变化；只有传输段涨，才优先怀疑出口。",{"type":25,"children":483},[484,488],{"text":485,"type":29,"marks":486},"排队段：",[487],{"type":34},{"text":489,"type":29}," 主要受队列、消费速度、突发流量影响，先检查队列长度与积压时间。",{"type":25,"children":491},[492,496],{"text":493,"type":29,"marks":494},"传输段：",[495],{"type":34},{"text":497,"type":29}," 即 t3−t2，更准确的说法是“写入连接至客户端收到回调的间隔”；受 RTT、代理、带宽、TLS 握手，以及客户端缓冲和事件循环调度影响，握手只在新建或重连时计入。",{"type":25,"children":499},[500,504,506,509],{"text":501,"type":29,"marks":502},"渲染段：",[503],{"type":34},{"text":505,"type":29}," 主要受主线程、长任务、批量更新影响，先检查客户端帧率与任务耗时。\n设置代理对照组时，可先按",{"text":99,"type":29,"marks":507},[508],{"href":102,"type":83},{"text":510,"type":29},"核对接入参数。",{"type":38,"level":21,"children":512},[513],{"text":514,"type":29},"使用 p50、p95 和 p99 比较通知延迟",{"type":25,"children":516},[517],{"text":518,"type":29},"均值受长尾影响，但均值本身不能展示长尾分布。报告至少给出 p50、p95、p99，并写清样本量、采样窗口和分位数置信限制；样本有限时给原始分布与中位数，标注不稳定，不把 p99 当确定结论，也不设通用的条数门槛。分位数要注明针对哪类样本（全部通知、成功送达、还是超时也算），否则不同人报的 p95 不可比。",{"type":38,"level":21,"children":520},[521],{"text":522,"type":29},"对照直连和代理链路并记录延迟结果",{"type":25,"children":524},[525],{"text":526,"type":29},"取直连基线时，保持同服务端、同时段、同事件流，只改出口。不能直连就注明“缺少对照”，不要默认代理无影响。",{"type":25,"children":528},[529],{"text":530,"type":29},"记录样本量和采样时间、t0 至 t4 的分段耗时、各分位数、是否有直连对照及时钟偏差。每条通知带唯一的 event\u002Ftrace ID，能把服务端记录与客户端记录对上；t4 要写明是 DOM 更新完成、下一帧还是可观测呈现，三种口径的数值不同。记录字段里区分“应投递、已收到、超时、丢失”和观察截止时间。\n验收看 p95 是否在预算内，且突增能落到具体分段。测试影响到生产订阅者或通知量超出预算时立即停止。",{"type":25,"children":532},[533,535,538],{"text":534,"type":29},"第一次配置代理对照实验时，可参考",{"text":79,"type":29,"marks":536},[537],{"href":82,"type":83},{"text":539,"type":29},"了解从获取代理到接入测试的步骤。",{"type":25,"children":541},[542],{"text":543,"type":29},"前端计时口径可参考：",{"type":110,"format":111,"children":545},[546],{"type":114,"children":547},[548],{"text":549,"type":29,"marks":550},"MDN Performance API",[551],{"href":552,"type":83},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fdocs\u002FWeb\u002FAPI\u002FPerformance_API",{"type":38,"level":21,"children":554},[555],{"text":131,"type":29},{"type":38,"level":133,"children":557},[558],{"text":559,"type":29},"端到端延迟和网络延迟有什么区别？",{"type":25,"children":561},[562],{"text":563,"type":29},"端到端包含服务端排队和客户端渲染，网络延迟只是传输一段。",{"type":38,"level":133,"children":565},[566],{"text":567,"type":29},"为什么不能只看平均值？",{"type":25,"children":569},[570],{"text":571,"type":29},"均值会被长尾拉高或拉低，但不能说明分布形状；p95\u002Fp99 才能反映大多数用户的实际体验。",[573],{"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":574,"featured":19,"sortOrder":20,"readingTime":21,"bodyType":22,"bodyBlocks":575,"bodyHtml":149},[],[576,583,586,589,592,595,598,601,604,607,610,617,620,623,630,633,645,648,651,654,657],{"type":25,"children":577},[578,579,582],{"text":28,"type":29},{"text":31,"type":29,"marks":580},[581],{"type":34},{"text":36,"type":29},{"type":38,"level":21,"children":584},[585],{"text":41,"type":29},{"type":25,"children":587},[588],{"text":45,"type":29},{"type":38,"level":21,"children":590},[591],{"text":49,"type":29},{"type":25,"children":593},[594],{"text":53,"type":29},{"type":38,"level":21,"children":596},[597],{"text":57,"type":29},{"type":25,"children":599},[600],{"text":61,"type":29},{"type":25,"children":602},[603],{"text":65,"type":29},{"type":38,"level":21,"children":605},[606],{"text":69,"type":29},{"type":25,"children":608},[609],{"text":73,"type":29},{"type":25,"children":611},[612,613,616],{"text":77,"type":29},{"text":79,"type":29,"marks":614},[615],{"href":82,"type":83},{"text":85,"type":29},{"type":38,"level":21,"children":618},[619],{"text":89,"type":29},{"type":25,"children":621},[622],{"text":93,"type":29},{"type":25,"children":624},[625,626,629],{"text":97,"type":29},{"text":99,"type":29,"marks":627},[628],{"href":102,"type":83},{"text":104,"type":29},{"type":25,"children":631},[632],{"text":108,"type":29},{"type":110,"format":111,"children":634},[635,640],{"type":114,"children":636},[637],{"text":117,"type":29,"marks":638},[639],{"href":120,"type":83},{"type":114,"children":641},[642],{"text":124,"type":29,"marks":643},[644],{"href":127,"type":83},{"type":38,"level":21,"children":646},[647],{"text":131,"type":29},{"type":38,"level":133,"children":649},[650],{"text":136,"type":29},{"type":25,"children":652},[653],{"text":140,"type":29},{"type":38,"level":133,"children":655},[656],{"text":144,"type":29},{"type":25,"children":658},[659],{"text":148,"type":29},null,[],[]]