14.3 HTTP、缓存、CDN 与容量优化
网络路径正常,用户仍可能慢:缓存键不稳定导致每次回源,响应不可压缩,负载均衡把流量送进过载池,或重试把一次故障放大成拥塞。应用与边缘优化必须围绕用户指标和系统容量共同设计。
先定位首字节时间
客户端的 TTFB 大致包含:
request upload
+ network to edge/server
+ edge queue and processing
+ origin/upstream queue and processing
+ network back to client
+ first response bytes因此 TTFB 高不能自动判为“服务端代码慢”。用同一 trace ID 对齐这些时间:
| 位置 | 应记录 |
|---|---|
| client/RUM | DNS、connect、TLS、TTFB、protocol、network type |
| CDN/LB | cache status、edge time、origin time、selected backend |
| application | queue time、handler time、status、request size |
| database/upstream | pool wait、RPC time、attempt count、timeout |
若应用 handler 只有 20 ms,而负载均衡排队 800 ms,继续优化 SQL 不会改善该样本。
DNS:减少依赖,不是盲目增加 TTL
DNS 性能受 resolver 距离与缓存、CNAME chain、权威服务可用性、DNSSEC 和响应大小影响。浏览器页面引用很多资源,也不等于产生同样数量的 DNS 查询;同一 hostname 可共享缓存和连接。
优化原则:
- 减少没有架构价值的独立 hostname 与 CNAME 层级;
- 使用可靠、低延迟且有冗余的权威 DNS;
- TTL 在缓存效率与变更速度之间取舍;
- 变更前先降低 TTL,等待旧 TTL 覆盖后再切换;
- 监控
NOERROR空答案、NXDOMAIN、SERVFAIL、truncation 与 DNSSEC; - 同时发布 A/AAAA 时,分别验证两条路径。
dns-prefetch 只预解析名称;preconnect 可能提前完成 DNS 与连接/TLS。它们会消耗 socket、CPU 和服务端资源,只应用于用户很可能访问的少量关键 origins。
预连接只优化该 origin 的连接准备,不能把“10 个资源”简单乘以一遍 DNS+TCP 时间,因为浏览器会缓存解析并复用/多路复用连接。
HTTP 缓存:先写对象语义
确定四件事:
- 哪些 cache 可以存:shared cache 还是 private browser cache;
- cache key 包含什么:URL、method、选定 request headers、租户或认证维度;
- 多久 fresh;
- stale 后如何 revalidate,以及源站失败时是否允许服务 stale。
内容寻址的静态资源
GET /assets/app.7f3a9c1.js HTTP/1.1
HTTP/1.1 200 OK
Cache-Control: public, max-age=31536000, immutable
Content-Type: text/javascript只有文件名随内容变化时,长 freshness 才安全。若同一 URL 的内容会被覆盖,immutable 会让修复难以及时到达客户端。
需要重验证的文档
HTTP/1.1 200 OK
Cache-Control: no-cache
ETag: "revision-42"no-cache 允许存储,但复用前通常必须 revalidate;no-store 才是要求 cache 不存储。客户端后续可发送:
If-None-Match: "revision-42"若表示未变化,服务端返回 304,不发送完整 representation。304 仍有 headers 和网络往返,不能称为“零字节、零成本”。
Vary 与个性化内容
内容若随 Accept-Encoding、语言等请求头变化,响应应正确设置 Vary。但把高基数或用户特有 header 放入 cache key 会导致命中率崩溃。
带认证或个性化的响应不能因为“想提高命中率”就标记为 public。共享缓存中的跨用户数据泄漏比性能回退严重得多。
压缩:按内容和 CPU 取舍
文本型 HTML、CSS、JavaScript、JSON 通常适合 gzip 或 Brotli;已经压缩的 JPEG、PNG、视频、ZIP 再压缩收益很小。动态高等级压缩可能增加 CPU 与 TTFB。
curl --compressed --silent --show-error --dump-header - \
--output /dev/null https://example.com/app.js检查 Content-Encoding、Vary: Accept-Encoding、原始大小、传输大小与服务端 CPU。预压缩静态资源可以用构建时间换请求时间,但要确保内容类型和缓存变体正确。
HTTP 连接与请求形态
- HTTP/1.1:控制每 origin 并发连接,尽量复用,避免大量短连接;
- HTTP/2:利用一条连接多路复用,谨慎处理优先级、单连接故障域与过大的并发 streams;
- HTTP/3:验证 UDP/QUIC 可达性、fallback、QPACK 与连接迁移观测;
- 所有版本:设置 request/response deadline、body 上限和 backpressure。
“把所有小文件合成一个大文件”不是跨协议通用答案。HTTP/2/3 降低了多请求的连接开销,但每个请求仍有 headers、调度和服务端成本;过度打包又会扩大缓存失效范围。根据依赖图、缓存粒度与实际 waterfall 决策。
CDN:优化 hit 之前先定义 cache key
一次 CDN 请求可能是:
- hit:边缘直接服务;
- miss:向 origin 拉取;
- revalidated:边缘条件请求验证后继续使用;
- stale:按策略在源站异常时服务旧对象;
- bypass:因规则、认证或请求特征跳过缓存。
重点指标:
request hit ratio
byte hit ratio
origin requests and egress
edge TTFB by region
miss/revalidation latency
cache-key cardinality
purge propagation and errors高 request hit ratio 不一定节省最多带宽;大量小对象命中而大对象回源时,byte hit ratio 可能仍很低。
常见问题:Cookie 或 query 参数无意进入 cache key、不同编码/语言没有正确 Vary、错误响应被缓存、所有 edge 同时回源造成 thundering herd。可用 request coalescing、origin shield、stale-if-error 和带 jitter 的刷新缓解,但必须定义一致性与故障边界。
不要靠猜测 X-Cache 等私有 header 判断所有 CDN。每家产品的 header、日志和命中状态语义不同,应以实际供应商文档与配置为准。
负载均衡与容量
负载均衡无法创造后端容量。若每个实例都已饱和,继续均匀分配只会让全部实例一起排队。
需要共同观察:
- active connections/requests;
- queue depth 与 queue time;
- success/error/timeout rate;
- per-backend latency 与 saturation;
- health check 状态变化;
- retry attempts 与 retry budget;
- connection pool、ephemeral ports 与 NAT/LB state capacity。
算法与业务成本
round robin 适合后端能力和请求成本相近的场景;least connections 对长连接有帮助,但连接数不一定代表实际工作量;consistent hashing 有利于 affinity/cache locality,却会在成员变化时产生再平衡。
健康检查应验证足以接流量的依赖,又不能因为一个非关键依赖抖动让整个池同时摘除。liveness、readiness 与深度业务探测承担不同职责。
重试会放大负载
一次调用链若每层都允许 3 次尝试,最坏请求量会按层放大。重试只适合可安全重试的操作,并应具备:
- bounded attempts;
- exponential backoff 与 jitter;
- end-to-end deadline;
- retry budget;
- idempotency key 或明确幂等语义;
- 熔断/限流与过载保护。
当服务已过载时,无节制重试会把局部故障变成全局拥塞。
大对象传输
大文件性能同时受 CDN、range requests、传输窗口、带宽和客户端读速限制。检查:
- 是否支持并正确处理
Range/206; - 内容是否适合边缘缓存;
- 是否需要分块上传、断点续传和 checksum;
- 应用是否把整个对象读入内存;
- 限速和公平性是否避免单用户占满链路;
- 下载测试是否把写盘速度混入网络测量。
用 /dev/null 测量可以减少本地磁盘影响,但仍包含客户端 CPU、TLS 与内核处理。
从基线到复验
- 定义用户 SLI:成功率、p50/p95/p99、吞吐与业务结果;
- 按 region、网络、协议、cache status、backend 等维度切分;
- 用 trace/抓包/连接状态定位限制阶段;
- 形成一个能被证伪的假设;
- 在代表性环境只改变一个主要变量;
- 同时观察目标指标与 guardrails;
- 灰度上线,保留回滚;
- 记录结果,包括没有收益的实验。
验收清单
- [ ] 不用固定毫秒阈值替代业务 SLO;
- [ ] 能解释 TTFB 为什么不等于应用处理时间;
- [ ] 能区分
no-cache、no-store与 immutable; - [ ] 能为 CDN 写出 cache key 和 purge 策略;
- [ ] 能说明负载均衡为何不能解决总容量不足;
- [ ] 性能变更具备基线、对照、guardrail 与回滚;
- [ ] IPv4、IPv6、HTTP/2 与 HTTP/3 的关键路径分别验证。
第四卷专业内容总结
这一卷从数据如何分层封装开始,走过 Socket、TCP、HTTP、TLS、WebSocket/gRPC、DNS/CDN/LB、Ethernet、IP、路由、拥塞控制、QUIC、网络诊断与 IPv6,最后落到性能测量和容量验证。
专业判断的共同方法始终一样:明确协议层和状态机,区分规范语义与实现策略,用观测证据限制结论,再通过可重复实验验证。后续统一文风时,故事和比喻会帮助读者进入这些模型,但不会替代机制本身。
规范入口
- RFC 9110:HTTP Semantics;
- RFC 9111:HTTP Caching;
- RFC 9211:Cache-Status;
- RFC 5861:stale-while-revalidate / stale-if-error;
- RFC 6585:Additional HTTP Status Codes;
- RFC 9113、RFC 9114:HTTP/2、HTTP/3。