用途
本技能是一套“证据驱动”的网络故障调查方法论,适用于网络、流式传输和协议类问题的排查,也是未知根因情况下的通用兜底方法。它针对的场景是:明显原因很可能是错的,而基于单条日志或单个间接数据点就下诊断的冲动需要被刻意放慢。技能本身源自一个真实的约 5 小时生产案例(见 references/case-sse-rst-130s.md),在该案例中,堆叠假设浪费了数小时,而一个 10 分钟的分层实验本可解决。
症状分诊
技能建议先判断症状是否属于本仓库中已有专项技能的领域。若症状属于 macOS Tailscale 与代理/VPN 冲突、Cloudflare 配置问题、Windows App/AVD/W365 远程桌面连接质量、客户端代理/VPN/TUN 错误路由、TLS 证书校验错误、代理可通但大流量缓慢、整个智能家居/IoT 设备组离线等,应先使用对应专项技能(如 tunnel-doctor、cloudflare-troubleshooting、windows-remote-desktop-connection-doctor)。若都不匹配,或专项技能的证据指向别处,再回到本技能的通用方法论。
另外,对于大 POST 体(如 Content-Length 大于 1 MB)出现 Cloudflare 524/522 的情况,失败往往是上传到源站的时间超过了 Cloudflare 的源站读取超时,而不是后端缓慢,应先用上传与处理超时的区分清单。
核心原则
- 证据优于假设:无法指向具体产物(日志行、pcap 帧、探测输出、指标样本)就是在猜,而不是在诊断。提出“X 是原因”之前,必须能说出直接证据;没有就先加装观测或抓包。
- 证伪优于确认:N 个独立来源“确认”一个假设并不使其为真,一个证伪观察即可排除它。行动前要回答:什么观察会让我放弃这个假设?若答不上来,该假设不可证伪,不得驱动调查。
- 分层隔离:多跳系统(客户端 → CDN → LB → 反向代理 → 应用 → 上游)的缺陷集中在层与层的接缝处。当症状可能来自多层时,不要推理是哪层,而要测试:用同一逻辑请求走三条仅差一跳的路径,比较症状出现在哪里。该技术同样可用于隔离容量问题:改变技术栈而非改变单跳,用共享网络路径但不共享应用代码的两条通道测量同一方向,是避免调优一个从来不是瓶颈的应用的最廉价方式。注意只有当两条通道都慢时才能读作“路径问题”;两条都快说明探测未能复现症状,什么也证明不了。
- 提交前反方复核:在认定根因或发布修复前,让独立复核者挑战结论而非确认结论。对每个发现应用四问过滤器(见 references/counter-review-pattern.md)后再据此行动。
- 绿色健康检查只证明它所探测的那条路径:多平面系统(TUN 代理的 DIRECT 平面与代理平面、服务的数据面与控制面等)会一次坏一个平面,只探测另一个平面的看门狗会全程保持绿色。接受“监控说健康”作为证据前,要问该检查实际走哪条路径。检查也只能证明它足够大才测量得到的量:路径覆盖是一个维度,规模是另一个,吞吐崩塌往往藏在后一个维度里。存活探测返回几百字节,其耗时由握手和往返主导,在全天响应 40 ms 不变的同时,链路真实容量可能相差百倍。实用规则:只有当传输时间主导探测总时间时,探测才测量到链路。因此应按所需答案而非便利来设定探测大小;若探测远低于一秒返回,那几乎全是建立过程。用“按时间预算而非按字节”的方式(流式传输 N 秒,把到达量除以 N)可绕开大小选择问题,这也正是 Step 0.7 命令廉价的原因。
- 先穷尽工具再向用户提问:用户的观察是线索,不是要抛回去的问题。对其中每个开放子问题,先假定可观测并寻找工具:监控/探测台账、系统日志、同网络的控制通道(另一台主机、有线设备、热点对比,甚至路由器状态页)。只有三类情况允许中途问用户:决策权(生产变更、重启、花费等超出授权边界)、仪器没有捕获的物理动作与第一人称观察、你没有的凭据或访问权限。每交给用户一个问题,都要在报告中写一句说明为何没有仪器能回答它、属于哪一类;写不出这句,就说明还没穷尽,不该问。
工作流
技能提供一份可复制的调查进度清单:
- Step 0:界定症状(确切错误、确切时间、谁受影响谁不受、近期变化)
- Step 0.5:验证前提——直接证据是否表明症状确实在发生
- Step 0.6:大 POST 体区分上传超时与处理超时
- Step 0.7:若没有报错只是慢,先测速率,再做双通道测量
- Step 1:在假设之前收集每一跳的直接证据
- Step 2:提出至少 3 个假设,为每个写明什么能证伪它、干预针对哪个层边界
- Step 3:设计决定性实验(网络场景用分层隔离)
- Step 4:若证据缺口阻碍直接观察,加装观测
- Step 5:执行,记录实际与预测的差异
- Step 6:行动前反方复核
- Step 7:修复后复跑同一实验验证
- Step 8:把走过的弯路记录为教学材料
关键子步骤
Step 0 界定范围:需提取确切错误字符串(复制粘贴,不要转述;socket closed、ECONNRESET、HTTP/2 RST_STREAM INTERNAL_ERROR 各不相同)、确切时间戳(带时区的 ISO-8601)、可复现性、受影响与不受影响者、近期变更。要区分症状与诊断:“慢”不是症状,“请求耗时 130.898s 后返回 HTTP/2 INTERNAL_ERROR”才是。但也要量化:写成“在指定载荷大小下的字节每秒”(如 95 秒传输 8.4 MB = 0.09 MB/s,全程 HTTP 200)后,“慢”就成了 Step 0.7 可处理的症状。
Step 0.5 验证前提:可接受的前提包括带时间戳和错误串的日志行、浏览器 DevTools 网络面板截图、可按需复现的命令、显示错误计数上升的指标图;不充分的前提包括“用户说感觉慢”“告警响了但没查”“上周有人提到”。前提验证失败时,正确做法是加观测等待复发,而不是因沉没成本继续调查。
Step 0.6 上传超时与处理超时:对 Content-Length 大于约 500 KB 的 524/522/504,定位边缘/反向代理访问日志,比较 bytes_read 与 Content-Length:相等且报错更可能是后端处理问题;小于且连接在超时窗口附近关闭则是上传问题。检查 duration/request_time 语义(Caddy duration、nginx $request_time 为从读到首字节到响应结束的墙钟时间;上游 request_time 为收全 body 后的处理时间)。若代理 duration 接近超时而上游 request_time 很短或没有记录,瓶颈就在上传。status=0(Caddy)或 -(nginx)表示代理从未写出 HTTP 响应,通常是下游/客户端先关闭。若请求 ID/ray ID/trace ID 未出现在上游日志中,说明请求从未上传完成。上传超时签名示例:status 0、duration 125.0、bytes_read 4111422、Content-Length 6042141——代理保持连接 125 秒,读了 6 MB 中的 4.1 MB,随后 Cloudflare 关闭并返回 524。处理超时签名示例:status 504、duration 120.1、bytes_read 6042141、Content-Length 6042141——body 已完整上传,后端未在代理超时前响应。
限制
本技能提供的是调查方法与流程约束,不包含具体修复代码;文中提到的案例文件与参考文件路径(如 references/instrumentation-patterns.md、references/packet-capture-recipes.md、references/layered-isolation-experiment.md、references/counter-review-pattern.md、references/case-proxy-tun-cname-override.md、references/case-iot-router-migration-wpa3.md)属于该技能仓库内容。除文中所述外,其他功能、收费、价格、平台和支持情况暂未提供。