学习了解一个技术,最好的方法是了解它的来龙去脉,前世今生,对于 HTTP 这项不断打补丁的技术更是如此。下面就从 HTTP 的发展历史来了解下 HTTP 吧
HTTP 的发展历史
诞生背景(1989-1991)
蒂姆·伯纳斯-李在欧洲核子研究中心(CERN)工作时,为了让世界各地的科学家们能够方便地共享文档,发明了:
- HTML:文档格式
- HTTP:传输协议
- URL:资源定位符
这就是万维网(World Wide Web)的雏形。
HTTP/0.9(1991)- 单行协议
最早的 HTTP 协议极其简单:
- 只支持 GET 方法
- 只能获取文本内容
- 完成即断开连接(无状态)
例如:
GET /index.html
<html>
这是一个简单的 HTML 文件
</html>
HTTP/0.9 - 最简单的请求响应
想象你在餐厅点一杯水:
// 请求
GET /water
// 响应(只能返回文本)
这是一杯水
没有状态码,没有头信息,完全单向沟通。就像你只能说"要水",服务员只能给你水。
HTTP/1.0(1996)- 构建可扩展性
为了支持多种类型的文档,HTTP/1.0 新增了很多重要的特性:
- 增加了 POST、HEAD 等方法
- 增加了响应状态码(200、404 等)
- 引入了 HTTP 头部(Header)
- 支持多种文档类型(Content-Type)
但仍存在问题:
- 每个请求都需要重新建立 TCP 连接
- 每个连接只能处理一个请求
- 服务器压力大,浏览网站速度慢
例如访问一个网页:
建立连接 -> 请求 HTML -> 断开连接
建立连接 -> 请求 CSS -> 断开连接
建立连接 -> 请求 JS -> 断开连接
建立连接 -> 请求图片 -> 断开连接
HTTP/1.0 - 增加了更多信息
现在像正常点餐了:
// 请求
GET /menu.html HTTP/1.0
Host: restaurant.com
Accept: text/html
User-Agent: Mozilla/4.0
// 响应
HTTP/1.0 200 OK
Content-Type: text/html
Content-Length: 137
<html>
<head><title>菜单</title></head>
<body>
<h1>今日特色</h1>
<p>红烧肉 - ¥38</p>
</body>
</html>
可以说明要什么格式,服务器也会告诉你状态(比如:200 成功,404 没找到)。
HTTP/1.1(1997)- 标准化的协议
HTTP/1.1 解决了很多 1.0 的问题:
- 长连接(Keep-Alive)
建立连接 -> 请求 HTML
-> 请求 CSS
-> 请求 JS
-> 请求图片
- 一个 TCP 连接可以发送多个请求
- 减少了建立和断开连接的开销
2. 管道机制(Pipeline)
请求:HTML、CSS、JS、图片 ->
响应:<- HTML、<- CSS、<- JS、<- 图片
- 允许在同一个连接中同时发送多个请求
- 但服务器必须按请求顺序返回响应
3. 新增方法 - PUT - DELETE - OPTIONS - TRACE
但仍有一些问题:
- 队头阻塞:前面的请求没处理完,后面的请求就要等待
- 无法复用连接:每个域名一般最多维护 6 个连接
- 头部冗余:每个请求都要携带完整的头部信息
HTTP/1.1 - 可以保持连接
像在餐厅吃饭,不用每次点菜都重新入座:
// 第一个请求
GET /appetizer.html HTTP/1.1
Host: restaurant.com
Connection: keep-alive
// 第一个响应
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 50
<div>您的开胃菜:沙拉</div>
// 第二个请求(同一个连接)
GET /main-course.html HTTP/1.1
Host: restaurant.com
// 第二个响应
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 52
<div>您的主菜:牛排</div>
HTTP/2(2015)- 更快的协议
HTTP/2 主要解决了性能问题:
- 多路复用(Multiplexing)
请求:HTML ->
CSS -> 响应可以乱序返回
JS -> 互不影响
图片 ->
- 一个连接可以并发处理多个请求和响应
- 解决了队头阻塞问题
-
二进制分帧
- 将信息分割为更小的帧,提高传输效率
- 可以乱序发送,根据标识重组
-
头部压缩(HPACK)
- 使用压缩算法减少头部体积
- 建立头部索引表,重复的头部只发送索引
-
服务器推送
- 服务器可以主动推送资源
- 比如:请求 HTML 时,服务器直接推送 CSS 和 JS
HTTP/2 - 多路复用
像一个超级服务员,可以同时处理多个需求:
// 并发请求
:method GET
:path /index.html
:scheme https
:authority website.com
:method GET
:path /style.css
:scheme https
:authority website.com
:method GET
:path /script.js
:scheme https
:authority website.com
// 响应可以乱序返回
// 帧1(CSS响应)
:status 200
content-type text/css
{CSS内容}
// 帧2(JS响应)
:status 200
content-type application/javascript
{JS内容}
// 帧3(HTML响应)
:status 200
content-type text/html
{HTML内容}
HTTP/3(2022)- 基于 UDP 的协议
为了解决 TCP 的一些固有问题,HTTP/3 基于 UDP 协议重新设计:
- 更快的建立连接
- TCP 需要三次握手
- UDP 基于 QUIC 协议,只需要一次握手
- 解决了 TCP 队头阻塞
- TCP 丢包会阻塞整个连接
- UDP 每个流是独立的,某个流的丢包不会影响其他流
总结:HTTP 的发展历史就是不断解决问题的过程
- 0.9:解决了文档共享的基本需求
- 1.0:解决了多种类型文档传输的问题
- 1.1:解决了连接复用的问题
- 2.0:解决了性能问题
- 3.0:解决了传输层的问题
HTTP/3 - 基于 UDP 的全新体验
像是用无人机送餐,更快更灵活:
// QUIC 握手(只需一次)
Client Hello (包含客户端支持的加密套件)
Server Hello (选择加密套件并发送证书)
// 之后可以立即发送数据
:method GET
:path /fast-food.html
:authority quick-restaurant.com
// 即使某个包丢失,其他内容也能正常送达
Stream 1: 汉堡数据
Stream 2: 薯条数据(丢失了一个包,但不影响汉堡的接收)
Stream 3: 可乐数据
有趣的对比:
- HTTP/0.9 像是简单的路边摊
- HTTP/1.0 像是正规餐厅
- HTTP/1.1 像是可以多点几次的自助餐
- HTTP/2 像是多个服务员同时服务
- HTTP/3 像是无人机送餐,即使一架出问题其他的还能送
总结
这个文档从历史发展的角度,介绍了每个版本 HTTP 协议要解决的具体问题,并且用简单的例子和图示来说明。这样前端新人可以更容易理解 HTTP 协议的演进过程,以及为什么需要这些改进。
我们平常开发中,使用最多的还是 HTTP/1.1,因为它是目前最广泛使用的版本。
所以下篇文章我们重点学习 HTTP/1.1 的使用,请求和响应的格式。