你可能遇到过这些情况:
- 页面第一次打开很慢,第二次秒开
- 明明改了静态资源,用户却一直看到旧内容
- 接口偶发慢,抓日志发现后端处理时间并不长
- 同一个域名,为什么有时是 HTTP/1.1,有时变成了 HTTP/2
这些问题本质都离不开 3 件事:
- HTTP 报文到底长什么样(你抓包才看得懂)
- 连接怎么复用(Keep-Alive/HTTP2 多路复用)
- 缓存怎么生效(强缓存/协商缓存)
我这篇就用"知识分享 + 可验证"的方式,把 HTTP 拆开讲清楚。
目录(按实战排查顺序)
-
- 先把 HTTP 的边界说清楚(它解决什么,不解决什么)
-
- 报文结构:抓包时你到底在看什么
-
- GET vs POST:别背八股,用语义就够了
-
- 版本演进:1.0/1.1/2.0 分别解决了什么痛点
-
- 连接复用:Keep-Alive 到底省了什么
-
- 缓存机制:为什么"第二次秒开",以及怎么让它立刻更新
-
- 实战:用 DevTools/curl 验证与排障
-
- 最后总结
1. 先把 HTTP 的边界说清楚
- HTTP 是一种 应用层协议,规定客户端与服务端如何用"请求-响应"的方式交换数据。
- 经典场景:浏览器请求页面、App 请求接口。
你在项目里更常用的表达是:
- HTTP 是无状态的:协议层不保存会话状态
- 状态靠应用层补:Cookie/Session/Token
2. 报文结构:抓包时你到底在看什么
2.1 请求报文(Request)
- 请求行:
METHOD path HTTP/version - 请求头:
Header: value - 空行
- 请求体(可选)
2.2 响应报文(Response)
实战里你重点看三类东西:
-
状态码:成功/重定向/缓存/失败
-
关键响应头:缓存相关、内容类型、压缩相关
-
响应体大小与耗时:到底慢在网络还是慢在后端
-
状态行:
HTTP/version status_code reason -
响应头
-
空行
-
响应体
3. GET vs POST:别背八股,用语义就够了
你可以这样讲:
- 语义:GET 用于获取资源;POST 用于提交数据/产生副作用。
- 幂等性:GET 通常幂等;POST 通常不幂等。
- 参数位置:GET 常放在 URL query;POST 常放在 body(但不是强制)。
容易踩坑的一句话:
- 不是"GET 不能有 body",而是多数实现不使用/不转发。
4. 版本演进:1.0/1.1/2.0 分别解决了什么痛点
4.1 HTTP/1.0:一次请求一次连接
- 默认短连接:一次请求一次连接
- 性能问题:连接建立/释放开销大
4.2 HTTP/1.1:默认 Keep-Alive,开始"复用连接"
核心改进:
- 默认持久连接(Keep-Alive):减少重复握手
- 管线化(Pipelining):理论上可连续发送请求,但实践中受限
- 缓存机制更完善 :
Cache-Control、ETag等
但它仍有一个很要命的瓶颈:
- 队头阻塞(Head-of-Line Blocking):同一连接内,前一个请求没返回,后面的可能被阻塞。
4.3 HTTP/2:一个连接里并发多个请求(多路复用)
核心改进(背诵版):
- 二进制分帧:不再是纯文本行
- 多路复用:一个连接并发处理多个 stream
- 头部压缩:HPACK
- 服务端推送:Server Push(实际使用需谨慎)
工程里你要记住一句:
- HTTP/2 解决了应用层的队头阻塞,但底层仍基于 TCP,TCP 层仍可能队头阻塞。
5. 连接复用:Keep-Alive 到底省了什么
Keep-Alive 的意义:
- 复用同一个 TCP 连接承载多个 HTTP 请求
- 减少三次握手/慢启动开销
但要注意两个现实问题:
- 连接复用不是无限的,服务端通常有连接数、超时时间等限制
6. 缓存机制:为什么"第二次秒开",以及怎么让它立刻更新
你排查缓存问题时,也建议按"先强后协商"来判断。
6.1 强缓存
特点:
- 不发请求到服务端
- 浏览器直接用本地缓存
常见头:
Cache-Control: max-age=...Expires
6.2 协商缓存
特点:
- 会发请求到服务端
- 服务端告诉你"缓存是否还可用"
常见头:
If-None-Match/ETagIf-Modified-Since/Last-Modified
协商缓存命中时通常返回:
304 Not Modified
7. 实战:用 DevTools/curl 验证与排障
7.1 用浏览器 DevTools 看协议版本/连接复用
- 打开 Chrome DevTools -> Network
- 勾选
Disable cache(排查缓存时很有用) - 看
Protocol列(h2 表示 HTTP/2) - 看
Connection ID/Remote Address(不同版本浏览器字段略有差异),判断是否复用连接
7.2 用 curl 直接验证缓存相关响应头
你可以用下面这些命令快速确认服务端到底给了什么缓存策略:
bash
curl -I https://example.com/static/app.js
重点关注:
Cache-ControlETagLast-Modified
如果你想验证协商缓存是否会返回 304:
bash
curl -I https://example.com/static/app.js \
-H "If-None-Match: <ETAG值>"
7.3 "改了资源但不生效"最常见的落地做法
- 静态资源 带 hash 文件名 (例如
app.3f2a9c.js),配合长缓存 - HTML 入口 短缓存/不缓存,保证能拿到新版本资源引用
8. 最后总结
8.1 常见状态码怎么归类理解
- 2xx:成功(
200) - 3xx:重定向/缓存(
301/302/304) - 4xx:客户端错误(
400/401/403/404) - 5xx:服务端错误(
500/502/503)
8.2 复述自测(你能讲顺,说明你真的理解了)
HTTP 是应用层的请求-响应协议,通常跑在 TCP 之上,本身无状态,状态一般通过 Cookie/Session/Token 在应用层实现。HTTP/1.1 用 Keep-Alive 复用连接减少握手开销,但同一连接仍可能队头阻塞;HTTP/2 用二进制分帧和多路复用提升并发能力,同时做了头部压缩。缓存建议按"先强后协商"理解:强缓存靠
Cache-Control: max-age让浏览器直接用本地;协商缓存靠ETag/Last-Modified让服务端决定是否返回304。