第32章 排错决策树:502 / 504 / 握手失败 / 环路 / 泄漏
学习目标
- 掌握代理排错的总方法论:沿生命周期分段二分定位
- 备齐排错工具箱,知道每个工具看什么层
- 用一棵决策树,从症状快速定位到根因
- 把全书各章的故障场景收口成可操作的排查路径
前置知识
- 第03章 连接生命周期与阶段故障(本章的骨架)
- 全书各章的"排错"小节
原理
总方法论:分段二分
代理把一条连接拆成两段(第01章):客户端→代理 和 代理→源站。排错第一步永远是确定故障在哪一段:
客户端 ──①── 代理 ──②── 源站
在代理上直接 curl 源站:
通 → 问题在 ① 段(客户端到代理)
不通 → 问题在 ② 段(代理到源站)
然后沿 第03章 的生命周期阶段(DNS→连接→认证→连源站→转发→回写)逐段二分,用 curl -w 的分段计时锁定是哪一步慢/断。
工具箱:每个工具看哪一层
| 工具 | 看什么 | 用于 |
|---|---|---|
curl -v -w | 分段计时、请求/响应头 | 定位慢在哪段(第03章) |
tcpdump/tshark | 字节流、TLS 握手、SNI | 连不连得通、握手对不对(第01章/第02章) |
mitmproxy | 应用层明文 | 请求内容对不对(第18章) |
ss/netstat | 连接状态/数量 | CLOSE_WAIT、TIME_WAIT、连接泄漏 |
dig/nslookup | DNS 解析 | DNS 错/泄漏(第03章) |
openssl s_client | 证书链、ALPN、SNI | TLS 握手失败、MITM(第05章) |
| 代理日志 | 错误码、上游状态 | 代理视角的真相 |
排错决策树(从症状出发)
症状?
├─ 502 Bad Gateway ──────────▶ 代理→源站这一跳"坏了"
│ ├─ 代理上 curl 源站不通? → 源站宕/端口错/网络(②段,第03章)
│ ├─ DNS 解析失败? → 代理侧 DNS/内网域名(第03/08章)
│ └─ 源站返回非法响应? → 协议不匹配(如 h2 后端配成 h1,第07章)
│
├─ 504 Gateway Timeout ──────▶ 代理→源站"超时"
│ └─ 源站慢/负载高,或 read timeout 太短(第31章)
│
├─ 503 Service Unavailable ──▶ 无健康后端 或 限流
│ ├─ Envoy "UH"? → 无健康 upstream(第15章)
│ └─ 触发了限流/熔断? → stick table/ratelimit(第14/31章)
│
├─ 407 / 401 ────────────────▶ 认证
│ ├─ 407? → 代理认证缺 Proxy-Authorization(第04章)
│ └─ 401? → 源站认证(与代理无关)
│
├─ Connection refused ───────▶ 代理没起/端口错/防火墙(①段)
│
├─ TLS 握手失败 ─────────────▶ 证书/SNI/版本/MITM
│ ├─ "unable to get local issuer"? → 被 MITM 或缺 CA(第05章)
│ ├─ SNI 不匹配/拿不到? → 证书选错/ECH(第02/05章)
│ └─ 版本/算法不兼容? → TLS 版本协商
│
├─ CONNECT 403/405 ──────────▶ 端口白名单(Safe_ports,第04/17章)
│
├─ CPU 100% / 无限循环 ───────▶ 透明代理没放过自己(环路,第11章)
│
├─ CLOSE_WAIT 大量堆积 ───────▶ 半关闭处理不当/连接泄漏(第21章)
│
├─ 慢 / 尾延迟高 ─────────────▶ 连接池小/缓冲/后端/协调遗漏(第31章)
│
├─ DNS 泄漏(被看到访问谁)────▶ 本地 vs 远端解析(第03章 socks5 vs socks5h)
│
└─ 间歇性"串味"/响应错配 ─────▶ 请求走私(hop-by-hop 头,第04/33章)
️ 实现 / 命令
实验一:502 分段二分实战
# 第1步:在代理机上,绕过代理直接连源站——区分是哪一段坏的
curl -v http://源站IP:端口/ --resolve 源站域名:端口:源站IP
# 通 → 问题在"客户端→代理"段或代理配置;不通 → "代理→源站"段(源站/网络)
# 第2步:看代理日志的上游错误
tail -f /var/log/nginx/error.log # connect() failed / no live upstreams / SSL handshake...
# 第3步:DNS?在代理机上解析源站域名
dig +short 源站域名 # 解析不出 → 代理侧 DNS 问题(第03章)
实验二:用分段计时定位"慢"
curl -w "DNS:%{time_namelookup} 连接:%{time_connect} TLS:%{time_appconnect} 首字节:%{time_starttransfer} 总:%{time_total}\n" \
-s -o /dev/null -x http://proxy:3128 https://源站/
# 哪段数值大,瓶颈就在哪(第03章):
# TLS 大 → 握手/会话复用(第31章);首字节-TLS 大 → 源站处理慢(504 前兆)
实验三:连接状态体检
ss -tanp | awk '{print $1}' | sort | uniq -c # 各状态连接数
# 大量 CLOSE_WAIT → 应用没关连接(第21章半关闭)
# 大量 TIME_WAIT → 短连接太多(第31章,连接池/tw_reuse)
实验四:TLS 握手失败定位
openssl s_client -connect 源站:443 -servername 源站域名 </dev/null 2>&1 | head -20
# Verify return code 非 0 → 证书链问题
# issuer 是 mitmproxy/企业 CA → 被 MITM(第05章)
# no peer certificate / alert → SNI 不匹配或版本不兼容
排错速查(症状→首查项)
| 症状 | 第一步查 | 相关章 |
|---|---|---|
| 502 | 代理上直连源站 + 代理 error 日志 | 03/04 |
| 504 | 源站响应时间、read timeout | 31 |
| 503 | upstream 健康、限流配置 | 15/14 |
| 握手失败 | openssl s_client 看证书/SNI | 05 |
| 环路/CPU 满 | 透明代理 uid 排除规则 | 11 |
| CLOSE_WAIT 堆积 | 半关闭/连接关闭逻辑 | 21 |
| 慢/尾延迟 | curl -w 分段 + wrk2 | 31 |
| DNS 泄漏 | socks5 vs socks5h | 03 |
| 内部调用全超时 | NO_PROXY 是否含集群内 | 08/25 |
系统化的网络排错决策树参见 网络手册 · 故障排查方法论。
本章小结
- 排错第一性原理:先分段二分(代理上直连源站,判断故障在客户端→代理还是代理→源站),再沿生命周期定位阶段。
- 工具按层分工:
curl -w看分段时间、tcpdump看握手字节、openssl s_client看证书、ss看连接状态、mitmproxy看明文。 - 记住核心因果:502=代理→源站坏、504=超时、503=无后端/限流、环路=没放过自己、CLOSE_WAIT=半关闭。
最后一章 第33章 代理安全,看代理作为攻击面的全部风险:开放代理、SSRF、请求走私、凭证泄漏,及加固清单——全书收尾。