HTTP发展历程
为了满足好奇心吧, 简单介绍下HTTP
的各个版本. 了解HTTP
协议是如何一步步发展至今的.
这篇文章不会涉及太多原理
HTTP/0.9
HTTP/0.9 诞生于1991年, 甚至都没有RFC
标准, 就是一个简单的文档.
这个版本的HTTP
协议, 只是简单规定了:
- 请求内容为:
GET /a/b/c.html
- 默认端口80
- 响应内容直接传输字符流
没了. 注意, 此时还没有请求体. 就是单纯的请求HTML
页面, 相当之简单了.
HTTP/1.0
随着互联网的发展, 只是单纯的HTML
页面已经不能满足当下的需求了. 还需要js/css/img/audio
等等资源文件.
那么, 如何使得HTTP
协议能够传输多种文件类型呢? HTTP/1.0
登场了, 1.0版本也有了对应的RFC 文档.
简单说来, 这个版本已经是我们常见的格式了, 其请求内容如:
// 请求信息:
GET /favicon.ico HTTP/1.0
Accept: image/avif
// 响应信息
HTTP/1.1 200 OK
Content-Length: 19933
Content-Type: image/gif
增加的内容大致如下:
- 增加了请求header.
- 响应内容增加了响应码
- 请求方式不再局限于
GET
, 增加了POST
PUT
等
这样一来, 就更加灵活了, 比如:
POST
等方法增加了请求的灵活性- 增加了响应码, 当请求失败的时候, 可以通过响应码传递失败原因
- header 的增加使得请求和响应可以传递各种各样协商好的信息, 比如:
- 指定文件类型
- 指定压缩算法, 可以减少体积, 降低网络传输时间
- 灵活的 header 也为未来的升级做好了支持
HTTP/1.1
HTTP/1.0
最大的问题是传输速率. 每次发起一个请求, 都要经过 TCP 三次握手->数据传输->TCP挥手
的完整过程. 随着页面要加载的文件越来越多, 就导致了页面加载时间较长. 因此, 升级后多个请求可公用一个 TCP 连接, 省掉了频繁建立和释放连接的时间.
不过HTTP/1.1
的请求体与响应体倒没有什么大的变化, 升级主要通过header
实现.
简单列举一些新增的header
:
Connection
: 标记连接方式,keep-alive
开启持久连接. 比如谷歌浏览器针对每个域名会同时开启6个 TCP 连接Host
: 标记域名, 可支持多个域名同一 IPCookie
: 增加客户端的数据存储Cache-Control
: 浏览器缓存静态资源
更详细的内容可查看RFC 文档
, HTTP/1.1`协议每隔几年就会进行更新, 其目前的更新文档那个大致如下: 1997年1月 - 1999年6月 - 2014年6月 - 2022年6月
HTTP/1.1
版本也是现在普遍使用的版本了.
HTTP/2
那么HTTP/1.1
已经完美了么? 这世上哪会有完美的东西, HTTP/1.1
还存在以下问题(可能列举的并不全面):
- 连接阻塞.
HTTP/1.1
多个请求可以复用同一个 TCP 连接, 但是多个请求是顺序请求的- 如果一个请求阻塞了5秒, 那么后面排队的请求都要延迟等待5秒. (这就是浏览器F12看到的请求排队时间)
- 无法指定请求的优先级.
- 在
HTTP/1.1
中发起多个请求时, 无法指定请求的顺序. - 比如在一个页面中,
js/css
等文件下载完毕, 页面整体就已经可以限时了, 图片/音频/视频 等内容的优先级可以低一些.
- 在
HTTP/1.1
是单向通信的, 服务器无法主动给浏览器发送消息.- 等等
为了解决这些问题, 推出了HTTP/2. 其前身是谷歌的实验性协议SPDY.
HTTP/2
在1.1
的基础上, 增加了:
- 多路复用. 多个请求复用同一个 TCP连接时, 无需排队, 可以并行发送. 给每个响应包标识请求 ID, 浏览器拿到后再进行组装
- 也基于此, 浏览器也无需开启多个 TCP 连接了, 所有请求可以公用同一个 TCP 连接. 也减少了多个连接的网络竞争.
- 可以设置请求的优先级. 可以给请求设置不同的权重, 以优先响应高权重的请求
- 允许服务器主动推送. 注意, 这个推送可不是
websocket
.- 浏览器请求
HTML
页面的时候, 解析后发现还需要请求相关的js/css
文件, 然后再去请求资源文件. 这中间就存在一定的时间消耗 - 现在, 浏览器请求
HTML
页面的时候, 服务器可以同时将相关的资源文件主动发给浏览器, 极大缩短页面的加载时间.
- 浏览器请求
- 头部压缩. 每个请求都会携带请求头, 如果请求头中存在
Cookie
可能就更大了. - 为了减少这些开销,
HTTP/2
使用HPACK对请求头及响应头进行压缩. (其实就是个霍夫曼编码)
等等吧, 更加详细的内容请查看其RFC 文档. 据说升级HTTP/2
之后能够带来20%~60%的效率提升.
另外, 我找到了一份HTTP/2
各个语言的实现列表, 可以简单看一下.
HTTP/3
HTTP/2
还没普及, 就已经推出新的协议来解决HTTP/2
的问题了. 那么HTTP/2
存在什么问题呢?
- TCP丢包阻塞.
- TCP 连接为了保证传输数据的顺序与可靠, 当出现丢包时会进行重传.
HTTP/2
使用同一个 TCP 连接发送多个请求的数据- 一旦其中一个请求发生丢包, 就会阻塞该连接的所有后续请求
- 这不同于
HTTP/1.1
的阻塞.HTTP/1.1
的阻塞是应用层的阻塞, 而HTTP/2
的阻塞是传输层的阻塞. - 据说, 经过测试, 当连接的丢包率大于2%时,
HTTP/2
的性能会低于HTTP/1.1
. 不过我没有找到测试数据.
- 建立连接耗时.
- TCP 请求的建立需要通过三次握手. 需要1.5RTT(往返时间)
- 如果使用 HTTPS 协议, 那么 TLS 也需要握手,
TLS1.3
需要2次, 即1RTT - 那么每次建立请求都需要3.5RTT, 网速较慢时, 握手时间甚至可能长达300ms
那么如果解决这些问题呢? 可以看到, 问题主要发生在传输层, TCP协议是无法轻易改动了. 此时, 又是谷歌出手了, 设计了QUIC, 采用UDP 进行数据传输. 在应用层来实现了原来传输层的功能. 因为基于 UDP 实现, 甚至握手阶段可以做到0RTT.
RFC 在今年六月份发布的HTTP/3也正是基于QUIC
.
其使用 UDP 替换了 TCP 协议, 至于其具体的实现原理, 暂时按下不表. 感兴趣的可以查看其RFC 文档详细了解.
对于我们日常开发来说, 优化的方向更多是在接口本身的响应时间. 但同时, 已经有先驱们在想着如何让数据传输的更快了, 膜拜. 不过要想等到HTTP/2
HTTP/3
普及, 估计也是 n 年后的事情了吧.