http

09. HTTP/1.1、HTTP/2、HTTP/3

0. 本章先解决什么问题

TCP 提供的是字节流。它不知道这些字节是在请求一个页面、提交一个表单、下载一个文件,还是调用某个接口。

HTTP 在应用层定义了:

请求怎么表达
响应怎么表达
资源如何定位
方法和状态码如何表示语义
头部如何传递控制信息
缓存、压缩、鉴权、内容类型如何协商

本章要解决:

  1. HTTP 请求和响应到底由哪些部分组成。
  2. 方法、状态码、Header、Body 分别承担什么语义。
  3. HTTP 为什么说是无状态协议,以及状态如何被应用补上。
  4. HTTP/1.1、HTTP/2、HTTP/3 的核心差异是什么。
  5. 排障时怎样从状态码、头部、连接阶段判断问题位置。

HTTP 缓存与连接路径

这张图怎么读

这张图提醒你:HTTP 不只是请求到了服务器再返回。浏览器、代理、CDN、源站都可能参与缓存判断;客户端和服务端还可能复用已有连接。排障时要同时看状态码、Header、缓存命中和连接阶段。

1. HTTP 的核心模型:请求和响应

HTTP 的基本交互是:

Client — Request —> Server
Client <— Response — Server

请求不是单纯一段字符串,它包含:

部分作用
方法表示想对资源做什么
URL/路径表示目标资源在哪里
Header控制信息和元数据
Body请求携带的数据

响应包含:

部分作用
状态码表示处理结果类别
Header响应元数据和控制信息
Body返回的数据

HTTP 不是只为网页服务。它是一种通用应用协议模型,可以承载文档、图片、接口数据、文件下载、流式响应等。

2. HTTP/1.1 请求长什么样

一个简化的 HTTP/1.1 请求:

代码块HTTP · 3 行收起展开
GET /users/42 HTTP/1.1
Host: api.example.com
Accept: application/json

如果有请求体,body 会跟在空行之后:

代码块HTTP · 6 行收起展开
POST /orders HTTP/1.1
Host: api.example.com
Content-Type: application/json
Content-Length: 31

{"item":"book","quantity":1}

结构可以理解为:

请求行
Header 列表
空行
Body

空行很重要。它表示 Header 结束,后面开始进入 Body。

3. HTTP/1.1 响应长什么样

一个响应:

代码块HTTP · 5 行收起展开
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 27

{"id":42,"name":"example"}

结构是:

状态行
Header 列表
空行
Body

响应体长度可以通过 Content-Length 指定,也可以用 chunked 等方式分块传输。

如果 Body 长度处理错,可能出现:

问题结果
Content-Length 比实际大客户端一直等更多数据,直到超时
Content-Length 比实际小客户端截断 body,剩余字节污染后续解析
分块格式错误客户端解析失败

这就是为什么 HTTP 虽然看起来是文本协议,底层仍然要严肃处理字节边界。

字节边界:HTTP 报文不是“按行读到结束”

HTTP Header 可以按行解析,但 Body 必须按协议边界解析。常见边界来源:

方式如何知道 Body 结束
Content-Length读固定字节数
Transfer-Encoding: chunked按 chunk 长度读取直到 0 chunk
无 Body 的状态/方法根据语义不读 body
连接关闭表示结束老式或特定场景,复用连接时不适合依赖

如果使用连接复用,边界错误会污染下一次响应:

响应 A 的多余字节
-> 被客户端当成响应 B 的开头

所以 HTTP 实现最底层仍然是严谨的字节流解析,而不是“看起来像文本就随便 split”。

4. 方法语义:安全性和幂等性

常见方法:

方法常见语义
GET获取资源
POST提交数据、创建资源或触发处理
PUT整体替换资源
PATCH部分更新资源
DELETE删除资源
HEAD只获取响应头
OPTIONS查询服务能力,常见于跨域预检

两个重要概念:

概念含义
安全请求不应该改变资源状态
幂等同一个请求执行一次和多次,最终效果应相同

通常:

方法安全幂等
GET
HEAD
PUT
DELETE通常设计为是
POST通常不是
PATCH不一定

这不是语法强制,而是协议语义约定。服务实现如果违反语义,会让缓存、重试、代理、客户端行为变得危险。

例如:如果 GET 请求会删除数据,那么浏览器预取、缓存刷新、爬虫访问都可能造成破坏。

反例:方法语义错了,会把中间层变成风险源

HTTP 的方法语义会影响浏览器、代理、缓存、重试库和网关策略。几个常见反例:

