【从0开始学计算机网络】| HTTP 协议的进化史

【从0开始学计算机网络】| HTTP 协议的进化史

前言

调 Nginx 时见过 keep-alive,看浏览器 DevTools 时见过 h2,面试里被问过"HTTP/2 为什么快"------这些零散的片段其实都指向同一条主线:HTTP 这三十多年是怎么一步步进化过来的。这篇文章把 1.0 到 3.0 的进化逻辑串起来,重点讲清楚每个版本当时卡在哪儿、怎么解决、又留下了什么新问题。

一、使用场景

这个知识点会在三类场景里反复出现。

排查页面加载慢。一个页面几十个请求,到底慢在建连接、慢在排队、还是慢在传输,得先搞清楚当前走的是哪个协议版本、队头阻塞发生在哪一层。

配置服务器和网关。Nginx 的 keepalive_timeout、upstream 连接池、Spring Boot 的 HTTP/2 开关,这些配置的含义全部挂在版本演进上,不了解演进史就只能照抄配置。

准备面试。HTTP 版本区别是计算机网络的高频考题,只背特性表很难答出层次,理解进化逻辑才能把"为什么"讲清楚。

二、核心概念

HTTP 本质上是一份通信约定:浏览器把"我要这个资源"翻译成一段固定格式的报文发出去,服务器按同样的格式把内容和状态传回来。

复制代码
GET /index.html HTTP/1.1
Host: www.example.com

这就是一次最小的 HTTP 请求。四个版本各自解决了一个瓶颈,先看总览:

版本 核心变化 解决了什么
HTTP/0.9 只有 GET 方法 只能传 HTML 文档
HTTP/1.0 引入状态码和头字段,支持多类型资源 内容从文字扩展到图片、音视频
HTTP/1.1 长连接、Host 头、分块传输、缓存控制 反复重建连接的开销
HTTP/2.0 二进制分帧、多路复用、HPACK 压缩 同一连接内串行排队
HTTP/3.0 底层换成 QUIC,跑在 UDP 上 TCP 队头阻塞、握手慢、连接不能迁移

看这张表的方式不是背,而是顺着"瓶颈→解法"往下读:1.0 卡在连接,1.1 解决了连接又带来排队,2.0 解决了排队又撞上 TCP 本身,3.0 干脆把传输层一起换掉。

三、最小代码

不用写任何后端代码,几条命令就能把版本差异摸一遍。

用 nc 手写一个 HTTP/1.0 请求:

bash 复制代码
printf 'GET / HTTP/1.0\r\nHost: example.com\r\n\r\n' | nc example.com 80

响应读完连接立刻关闭。同样的请求换成 1.1:

bash 复制代码
printf 'GET / HTTP/1.1\r\nHost: example.com\r\n\r\n' | nc example.com 80

响应结束后连接会保持一段时间,还能在这条连接上继续发第二个请求------这就是长连接最直观的形态。

再用 curl 看协议协商结果:

bash 复制代码
curl -sv --http1.1 -o /dev/null https://example.org 2>&1 | grep -i http
curl -sv --http2   -o /dev/null https://example.org 2>&1 | grep -i http

后一条的输出里会出现 Using HTTP2 的字样,说明服务端支持且协商成功。想在自己项目里打开 HTTP/2,Spring Boot 加一个配置即可(配置键随版本略有差异,以所用版本的官方文档为准):

yaml 复制代码
server:
  http2:
    enabled: true

四、执行流程

同一次页面加载,在四个版本下走的是完全不同的流程。

**HTTP/1.0:一个请求一条连接。**页面里要用到 index.html、main.js、style.css 和 logo.png 四个资源,浏览器就得做四轮"三次握手、发请求、收响应、四次挥手",时间大头花在建连接而不是传数据上。1.0 真正的历史意义,是把可传输的内容从纯文字扩展到了图片、音视频、CSS 和 JS。

**HTTP/1.1:连接复用,但请求串行。**一条 TCP 连接建立后反复用于多轮请求和响应,建连接的开销被摊薄,握手次数大幅下降;Host 头让一台服务器能靠域名区分多个站点,虚拟主机因此跑了起来。但同一条连接里,响应必须按请求顺序返回:第一个请求是一张几 MB 的大图,后面的小请求就算服务器早就处理完,也得排队等------这就是队头阻塞。

