本文覆盖 HTTP 从 0.9 到 HTTP/3 的演进历程、请求/响应报文结构、常用状态码、持久连接与队头阻塞、HTTP/2 多路复用/头部压缩/服务端推送、HTTP/3 基于 QUIC,以及 Cookie/Session/Token 机制。
目录
| 章节 | 说明 |
|---|---|
| HTTP 演进历程 | 0.9 → 1.0 → 1.1 → 2 → 3 |
| HTTP/1.1 持久连接与队头阻塞 | keep-alive、pipeline、队头阻塞 |
| HTTP/2 核心特性 | 多路复用、头部压缩、服务端推送 |
| HTTP/3 与 QUIC | 基于 UDP 的新一代协议 |
| 报文结构 | 请求行、状态行、头部字段、body |
| 常用状态码 | 2xx/3xx/4xx/5xx |
| Cookie / Session / Token | 三种身份认证机制对比 |
HTTP 演进历程
| 版本 | 年份 | 核心特性 | 底层协议 |
|---|---|---|---|
| HTTP/0.9 | 1991 | 纯文本,只有 GET,响应后立即关闭连接 | TCP |
| HTTP/1.0 | 1996 | 增加 HEAD/POST,引入 Header、状态码、版本号,支持非文本 | TCP |
| HTTP/1.1 | 1999 | 默认长连接,分块传输,缓存控制,强制 Host 头 | TCP |
| HTTP/2 | 2015 | 二进制分帧,多路复用,头部压缩(HPACK),服务端推送 | TCP |
| HTTP/3 | 2022 | 基于 QUIC(UDP),解决 TCP 队头阻塞,0-RTT | UDP |
HTTP/1.1 是目前仍被最广泛使用的版本,HTTP/2 普及率在提升,HTTP/3 是未来方向。
HTTP/1.1 持久连接与队头阻塞
短连接 vs 长连接
短连接(HTTP/0.9、1.0 默认):每次请求都要经历 TCP 三次握手 + 请求响应 + 四次挥手,建立/关闭连接的开销占比高达 60%。
长连接(HTTP/1.1 默认):TCP 连接在多次请求间复用,通过 Connection: keep-alive 保持。
sequenceDiagram
participant C as 客户端
participant S as 服务端
C->>S: TCP 三次握手
C->>S: 请求 1
S->>C: 响应 1
C->>S: 请求 2(复用同一连接)
S->>C: 响应 2
C->>S: 请求 3
S->>C: 响应 3
C->>S: TCP 四次挥手
长连接关闭策略(Nginx 示例):
keepalive_timeout 60:空闲 60 秒后关闭keepalive_requests 1000:同一连接最多处理 1000 个请求- 客户端主动关闭:请求头加
Connection: close
队头阻塞(Head-of-Line Blocking)
HTTP/1.1 的"请求-应答"模型是串行的:前一个请求未完成,后续请求必须等待。即使后续请求很简单,也会被阻塞。
缓解方案(非根本解决):
- 并发连接:浏览器同时开多个 TCP 连接(通常 6~8 个)
- 域名分片:将资源分布到多个子域名,突破并发连接数限制
HTTP/2 核心特性
HTTP/2 基于 Google 的 SPDY 协议,从根本上改变了 HTTP 的传输方式。
多路复用(Multiplexing)
HTTP/2 引入二进制分帧,将请求/响应拆分为多个帧(Frame),每帧标记所属的流(Stream)ID。多个流可以在同一 TCP 连接上并发传输,彻底解决了 HTTP/1.1 的队头阻塞问题(应用层层面)。
同一 TCP 连接上:
Stream 1: [帧1] [帧3] [帧5] ← 请求 A
Stream 2: [帧2] [帧4] [帧6] ← 请求 B
交错传输,互不阻塞
头部压缩(HPACK)
HTTP/1.1 的头部是纯文本,且每次请求都要重复发送大量相同头部(User-Agent、Cookie 等)。HTTP/2 使用 HPACK 算法:
- 维护一个静态表(常用头部)和动态表(本次连接中出现过的头部)
- 已出现过的头部只需发送索引号,大幅减少传输量
服务端推送(Server Push)
服务器可以主动向客户端推送资源,无需客户端请求。例如客户端请求 HTML,服务器可同时推送 CSS 和 JS,减少往返次数。
HTTP/2 特性汇总
| 特性 | 说明 |
|---|---|
| 二进制分帧 | 不再是纯文本,解析效率更高 |
| 多路复用 | 单连接并发多请求,解决应用层队头阻塞 |
| 头部压缩(HPACK) | 减少重复头部传输 |
| 服务端推送 | 主动推送资源,减少 RTT |
| 流优先级 | 可为不同请求设置优先级 |
| 事实上要求 TLS | 主流浏览器只支持 HTTPS 下的 HTTP/2 |
HTTP/3 与 QUIC
HTTP/2 虽然解决了应用层队头阻塞,但底层 TCP 仍有队头阻塞:TCP 层的一个丢包会导致同一连接上所有流都被阻塞。
HTTP/3 使用 QUIC 协议(Quick UDP Internet Connections),运行在 UDP 之上:
| 特性 | 说明 |
|---|---|
| 基于 UDP | 绕过 TCP 的队头阻塞 |
| 内置 TLS 1.3 | 握手与加密合并,0-RTT 或 1-RTT 建连 |
| 连接迁移 | 使用 Connection ID 而非四元组,切换网络不断连 |
| 流级别可靠性 | 每个流独立处理丢包,一个流丢包不影响其他流 |
| 拥塞控制 | 在 UDP 之上实现,可灵活升级算法 |
报文结构
HTTP 报文由三部分组成:起始行 + 头部字段 + 空行 + 消息体(body)。
请求报文
GET /index.html HTTP/1.1 ← 请求行:方法 + URI + 版本
Host: www.example.com ← 头部字段(必须有 Host)
User-Agent: Mozilla/5.0
Accept: text/html
Connection: keep-alive
← 空行(CRLF)
← body(GET 通常为空)
请求行三要素:请求方法 + 请求目标(URI)+ 版本号
| 方法 | 含义 | 是否有 body |
|---|---|---|
| GET | 获取资源 | 否 |
| POST | 提交数据 | 是 |
| PUT | 更新/创建资源 | 是 |
| DELETE | 删除资源 | 否 |
| HEAD | 只获取头部 | 否 |
| PATCH | 部分更新 | 是 |
| OPTIONS | 查询支持的方法 | 否 |
响应报文
HTTP/1.1 200 OK ← 状态行:版本 + 状态码 + 原因短语
Content-Type: text/html
Content-Length: 1234
Server: nginx/1.18.0
← 空行
<html>...</html> ← body
常用头部字段
| 字段 | 类型 | 说明 |
|---|---|---|
Host |
请求 | 唯一必填字段,指定目标主机 |
Content-Type |
通用 | body 的 MIME 类型 |
Content-Length |
通用 | body 的字节长度 |
Authorization |
请求 | 认证信息 |
Cache-Control |
通用 | 缓存策略 |
Location |
响应 | 重定向目标 URL |
Set-Cookie |
响应 | 设置 Cookie |
Cookie |
请求 | 发送 Cookie |
Transfer-Encoding: chunked |
通用 | 分块传输,不需要预知 body 长度 |
常用状态码
2xx 成功
| 状态码 | 含义 | 典型场景 |
|---|---|---|
| 200 OK | 请求成功 | 正常响应 |
| 201 Created | 资源已创建 | POST 创建成功 |
| 204 No Content | 成功但无 body | DELETE 成功 |
| 206 Partial Content | 部分内容 | 断点续传,配合 Content-Range |
3xx 重定向
| 状态码 | 含义 | 典型场景 |
|---|---|---|
| 301 Moved Permanently | 永久重定向 | HTTP → HTTPS 迁移 |
| 302 Found | 临时重定向 | 维护期间跳转 |
| 304 Not Modified | 缓存未过期 | 条件请求命中缓存 |
4xx 客户端错误
| 状态码 | 含义 | 典型场景 |
|---|---|---|
| 400 Bad Request | 请求格式错误 | 参数缺失/格式错误 |
| 401 Unauthorized | 未认证 | 需要登录 |
| 403 Forbidden | 无权限 | 已登录但无权访问 |
| 404 Not Found | 资源不存在 | URL 错误 |
| 405 Method Not Allowed | 方法不允许 | 用 GET 访问只支持 POST 的接口 |
| 429 Too Many Requests | 请求过多 | 触发限流 |
5xx 服务端错误
| 状态码 | 含义 | 典型场景 |
|---|---|---|
| 500 Internal Server Error | 服务器内部错误 | 未捕获异常 |
| 502 Bad Gateway | 网关错误 | 上游服务挂了 |
| 503 Service Unavailable | 服务不可用 | 服务器过载,配合 Retry-After |
| 504 Gateway Timeout | 网关超时 | 上游服务响应超时 |
Cookie / Session / Token
Cookie
服务器通过 Set-Cookie 响应头将数据写入浏览器,浏览器后续请求自动携带 Cookie 头。
sequenceDiagram
participant B as 浏览器
participant S as 服务器
B->>S: GET /login(首次访问)
S->>B: 200 OK<br/>Set-Cookie: user_id=123; HttpOnly; Max-Age=86400
B->>S: GET /profile<br/>Cookie: user_id=123
S->>B: 200 OK(识别用户)
Cookie 关键属性:
| 属性 | 说明 |
|---|---|
Max-Age / Expires |
有效期(Max-Age 优先级更高) |
Domain / Path |
作用域,限制发送范围 |
HttpOnly |
禁止 JS 读取,防 XSS |
Secure |
仅 HTTPS 传输 |
SameSite=Strict/Lax |
防 CSRF,限制跨站携带 |
Session
Session 将用户状态存储在服务器端,Cookie 只存 Session ID。
浏览器 Cookie: session_id=abc123
服务器内存/Redis: abc123 → {user_id: 456, role: admin, ...}
缺点:服务器有状态,水平扩展时需要 Session 共享(Redis 等)。
Token(JWT)
Token 将用户信息编码在客户端,服务器无状态。JWT(JSON Web Token)结构:
Header.Payload.Signature
eyJ0eXAiOiJKV1QiLCAiYWxnIjoiSFMyNTYifQ.eyJ1c2VySWQiOjEyM30.签名
对比:
| 维度 | Cookie+Session | Token(JWT) |
|---|---|---|
| 存储位置 | 服务器(Session)+ 浏览器(Cookie) | 客户端(localStorage/Cookie) |
| 服务器状态 | 有状态 | 无状态 |
| 水平扩展 | 需要 Session 共享 | 天然支持 |
| 安全性 | 可配合 HttpOnly 防 XSS | 需防止 Token 泄露 |
| 注销 | 删除 Session 即可 | 需要黑名单或短有效期 |
| 适用场景 | 传统 Web 应用 | API、微服务、移动端 |
参考资料
- 《透视 HTTP 协议》— 罗剑锋,极客时间,第 01、09、12、13、17、19、30、31、32 讲
- RFC 7230-7235 (HTTP/1.1)、RFC 7540 (HTTP/2)、RFC 9114 (HTTP/3)
评论 (0)
发表评论