HTTP持久连接与版本演进
概念引入
想象你是一家餐厅的服务员。HTTP/0.9时代,顾客每点一道菜,你都要重新跑一趟厨房(建立新连接),送完菜就关门。到了HTTP/1.0,虽然可以点完整的一桌菜,但送完还是要关门。HTTP/1.1终于学会了"保持连接"——就像服务员站在餐桌旁,顾客随时可以加菜。而HTTP/2和HTTP/3,则像是开了多个窗口、用上了无人机送餐,效率大幅提升。
今天,我们就以广州飞翔科技(官网:www.feixiang.net)的技术升级故事为主线,看看HTTP协议是如何一步步进化的。
核心内容
HTTP版本演进之路
| 版本 | 年份 | 核心特性 | 连接方式 |
|---|---|---|---|
| HTTP/0.9 | 1991 | 只有GET,无头部 | 短连接 |
| HTTP/1.0 | 1996 | 引入头部、POST、状态码 | 默认短连接 |
| HTTP/1.1 | 1997 | 持久连接、管道化、Host头 | 默认长连接 |
| HTTP/2 | 2015 | 二进制分帧、多路复用 | 单TCP连接多流 |
| HTTP/3 | 2022 | 基于QUIC/UDP | 0-RTT握手 |
持久连接(Persistent Connection):TCP连接建立后,可以复用该连接发送多个HTTP请求/响应,不必每次新建连接。
多路复用(Multiplexing):在单一连接上同时传输多个独立的请求/响应流,互不阻塞。
QUIC(Quick UDP Internet Connections):基于UDP构建的传输协议,内置TLS加密,解决TCP队头阻塞问题。
HTTP/1.0的短连接之痛
飞翔公司早期官网使用HTTP/1.0。每次用户访问首页,浏览器需要:
- 建立TCP连接(三次握手)
- 发送HTTP请求
- 接收响应
- 关闭TCP连接(四次挥手)
如果页面有10张图片、2个CSS、3个JS文件,就要重复15次"握手-请求-响应-挥手"!就像航仔(后端)每次给图妹(产品经理)递文件都要重新敲门、自我介绍、递完走人——效率极低。
// HTTP/1.0 请求示例
GET /index.html HTTP/1.0
Host: www.feixiang.net
Connection: close ← 明确告诉服务器:发完就关
HTTP/1.0 200 OK
Content-Type: text/html
Connection: close ← 服务器回应:好的,关闭
<html>...</html>
// 连接关闭,下次请求重新握手
HTTP/1.1 持久连接:keep-alive
HTTP/1.1 默认启用持久连接(Persistent Connection),Connection: keep-alive 已内置于协议规范,客户端和服务器无需显式声明即可复用 TCP 连接。部分旧实现或调试场景下显式写出 Connection: keep-alive 仅为兼容或清晰表达意图。
// HTTP/1.1 持久连接请求
GET /logo.png HTTP/1.1
Host: www.feixiang.net
Connection: keep-alive ← 请求保持连接
HTTP/1.1 200 OK
Content-Type: image/png
Connection: keep-alive ← 同意保持
// 连接不关闭,继续发送下一个请求
GET /style.css HTTP/1.1
Host: www.feixiang.net
凌叔(运维)算了一笔账:启用keep-alive后,飞翔官网的TCP握手次数减少了80%,页面加载时间从3秒降到1.5秒。
流水线方式(Pipelining):理想很丰满
HTTP/1.1还支持流水线(Pipelining)——浏览器可以连续发送多个请求,不必等上一个响应回来。
浏览器: 请求A → 请求B → 请求C
服务器: 响应A → 响应B → 响应C
但流水线有个致命缺陷:队头阻塞(Head-of-Line Blocking)。如果请求A的响应很大或很慢,请求B和C就算已经处理好了,也只能排队等着。
就像飞翔公司食堂打饭,虽然你可以一次性报三个菜名,但如果第一个菜要现炒5分钟,后面两个已经装好的菜也只能干等着。
实际中,浏览器基本默认关闭Pipelining,所以HTTP/1.1本质上还是串行请求。
HTTP/2:二进制分帧与多路复用
翼王(架构师)拍板:飞翔官网升级到HTTP/2!
HTTP/2做了革命性改变:
- 二进制分帧:将请求/响应拆分成更小的帧(Frame),不再是纯文本
- 多路复用:多个请求/响应在同一个TCP连接上交错传输
- 头部压缩(HPACK):减少重复头部传输
HTTP/2中,每个请求分配一个流(Stream),有唯一的Stream ID。帧头部携带Stream ID,服务器收到后可以重新组装。即使Stream 1的响应慢,Stream 3的帧照样可以传输。
HTTP/3:基于QUIC的飞跃
HTTP/2解决了HTTP层的队头阻塞,但TCP层的队头阻塞依然存在——如果TCP丢了一个包,所有流都要等重传。
HTTP/3彻底抛弃TCP,改用QUIC协议(基于UDP):
- 每个流独立传输,一个流丢包不影响其他流
- 内置TLS 1.3,握手更快(0-RTT或1-RTT)
- 连接迁移:IP变了连接也不断
风速(算法)测试发现:在弱网环境下,HTTP/3比HTTP/2快了40%。飞翔公司的移动端用户明显感受到页面加载更流畅了。
飞翔公司官网升级之路
空少(前端)感慨:"以前HTTP/1.1时代,我要把10个小图标拼成一张雪碧图来减少请求数。HTTP/2之后,直接发10个请求反而更快,因为多路复用不阻塞!"
本篇小结
- HTTP/0.9 极简,只有GET,无头部信息
- HTTP/1.0 引入头部和状态码,但默认短连接,每次请求都新建TCP连接
- HTTP/1.1 默认启用持久连接(
Connection: keep-alive),支持流水线但存在队头阻塞问题 - HTTP/2 采用二进制分帧和多路复用,多个请求在同一个TCP连接上并行传输,彻底解决HTTP层队头阻塞
- HTTP/3 基于QUIC和UDP,消除TCP层队头阻塞,握手更快,支持连接迁移
- 飞翔公司从HTTP/1.0 → 1.1 → 2 → 3的升级,页面加载时间从3s → 1.5s → 0.8s → 0.5s
动手实践
温馨提示: 以下实践示例中涉及的域名(如
www.feixiang.net)、公司场景和接口均为虚构数据,仅用于演示协议原理,实际执行时可能不会产生文档中描述的效果。建议将命令中的域名替换为你自己可访问的真实地址进行练习。
实践:查看网站支持的HTTP版本
# 使用curl查看HTTP/2支持情况
curl -I --http2 https://www.feixiang.net
# 强制使用HTTP/1.1并观察连接头
curl -I --http1.1 https://www.feixiang.net
实践:用Chrome开发者工具观察协议版本
- 打开Chrome,按F12进入Network面板
- 右键表头,勾选"Protocol"列
- 访问
www.feixiang.net,观察各资源的协议是h2(HTTP/2)还是h3(HTTP/3)
实践:思考题
飞翔公司官网首页需要加载
index.html、main.css、app.js、banner.jpg、logo.png共5个资源。在HTTP/1.1(无流水线)和HTTP/2环境下,分别需要几次TCP握手?为什么?
实践:用curl测试HTTP/3(需支持HTTP/3的curl版本)
# 测试HTTP/3连接
curl --http3-only -I https://www.feixiang.net