附录:速查表、检查清单与抓包手册
这三份附录,是给你放在工位旁边、随时翻阅的”工具手册”。不是用来读的,是用来查的。
附录 A:场景 → 知识点速查表
用法:找到你遇到的”现象”,跳到对应章节和知识点。
一、文件与数据传输
| 现象 | 根因 | 知识点 | 章节 |
|---|---|---|---|
| 大文件上传到一半失败 | 没处理边界/超时 | Content-Length vs chunked | 02 |
| 下载文件浏览器直接打开 | 缺 Content-Disposition | attachment | 02 |
| 做不了断点续传 | 没支持 Range | 206 + Content-Range | 04、10 |
| 上传到一半才被拒 | 没先问 | Expect: 100-continue | 04 |
| 传了 5MB 报 unexpected EOF | 长度承诺没兑现 | Content-Length 是承诺 | 02 |
二、登录与鉴权
| 现象 | 根因 | 知识点 | 章节 |
|---|---|---|---|
| 登录后一刷新就掉线 | session 存了服务器内存 | 无状态 + 共享存储 | 09 |
| 跨域带不上 Cookie | SameSite/作用域 | Domain/Path/SameSite | 09 |
| JS 读不到 Cookie | HttpOnly 是特性 | 防 XSS | 09 |
| 加机器后登录态丢失 | 状态存本地 | Redis 集中存储 | 09 |
| 用户 A 看到 B 的数据 | 缓存污染 | Vary: Cookie | 08 |
三、性能
| 现象 | 根因 | 知识点 | 章节 |
|---|---|---|---|
| 本地快线上慢 | 握手成本 | RTT、TCP+TLS 握手 | 07 |
| 第一个请求慢,后面快 | 建连 + 慢启动 | 连接复用 | 07 |
| 加机器没用 | 队头阻塞/下游瓶颈 | HOL | 07、12 |
| QPS 卡在某个数 | 连接池太小 | MaxIdleConnsPerHost | 07 |
| too many open files | 连接泄漏 | 读 body + Close | 07 |
四、实时通信
| 现象 | 根因 | 知识点 | 章节 |
|---|---|---|---|
| 轮询打垮服务器 | 无效请求太多 | 长轮询/SSE/WebSocket | 14 |
| WebSocket 连几分钟就断 | 中间设备空闲超时 | 心跳 | 14 |
| Nginx 后 WebSocket 连不上 | 没放行 Upgrade | 101 升级 | 14 |
| 并发写 panic | WebSocket 不支持并发写 | 写泵 | 14 |
五、幂等与重复
| 现象 | 根因 | 知识点 | 章节 |
|---|---|---|---|
| 双击下两单 | 操作不幂等 | 幂等性 | 03 |
| 回调重试重复扣款 | 服务端没幂等 | Idempotency-Key | 03 |
| 重试风暴 | 没退避/抖动 | 指数退避 + jitter | 03 |
| GET 删除数据 | GET 有副作用 | 安全方法 | 03 |
六、缓存
| 现象 | 根因 | 知识点 | 章节 |
|---|---|---|---|
| 发新版看不到 | 缓存没失效 | 强缓存/协商缓存 | 08 |
| 什么都没配却被缓存 | 启发式缓存 | (Date-Last-Modified)×10% | 08 |
| 用户看到别人数据 | 缓存污染 | private + Vary | 08 |
| CDN 不回源 | 缓存键问题 | URL 版本化 | 08 |
七、跨域(CORS)
| 现象 | 根因 | 知识点 | 章节 |
|---|---|---|---|
| 上线就报 CORS | 同源策略(浏览器) | 不是服务端问题 | 05 |
| 多出 OPTIONS 请求 | 预检 | 非简单请求 | 05 |
| 带自定义头就报错 | 没 Allow-Headers | Access-Control | 05 |
八、状态码与错误
| 现象 | 根因 | 知识点 | 章节 |
|---|---|---|---|
| 监控一片绿但用户炸了 | 全返回 200 | 状态码语义 | 04 |
| 502/503/504 分不清 | 网关/上游/超时 | 精确语义 | 04 |
| 刷新 POST 重复提交 | 没用 303 | PRG 模式 | 04 |
| 限流不知道等多久 | 没 Retry-After | 429 + Retry-After | 04 |
九、安全
| 现象 | 根因 | 知识点 | 章节 |
|---|---|---|---|
| token 明文裸奔 | 没 HTTPS | 认证≠加密 | 11 |
| base64 当加密 | 编码≠加密 | Basic | 11 |
| 请求被重放 | 没防重放 | nonce + timestamp | 11 |
| 中间人攻击 | InsecureSkipVerify | TLS 身份认证 | 11 |
| 降级攻击 | 没 HSTS | Strict-Transport-Security | 11 |
十、演进与兼容
| 现象 | 根因 | 知识点 | 章节 |
|---|---|---|---|
| 升级 h2 老客户端崩 | 不兼容变更 | 版本号/协商 | 15 |
| 加字段老客户端崩 | 没向后兼容 | 只增不改 | 15 |
| 中间设备拦新流量 | 中间件僵化 | GREASE/换地基 | 13、15 |
附录 B:协议设计检查清单(21 条)
用法:设计任何协议,按这 21 条逐项过一遍。它浓缩了全书所有原则。
一、地基(不变量)—— 这些一旦定错,改不动
- ☐ 边界方案明确吗?(长度前缀 / 分隔符 / 关闭连接)接收方怎么知道”报文到哪结束”?
- ☐ 字节序定死了吗?(大端/小端)多字节数字双方解析一致吗?
- ☐ 版本号位置固定吗? 接收方能”先读版本号,再决定怎么解析”吗?
- ☐ 有防误连的标识吗?(魔数/握手验证)能挡住”误连的、不懂协议的设备”吗?
二、语义 —— 每一层表达自己那层的语义
- ☐ 错误模型分类了吗? 第一位是不是”能不能重试”?
- ☐ 幂等与去重设计了吗? 重复请求会被重复执行吗?
- ☐ 超时语义清晰吗? deadline 传递了吗?还是每跳各自超时?
- ☐ 每个操作的语义明确吗? 有没有”语义含糊”的操作(像 POST 那样)?
三、扩展 —— 扩展点的精髓在”降级”
- ☐ 扩展点预留了吗?(版本/标志位/新类型/TLV)未来能加东西吗?
- ☐ 每个扩展点配了”优雅降级”吗? 老实现遇到新东西会崩溃吗?
- ☐ 不变量和扩展点区分清楚了吗? 哪些是”被依赖做决策”的地基,哪些是”被传递”的扩展点?
四、可靠 —— 弱网/异常下的表现
- ☐ 有序号/去重/补发机制吗? 消息会丢、会重、会乱序吗?
- ☐ 有重连策略吗?(心跳、退避、抖动)断线了怎么办?
- ☐ 有多路复用的话,做流控了吗? 某个流会饿死别的流吗?
五、安全 —— 分层安全,各管一段
- ☐ 认证了吗?(握手 + 凭证)知道对方是谁吗?
- ☐ 防篡改了吗?(签名/HMAC)内容被改能发现吗?
- ☐ 防重放了吗?(时间戳 + nonce)抓包重发能防住吗?
- ☐ 传输加密了吗? 内容会被窃听吗?
六、工程 —— 能落地 > 正确
- ☐ 可调试吗? 有日志、抓包工具吗?
- ☐ 可测试吗? 能 mock、能 fuzz 吗?
- ☐ 有文档吗? 协议规范 + 版本历史,新人能看懂吗?
最后,第 0 条(最重要):
☐ 你论证过”为什么不直接用现成的方案”吗? 造轮子的正当理由,要么”覆盖不到我的场景”,要么”我的差异化优势足够大”。答不上来,就先用现成的。
附录 C:抓包实战手册
用法:遇到”诡异问题”,按这个手册抓包定位。工具三个:curl(简单)、Wireshark(深入)、mitmproxy(看 HTTPS 明文)。
一、curl:最快的原始报文查看
# 看完整请求和响应(含首部)
curl -v https://example.com/api/user
# 只看响应头
curl -I https://example.com/api/user
# 看连接和 TLS 细节(握手、ALPN 协商结果)
curl -v --http2 https://example.com 2>&1 | grep -E "ALPN|HTTP/2"
# 手动发一个带 Range 的请求
curl -v -H "Range: bytes=0-99" https://example.com/big.iso看什么:
>开头的行是你发出的请求。<开头的行是收到的响应。- 重点看:状态码、Content-Length、Cache-Control、Transfer-Encoding、Location。
二、Wireshark:深入协议层
# 过滤只看 HTTP
http
# 只看某个域名
http.host == "example.com"
# 只看握手(TCP 三次握手 + TLS 握手)
tcp.flags.syn == 1 || tls.handshake
# 只看重传(TCP 层队头阻塞的线索)
tcp.analysis.retransmission看什么:
- TCP 三次握手:看 SYN → SYN-ACK → ACK,确认握手正常。
- TLS 握手:看 ClientHello/ServerHello,确认 ALPN 协商结果。
- 重传:
tcp.analysis.retransmission,重传多 = 丢包 = 弱网/队头阻塞。 - RST/FIN:看谁先断的连接(排查”幽灵连接”)。
三、mitmproxy:看 HTTPS 明文
HTTPS 抓包是密文,看不到内容。mitmproxy 通过”中间人”方式解密:
# 启动,监听 8080
mitmproxy -p 8080
# 浏览器/客户端配置代理到 localhost:8080,并信任 mitmproxy 的根证书看什么:
- 解密后的完整请求/响应(含首部和 body)。
- 适合排查”代理改了我的头”、“Content-Encoding 对不对”这类问题。
注意:mitmproxy 本质是”企业 MITM 代理”(第 11 章讲过),它会解密你的流量。只在本地调试用,别用于生产数据。
四、Go 里直接抓自己的请求
// 打印请求的原始信息(不依赖外部工具)
resp, _ := http.Get("https://example.com")
fmt.Printf("状态码: %s\n", resp.Status)
fmt.Printf("Protocol: %s\n", resp.Proto) // HTTP/1.1 还是 HTTP/2.0
for k, v := range resp.Header {
fmt.Printf("%s: %s\n", k, v)
}
// resp.Proto 能告诉你 ALPN 协商出了什么协议五、抓包定位问题的”黄金五问”
1. 请求发出去了吗? → 有没有这条记录
2. 到达服务器了吗? → 服务器 access log 有没有
3. 请求长什么样? → 方法、URL、Host、长度、被代理改过吗
4. 响应长什么样? → 状态码、首部、body 完整吗
5. 被重试了几次? → 客户端/网关/服务器三边对齐
全书完。墨叔,于北京。