设计为什么危险
用 GET 触发删除或扣费预取、爬虫、缓存刷新都可能触发副作用
用 POST 做可安全重试的查询却不加幂等键超时重试可能创建重复资源
DELETE 每次调用都报错而不是保持最终删除状态客户端重试和状态收敛会变复杂
PUT 只做部分更新但不说明语义客户端以为是整体替换,数据可能被覆盖

设计 HTTP 接口时要把“网络会超时、客户端会重试、中间层会缓存或预取”当作现实前提。方法语义不是为了好看,它决定失败后能不能安全恢复。

5. 状态码是排障入口

状态码分组:

范围含义常见例子
1xx信息性响应100 Continue
2xx成功200、201、204
3xx重定向或缓存相关301、302、304
4xx客户端侧请求问题400、401、403、404、409、429
5xx服务侧或上游问题500、502、503、504

更具体的排障直觉:

状态码常见含义
400请求格式或参数不符合要求
401未认证或认证信息无效
403已识别身份,但没有权限
404资源或路由不存在
409状态冲突
429请求过多,被限流
500服务内部错误
502网关连接上游失败或上游响应异常
503服务不可用,可能维护、过载、实例不足
504网关等待上游超时

状态码不是结论,但它能帮你缩小范围。

6. Header:HTTP 的控制平面

Header 不承载主体数据,却控制了大量行为。

Header作用
Host同一个 IP 上区分不同站点或服务
Content-TypeBody 的格式
Content-LengthBody 的字节长度
Accept客户端希望接收的格式
Authorization认证信息
Cookie浏览器携带的状态数据
Cache-Control缓存策略
ETag资源版本标识
Location重定向目标
User-Agent客户端信息
X-Forwarded-For代理链路中记录原始来源的常见头

Header 的关键点是:它们经常影响中间设备行为。

缓存看 Cache-Control / ETag
代理看 Host / 转发头
浏览器看 Cookie / CORS 相关头
客户端看 Content-Type / Content-Length

7. HTTP 是无状态协议

HTTP 本身不记住上一次请求。

请求 1: GET /profile
请求 2: POST /orders

协议层面这两次请求是独立的。所谓登录状态、购物车、会话上下文,通常由应用用额外机制补上:

机制思路
Cookie + Session客户端带会话标识,服务侧查会话数据
Token客户端每次带可验证凭证
URL 参数少量场景把状态放在链接里
服务侧存储状态保存在服务侧或共享存储中

无状态的好处是请求可以被独立转发、扩展和缓存。代价是应用必须明确设计状态保存、过期、刷新、撤销和安全边界。

8. 缓存:HTTP 不只是传输,还会影响是否访问源站

HTTP 缓存能减少延迟和带宽消耗,但也会导致“为什么我改了内容还是旧的”。

常见机制:

机制含义
Cache-Control控制是否缓存、缓存多久、是否必须重新验证
ETag服务给资源的版本标识
Last-Modified资源最后修改时间
304 Not Modified客户端缓存仍可用,不返回完整 body

简化流程:

第一次请求:
客户端 -> 服务端
服务端 -> body + ETag
客户端缓存

再次请求:
客户端 -> 带 ETag 询问是否变化
服务端 -> 304
客户端使用本地缓存

缓存问题要同时看浏览器、代理、缓存节点和源服务响应头。

9. 连接复用与 Keep-Alive

每次 HTTP 请求都重新建立 TCP/TLS 连接成本很高。

Keep-Alive 允许一条连接承载多个请求:

TCP/TLS 连接建立
请求 1 -> 响应 1
请求 2 -> 响应 2
请求 3 -> 响应 3
连接关闭或回收

好处:

好处解释
减少握手成本不必每次重新 TCP/TLS 握手
降低延迟后续请求可以更快发出
减少资源抖动避免大量短连接创建和关闭

代价:

代价解释
连接池管理复杂空闲连接、最大连接数、失效连接都要处理
中间设备可能关闭空闲连接应用复用时可能遇到已断连接
长连接占资源服务端和客户端都要维护状态

连接复用是性能优化,也是资源管理问题。

10. HTTP/1.1、HTTP/2、HTTP/3

三个版本的核心差异:

版本传输基础核心特点主要解决
HTTP/1.1TCP文本消息、持久连接基本请求响应与连接复用
HTTP/2TCP二进制帧、多路复用、头部压缩多请求并发和头部开销
HTTP/3QUIC/UDP基于 QUIC,多路复用,连接迁移减少 TCP 层队头阻塞和握手成本

10.1 HTTP/1.1 的限制

HTTP/1.1 可以复用连接,但一个连接上请求/响应的处理仍容易受到顺序影响。实际系统通常会开多条连接并发请求。

10.2 HTTP/2 的多路复用

HTTP/2 把数据拆成帧,在一条连接上承载多个流:

