引言
HTTP是一个应用层协议,由请求和响应构成,是一个标准的client-server模型。
我们先看一下 [http版本草案演进图]
下面我们从主要变更中看它的变化
一、早期 HTTP/0.9 – 单行协议
请求由单行指令构成,以唯一可用方法GET开头,其后跟目标资源的路径(一旦连接到服务器,协议、服务器、端口号这些都不是必须的)。
GET /mypage.html
响应也极其简单的:只包含响应文档本身。
<HTML>
这是一个非常简单的HTML页面
</HTML>
HTTP/0.9 的响应内容并不包含HTTP头,这意味着只有HTML文件可以传送,无法传输其他类型的文件;也没有状态码或错误代码:一旦出现问题,一个特殊的包含问题描述信息的HTML文件将被发回。
二、HTTP/1.x
由于 HTTP/0.9 协议的应用十分有限,浏览器和服务器迅速扩展内容使其用途更广,http也在发展。 http 1.x 也经历两次大的演变。
1、HTTP/1.0 - 构建可扩展性
和http0.x相比改变的地方,主要体现在下面几个地方:
- 协议版本信息现在会随着每个请求发送(HTTP/1.0被追加到了GET行)
- 状态码会在响应开始时发送,使浏览器能了解请求执行成功或失败,并相应调整行为(如更新或使用本地缓存)
- 引入了HTTP头的概念,无论是对于请求还是响应,允许传输元数据,使协议变得非常灵活,更具扩展性。
- 在新HTTP头(
Content-Type
)的帮助下,具备了传输除纯文本HTML文件以外其他类型文档的能力
一个典型的HTTP/1.0请求看起来就像这样:
GET /mypage.html HTTP/1.0
User-Agent: NCSA_Mosaic/2.0 (Windows 3.1)
200 OK
Date: Tue, 15 Nov 1994 08:12:31 GMT
Server: CERN/3.0 libwww/2.17
Content-Type: text/html
<HTML>
一个包含图片的页面
<IMG SRC="/myimage.gif">
</HTML>
接下来是第二个连接,请求获取图片:
GET /myimage.gif HTTP/1.0
User-Agent: NCSA_Mosaic/2.0 (Windows 3.1)
200 OK
Date: Tue, 15 Nov 1994 08:12:32 GMT
Server: CERN/3.0 libwww/2.17
Content-Type: text/gif
(这里是图片内容)
http1.0是狭义的,只是一种尝试,并不是官方标准,在发布后的几个月后,就发布了HTTP1.1标准
2、HTTP/1.1 – 标准化的协议
HTTP/1.1 消除了大量歧义内容并引入了多项改进。HTTP/1.1 就是我们比较熟悉的协议了。在这个版本中主要做了下面几个改动:
- 长连接
1、支持长连接(
PersistentConnection
)和请求的管线化(Pipelining
)处理, 在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟, 默认开启Connection: keep-alive,HTTP1.0要自己设置 - 支持响应分块,新增了24个错误状态响应码
- 引入了更多的缓存控制策略
- 引入内容协商机制,包括语言,编码,类型等,并允许客户端和服务器之间约定以最合适的内容进行交换
- host+端口,指定具体访问站点,能够使不同域名配置在同一个IP地址的服务器上、
那么在 HTTP/1.1 时代,浏览器是如何提高页面加载效率的呢?主要有下面两点:
- 维持和服务器已经建立的 TCP 连接,在同一连接上顺序处理多个请求。
- 和服务器建立多个 TCP 连接。
3、http/1.x的缺陷
- 单个 TCP 连接在同一时刻只能处理一个请求 也就是说任意两个 HTTP 请求从开始到结束的时间在同一个 TCP 连接里不能重叠。
虽然 HTTP/1.1 规范中规定了Pipelining
来试图解决这个问题,但是这个功能在浏览器中默认是关闭的
关于Pipelining:
A client that supports persistent connections MAY "pipeline" its requests (i.e., send multiple requests without waiting for each response). A server MUST send its responses to those requests in the same order that the requests were received. 一个支持持久连接的客户端可以在一个连接中发送多个请求(不需要等待任意请求的响应)。收到请求的服务器必须按照请求收到的顺序发送响应。
但是问题来了:
由于 HTTP/1.1 是个文本协议,同时返回的内容也并不能区分对应于哪个发送的请求,所以顺序必须维持一致。比如你向服务器发送了两个请求 GET/query?q=A 和 GET/query?q=B,服务器返回了两个结果,浏览器是没有办法根据响应结果来判断响应对应于哪一个请求的。
Pipelining 这种设想看起来比较美好,但是在实践中会出现许多问题:
- 一些代理服务器不能正确的处理 HTTP Pipelining。
- 正确的流水线实现是复杂的。
- Head-of-line Blocking 连接头阻塞:在建立起一个 TCP 连接之后,假设客户端在这个连接连续向服务器发送了几个请求。按照标准,服务器应该按照收到请求的顺序返回结果,假设服务器在处理首个请求时花费了大量时间,那么后面所有的请求都需要等着首个请求结束才能响应。
所以现代浏览器默认是不开启 HTTP Pipelining 的。
三、SPDY 协议
因为HTTP/1.x的问题,我们会引入雪碧图、将小图内联、使用多个域名等等的方式来提高性能。不过这些优化都绕开了协议,直到2009年,google公开了自行研发的 SPDY 协议,主要解决HTTP/1.1效率不高的问题。谷歌推出SPDY,才算是正式改造HTTP协议本身。降低延迟,压缩header等等,SPDY的实践证明了这些优化的效果,也最终带来HTTP/2的诞生。
SPDY 协议在Chrome浏览器上证明可行以后,就被当作 HTTP/2 的基础,主要特性都在 HTTP/2 之中得到继承。
四、HTTP/2 - SPDY的升级版
2015年,HTTP/2 发布。HTTP/2是现行HTTP协议(HTTP/1.x)的替代,但它不是重写,HTTP方法/状态码/语义都与HTTP/1.x一样。HTTP/2基于SPDY3,专注于性能,最大的一个目标是在用户和网站间只用一个连接(connection)。
(一)HTTP/2 作为SPDY的升级版和SPDY的主要区别在:
- HTTP2.0 支持明文 HTTP 传输,而 SPDY 强制使用 HTTPS
- HTTP2.0 消息头的压缩算法采用 HPACK,而非 SPDY 采用的 DEFLATE
- 更加完善的协议商讨和确认流程
- ...
(二)HTTP2.0和HTTP1.X相比的新特性
- 二进制协议而不是文本协议,引入二进制数据帧和流的概念。
- 遵循多路复用,代替同一host下的内容,只建立一次连接。并行的请求能在同一个链接中处理,移除了HTTP/1.x中顺序和阻塞的约束
- 压缩了headers,移除了重复和传输重复数据的成本,Http/1.x则不论什么请求都会发送
- 允许服务器预先将网页所需要的资源PUSH到浏览器的内存当中
h2优异的性能变现,我们举个著名的例子:
同时请求 379 张图片,从Load time 的对比我们来看一下HTTP 1.x 和 HTTP/2的区别 http2.akamai.com/demo
(三)http/1怎么升级http/2?
HTTP2.0其实可以支持非HTTPS的,但是现在主流的浏览器像chrome,firefox表示还是只支持基于TLS
部署的http/2协议,所以要想升级成http/2还是先升级https
升级到2.0后,直接在ngnix配置文件中启动相应的协议就好
(四)缺陷
同一域名下只需要使用一个 TCP 连接。但当这个连接中出现了丢包的情况。整个 TCP 都要开始等待重传,也就导致了后面的所有数据都被阻塞了。这种单个数据包造成的阻塞,也就是TCP上的队头阻塞
但是对于 HTTP/1.1 来说,可以开启多个 TCP 连接,出现这种情况反到只会影响其中一个连接,剩余的 TCP 连接还可以正常传输数据
这是底层支撑的 TCP 协议造成的,但是TCP 存在的时间实在太长,已经充斥在各种设备中了,并且这个协议是由操作系统实现的,这使得修改它是一件不可完成的任务。
所以并没有彻底解决TCP中队头阻塞的问题
五、HTTP/3 - 另起炉灶
Google 在推SPDY的时候就已经意识到了这些问题,于是就另起炉灶搞了一个基于 UDP(用户数据报协议),又取了 TCP 中的精华的“QUIC”协议,让HTTP跑在QUIC上而不是TCP上。
使用QUIC时,两端间仍然建立一个连接,该连接也经过协商使得数据得到安全且可靠的传输。 当我们在这个连接上建立两个不同的数据流时,它们互相独立。也就是说,如果一个数据流丢包了,只有那个数据流必须停下来,等待重传。
有望彻底解决队头阻塞的问题
但是目前还没有任何主流浏览器的任何状态的任何版本支持IETF(备注4)版本的QUIC或者HTTP/3协议。
尽管Google Chrome在数年前已经支持Google版的QUIC,但是正在开发的大多数新实现已经决定着眼于IETF版本,与Google版本并不兼容。
参考
备注
1、持久性连接 非服务器在返回资源后就自动断开连接。 持久性连接在没有收到显示关闭连接的通知前,会一直保持该连接。 页面上的多数资源能够在一条TCP链接上传输。这样极大的减少了多次TCP连接,减少带来的性能损失。
2、慢启动 为了避免将无法处理的数据包泛洪到网络中,TCP协议对建立的连接使用使用了一种称为"慢启动"的预热暂缓期用来给TCP堵塞控制算法确定可以传输的数据量,而不是在建立连接后尽快发送所有未完成的数据。由于每一个新连接都必须经过这个缓慢的启动过程,这也成了网络性能的一个瓶颈。
3、怎么看网站是用的h2还是h1 window.chrome.loadTimes()输出的connectionInfo 和 npnNegotiatedProtocol 是h2就说明使用的是http2
4、IETF 国际互联网工程任务组。http是IETF和万维网合作的产品