**HTTP/2.0:二进制分帧加多路复用。**报文拆成二进制帧,同一连接上的多路数据交错传输,不存在排队。两个配套优化:HPACK 让 Cookie 和 User-Agent 这类每次重复的字段只传增量;Server Push 让服务器在浏览器请求 HTML 时顺手把关联资源推过去。要注意,HTTP/2 消灭的只是 HTTP 层的排队,传输层的排队原封不动------TCP 坚持按序交付,中间缺了一个包,后面已到达的数据全部积压,整条连接上的流都得陪着等。

**HTTP/3.0:把传输层从 TCP 换成 QUIC。**协议栈从 HTTP → TCP → IP 变成 HTTP → QUIC → UDP → IP。QUIC 在用户态实现可靠传输,每条流独立做丢包恢复,一条流丢包不再拖累别的流;握手与 TLS 合并,首次连接 1-RTT,重连可 0-RTT;连接靠连接 ID 标识而不是 IP 加端口,手机从 WiFi 切到移动网络连接不断,这对移动端体验是质的变化。

五、常见坑

**把 HTTP/2 当万能提速。**高丢包的弱网环境下它可能反而更慢:1.1 时代浏览器对同一域名开 6 条连接,丢包只卡住一条;HTTP/2 把所有请求挤进一条 TCP 连接,丢一次包全部卡住。

**协议版本没生效却不自知。**Nginx 监听参数里少了 http2,或 TLS 握手没协商出 ALPN,浏览器会静默降级回 1.1,页面照样能打开,只是快不起来。这种问题只能靠 DevTools 的 Protocol 列或 curl 抓出来。

**keep-alive 两端超时不一致。**Nginx 到后端开着 keepalive 连接池,Tomcat 的 keep-alive 超时却比 Nginx 短,Nginx 拿一条已被后端关掉的连接发请求,就会出现偶发 502 或 connection reset。两头超时要配成上游略长于下游。

**把 Server Push 当长期依赖。**它的实际收益长期低于预期,Chrome 从 106 版本起已移除支持,新设计用 preload 或 103 Early Hints 代替。

**默认信任 0-RTT 的重放安全性。**QUIC 重连时的 0-RTT 数据会在服务器确认身份之前就被处理,攻击者可以原样重放这段数据来重复触发操作。下单、支付这类非幂等请求不能放进 0-RTT,需要在应用层单独拦截。

六、验证方式

每条演进结论都能在本机验证。

验证长连接:curl -v 连续请求同一站点两次,输出里第二次会出现 Re-using existing connection;加 --http1.0 再试,两次都会新建连接。

验证当前页面的协议版本:DevTools 的 Network 面板,表头右键勾选 Protocol 列,能看到每个请求走的是 h2 还是 http/1.1。

验证队头阻塞:1.1 下对同一域名并发请求一个大文件和几个小文件,观察小文件的 Queueing 和 Stalled 时间;同样的请求切到 HTTP/2,小文件不再排在大文件后面。

连接迁移的完整验证需要 QUIC 环境,成本较高,可以退而求其次:读 RFC 9000 里连接 ID 的章节,或用 wireshark 抓 QUIC 包看 Initial 包里的连接 ID 字段。

七、总结

一个可迁移的方法:看协议演进不要背特性表,先问当时卡在哪个瓶颈。1.0 卡在连接,1.1 卡在排队,2.0 卡在 TCP,3.0 把传输层一起换掉。带着这个框架去看 Nginx、Tomcat、Netty 的连接管理配置,或者去答面试题,都会顺很多。

相关推荐
Multipath7123 小时前
乾元通轨道交通多链路聚合通信方案
网络·网络协议·5g·智能路由器·信息与通信
YumiProxy4 小时前
代理IP自动切换避坑指南:会话保持、请求头与错误处理实战
网络·网络协议·tcp/ip·代理模式·ip
为思念酝酿的痛5 小时前
NAT技术--网络地址转换原理
网络·计算机网络·智能路由器
发光小北5 小时前
CANOPEN 转Modbus 在现场应用中有什么问题?
网络协议
发光小北13 小时前
四路can转WIFI在现场应用中有什么问题?
网络协议
长按助力退休20 小时前
HTTP缓存从强缓存到协商缓存,前端性能优化第一步
http
刘马想放假1 天前
网络链路备份与高可用技术详解:从 Dual-WAN、LACP 到 SD-WAN 与 MPTCP
网络协议
Zelman1 天前
第07章-数据中心网络
后端·网络协议
网络小江1 天前
上网第四十八课:抓包实战入门——Wireshark 读懂第一个包
网络协议