TCP 连接
stream 1: request/response
stream 2: request/response
stream 3: request/response

这样可以减少连接数量,提高并发效率。但它仍然运行在 TCP 上,如果 TCP 层丢包,整条 TCP 字节流的有序交付仍可能影响多个流。

10.3 HTTP/3 与 QUIC

HTTP/3 基于 QUIC,QUIC 运行在 UDP 之上,在传输层内部实现加密、可靠传输、多路复用等能力。

它的重要目标是减少 TCP 层队头阻塞,并改善连接建立、网络切换等场景的体验。

初学时要先吃透 HTTP 的语义,再理解版本优化。版本变化主要优化传输方式,不改变“请求/响应、方法、状态码、头部、资源语义”的基本思维。

11. 联系实际:一次 HTTP 请求慢,应该拆成哪些阶段

一个 URL 访问慢,可能慢在很多地方:

DNS 解析
TCP 连接
TLS 握手
请求上传
服务处理
等待首字节
响应下载
缓存命中/未命中
重定向
代理转发

每个阶段对应不同证据:

阶段常见证据
DNS 慢解析时间长、不同解析器结果不同
TCP 慢connect time 长、SYN 重传
TLS 慢证书验证、握手失败、协议协商异常
服务处理慢首字节等待长,服务日志耗时高
下载慢响应体大、带宽不足、丢包、窗口限制
缓存异常304/Cache-Control/ETag 行为不符合预期
网关异常502/503/504、上游连接失败

所以排查 HTTP 不只是看状态码,还要看时间分解和链路位置。

排障卡:改了内容还是旧的,先看缓存证据

“HTTP 返回旧内容”通常不是一句“缓存问题”就结束。按层拆:

证据说明
Cache-Control是否允许缓存、缓存多久、是否必须重新验证
ETag / Last-Modified客户端是否能做条件请求
304 Not Modified服务认为缓存仍有效,未返回完整 body
CDN/代理命中 Header是否被中间缓存直接返回
浏览器缓存状态是否没有真正发起网络请求
源站日志请求有没有到达源站

再看连接复用:

请求失败发生在复用旧连接后的第一次写入
-> 可能是中间设备或服务端回收了空闲连接

HTTP/2 多个流一起变慢
-> 可能是底层 TCP 丢包、连接级窗口或上游处理阻塞

HTTP 排障的基本动作是把“协议语义”和“传输连接”分开:状态码/方法/Header 解释语义,连接复用/握手/下载解释传输成本。

手推:Content-Length 错了会让 HTTP 边界整体错位

HTTP/1.1 在同一连接上复用多个请求/响应时,接收方必须知道当前消息 body 到哪里结束。

假设响应头写:

Content-Length: 5

body 实际发送:

HelloWorld

接收方会把前 5 byte 当作当前响应体:

Hello

剩下的:

World

可能被误认为下一个响应的开头,整个连接上的消息边界都会错位。

反过来,如果 Content-Length 写得比实际 body 长,接收方会继续等待不存在的字节,表现为卡住或超时。

所以 HTTP 不是“读到空行后随便读一点 body”。body 边界必须由协议规则决定,例如 Content-Length、分块传输或连接关闭。

边界条件:状态码是入口,不是完整结论

同一个状态码可能来自不同层:

状态码可能来源
404源站没有资源、网关路由不到、缓存里保存了旧 404
502代理无法连接上游、上游返回非法响应、协议转换失败
503上游过载、维护、限流、负载均衡没有健康节点
504网关等上游超时,不代表客户端到网关不通

排障时不能只记录:

返回 502

要继续问:

这个状态码是谁生成的?
请求有没有到源站?
上游连接是否建立?
响应是否被代理改写?
缓存是否命中?

HTTP 的控制面在 header 和中间层里。状态码是入口,证据链才是结论。

12. 学完本章你能解决什么问题

学完本章,你应该能:

  1. 手写并解释一个 HTTP/1.1 请求和响应的结构。
  2. 区分方法语义、安全性、幂等性,并理解它们对重试和缓存的影响。
  3. 根据 4xx、5xx、502、503、504 等状态码判断初步排障方向。
  4. 解释 Header 如何影响内容类型、缓存、认证、代理转发和连接管理。
  5. 理解 HTTP 无状态,以及应用如何用 Cookie、Session、Token 等机制补状态。
  6. 解释 Keep-Alive、连接复用的性能收益和资源风险。
  7. 概括 HTTP/1.1、HTTP/2、HTTP/3 的核心差异。
  8. 把一次慢请求拆成 DNS、连接、握手、服务处理、下载、缓存、网关等阶段定位。

延伸阅读