快速掌握 HTTP 1.0 1.1 2.0 3.0的特点及其区别

992 阅读12分钟

点赞再看,养成习惯,公众号搜一搜【一角钱技术】关注更多原创技术文章。本文 GitHub org_hejianhui/JavaStudy 已收录,有我的系列文章。

HTTP1.0

1.0的HTTP版本,是一种无状态,无连接的应用层协议。 HTTP1.0规定浏览器和服务器保持短暂的链接。

浏览器每次请求都需要与服务器建立一个TCP连接,服务器处理完成以后立即断开TCP连接(无连接),服务器不跟踪也每个客户单,也不记录过去的请求(无状态)。

这种无状态性可以借助cookie/session机制来做身份认证和状态记录。

HTTP1.0存在的问题

无法复用连接

每次发送请求,都需要进行一次TCP连接,而TCP的连接释放过程又是比较费事的。这种无连接的特性会使得网络的利用率变低。

队头阻塞(head of line blocking)

由于HTTP1.0规定下一个请求必须在前一个请求响应到达之前才能发送,假设前一个请求响应一直不到达,那么下一个请求就不发送,后面的请求就阻塞了。

HTTP1.1

HTTP1.1继承了HTTP1.0的简单,克服了HTTP1.0性能上的问题。

长连接

HTTP1.1增加Connection字段,通过设置Keep-Alive保持HTTP连接不断卡。避免每次客户端与服务器请求都要重复建立释放建立TCP连接。提高了网络的利用率。

如果客户端想关闭HTTP连接,可以在请求头中携带Connection:false来告知服务器关闭请求。

管道化(pipelining)— 尴尬的假并行传输

HTTP1.1支持请求管道化(pipelining)。

基于HTTP1.1的长连接,使得请求管线化成为可能。 管线化使得请求能够**“并行”传输**。

例如

假如响应的主体是一个html页面,页面中包含了很多img,这个时候keep-alive就了很大作用。能够“并行”发送多个请求。(注意,这里的“并行”并不是真正意义上的并行传输

需要注意的是:服务器必须按照客户端请求的先后顺序依次回送相应的结果,以保证客户端能够区分出每次请求的响应内容。

也就是说,HTTP管道化可以让我们把先进先出队列从客户端(请求队列)迁移到服务端(响应队列)

如果,客户端同时发了两个请求分别获取html和css,假如说服务器的css资源先准备就绪,服务器也会先发送html,再发送css。 换句话来说,只有等到html响应的资源完全传输完毕后,css响应的资源才开始传输,不允许同时存在两个并行的响应。

可见,HTTP1.1还是无法解决队头阻塞(head of line blocking)的问题。同时“管道化”技术存在各种各样的问题,所以很多浏览器要么根本不支持它,要么直接默认关闭,并且开启的条件很苛刻……而且好像实际也没有什么用处。

真并行传输 — 浏览器优化策略

HTTP1.1支持管道化,但是服务器也必须进行逐个响应的送回,这个是很大的一个缺陷。实际上,现阶段的浏览器厂商采取了另外一种做法,它允许我们打开多个TCP的会话,也就是说,上图我们看到的并行,其实是不同的TCP连接上的HTTP请求和相应。这才是真正的并行!

很多人以为的连接数情况:

image.png

实际情况(china):

image.png

缓存处理 — 强缓存、协商缓存,启发式缓存(新增)

此外,HTTP1.1还加入了缓存处理(强缓存和协商缓存),新的字段如cache-control,支持断点传输,以及增加了Host字段(使得一个服务器能够用来创建多个Web站点)

HTTP2.0

二进制分帧

HTTP2.0通过在应用层和传输层之间增加一个二进制分层帧,突破了HTTP1.1的性能限制,改进传输性能。

多路复用(链接共享)— 真并行传输

  • 流(stream):已建立连接上的双向字节流。
  • 消息:与逻辑消息对应的完整的一系列数据帧。
  • 帧(frame):HTTP2.0通信的最小单位,每个帧包含头部,至少也会标识出当前所属的流(stream_id)

所有HTTP2.0通信都在一个TCP链接上完成,这个链接可以撑在任意流量的双向数据流。

每个数据流以消息的形式发送,而消息由一或多个帧组成。这些帧可以乱序发送,然后再根据每个帧头部的流标识符(Stream_id)重新封装。

多路复用(连接共享)可能会导致关键字被阻塞,HTTP2.0里每个数据流都可以设置优先级和依赖,优先级高的数据流会被服务器优先处理和返回客户端,数据流还可以依赖其他的子数据流。

可见,HTTP2.0实现了真正的并行传输,它能够在一个TCP上进行任意数量的HTTP请求。而这个强大的功能基于“二级制分帧”的特性。

头部压缩

在HTTP1.X中,头部元数据都是以纯文本的形式发送的,通常会给每个请求增加500-8000字节的负荷。

比如cookie,默认情况下,浏览器会在每次请求的时候,把cookie附在header上面发给服务器。

HTTP2.0使用encoder来减少需要传输的header大小,通讯双方各自cache一份header_files表,既避免重复header的传输,又减少了需要传输的大小。

高效的压缩算法可以很大的压缩header,减少发送包的数量从而降低延迟。

服务器推送

服务器除了最初请求的响应外,服务器还可以额外向客户端推送资源,而无需客户端明确的需求。

HTTP3.0

Google搞了一个基于UDP协议的QUIC协议,并且使用在了HTTP/3上, HTTP/3之前的名称为HTTP-over-QUIC。

早期Quic协议,存在IETF和Google两个版本,直到它被证实命名为HTTP3.0

IETF的QUIC工作小组创造了QUIC传输协议。QUIC是一个使用UDP来替代TCP的协议。最初的时候,Google开始助力QUIC,其后QUIC更多地被叫做“HTTP/2-encrypted-over-UDP “。

社区中的人们已经使用非正式名称如iQUIC和gQUIC来指代这些不同版本的协议,以将QUIC协议与IETF和Google分开(因为它们在细节上差异很大)。通过“iQUIC”发送HTTP的协议被称为“HQ”(HTTP-over-QUIC)很长一段时间。

2018年11月7日,Litespeed的Dmitri宣布他们和Facebook已经成功地完成了两个HTTP/3实现之间的第一次互操作。Mike Bihop在该主题的HTTPBIS会话中的后续介绍可以在这里看到。会议结束时达成共识称新名称是HTTP/3!

0-RTT — QUIC协议相比HTTP2.0的最大优势

缓存当前会话的上下文,下次恢复会话的时候,只需要将之前的缓存传递给服务器,验证通过,就可以进行传输了。

0-RTT建连可以说是QUIC相比HTTP2最大的性能优势。

什么是0-RTT建连

  • 传输层0-RTT就能建立连接
  • 加密层0-RTT就能建立加密连接

多路复用

QUIC基于UDP,一个连接上的多个stream之间没有依赖,即使丢包,只需要重发丢失的包即可,不需要重传整个连接。

更好的移动端表现

QUIC在移动端的表现比TCP好,因为TCP是基于IP识别连接,而QUIC是通过ID识别链接。 无论网络环境如何变化,只要ID不便,就能迅速重新连上。

加密认证的根文 — 武装到牙齿

TCP协议头没有经过任何加密和认证,在传输过程中很容易被中间网络设备篡改、注入和窃听。

QUIC的packet可以说武装到了牙齿,除了个别报文,比如PUBLIC_RESET和CHLO,所有报文头部都是经过认证的,报文Body都是经过加密的。

所以只要对 QUIC 做任何更改,接收端都能及时发现,有效地降低了安全风险。

向前纠错机制

QUIC协议有一个非常独特的特性,成为向前纠错(Foward Error Connec,FEC),每个数据包除了它本身的内容之外还包括了其他数据包的数据,因此少量的丢包可以通过其他包的冗余数据直接组装而无需重传。

向前纠错牺牲了每个数据包可以发送数据的上限,但是带来的提升大于丢包导致的数据重传,因为数据重传将会消耗更多的时间(包括确认数据包丢失,请求重传,等待新数据包等步骤的时间消耗)。

例如:

  • 我总共发送三个包,协议会算出这个三个包的异或值并单独发出一个校验包,也就是总共发出了四个包。
  • 当其中出现了非校验包丢失的情况,可以通过另外三个包计算出丢失的数据包的内容。
  • 当然这种技术只能使用在丢失一个包的情况下,如果出现丢失多个包,就不能使用纠错机制了,只能使用重传的方式了。

问题归纳

HTTP1.1的合并请求(如CSSsprites)是否适用于HTTP2.0

没有必要。

在头部压缩技术总,客户端和服务器均会维护两份相同的静态字典动态字典

在静态字典中,包含了常见的头部名称与值的组合。静态字典在首次请求时可以使用。那么现在头部的字段就可以被简写成静态字典中相应字段的index。

而动态字典跟连接的上下文相关,每个HTTP/2连接维护的动态字典不尽相同。动态字典可以在连接不停地进行更新。

也就是说,原本完整的HTTP报文头部的键值或字段,由于字典的存在,现在可以转换成索引index,在相应的端再进行查找还原,也就起到了压缩的作用。

所以,同一个链接上产生的请求和响应越多,动态字典累积得越全,头部压缩的效果也就越好,所以针对HTTP/2网站,最佳实践是不要合并资源

另外,HTTP2.0多路复用,使得请求可以并行传输,而HTTP1.1合并请求的一个原因也是为了防止过多的HTTP请求带来的阻塞问题。而现在HTTP2.0已经能够并行传输了,所以合并请求也就没有必要了。

为什么要有HTTP3.0:HTTP/2底层TCP的局限带来的问题

由于HTTP/2使用了多路复用,一般来说,同一个域名下只需要使用一个TCP链接,但当这个连接中出现了丢包的情况,就会导致HTTP/2的表现情况反倒不如HTTP/2了。

原因是: 在出现丢包的额情况下,整个TCP都要开始等待重传,导致后面的所有数据都被阻塞。

但是对于HTTP/1.1来说,可以开启多个TCP连接,出现这种情况只会影响其中一个连接,剩余的TCP链接还可以正常传输数据。

由于修改TCP协议是不可能完成的任务。

如何在Chrome中启用 QUIC 协议

MTF在资源服务器和内容分发节点都已经启用了 HTTP3.0 协议,根据 用户浏览器 向下兼容,强烈建议您在Chrome浏览器开启实验性QUICK协议支持,体验加速效果:

  1. 在浏览器地址栏:输入chrome://flags
  2. 找到Experimental QUIC protocol,将Default改为Enabled
image.png

总结

HTTP 1.0

  • 无状态,无连接
  • 短连接:每次发送请求都要重新建立tcp请求,即三次握手,非常浪费性能
  • 无host头域,也就是http请求头里的host,
  • 不允许断点续传,而且不能只传输对象的一部分,要求传输整个对象

HTTP 1.1

  • 长连接,流水线,使用connection:keep-alive使用长连接
  • 请求管道化
  • 增加缓存处理(新的字段如cache-control)
  • 增加Host字段,支持断点传输等
  • 由于长连接会给服务器造成压力

HTTP 2.0

  • 二进制分帧
  • 头部压缩,双方各自维护一个header的索引表,使得不需要直接发送值,通过发送key缩减头部大小
  • 多路复用(或连接共享),使用多个stream,每个stream又分帧传输,使得一个tcp连接能够处理多个http请求
  • 服务器推送(Sever push)

HTTP 3.0

  • 基于google的QUIC协议,而quic协议是使用udp实现的
  • 减少了tcp三次握手时间,以及tls握手时间
  • 解决了http 2.0中前一个stream丢包导致后一个stream被阻塞的问题
  • 优化了重传策略,重传包和原包的编号不同,降低后续重传计算的消耗
  • 连接迁移,不再用tcp四元组确定一个连接,而是用一个64位随机数来确定这个连接
  • 更合适的流量控制
  • 基于UDP实现
  • 0RTT建连
  • 基于UDP的多路复用
  • 加密认证的报文
  • 向前纠错机制

文章持续更新,可以公众号搜一搜「 一角钱技术 」第一时间阅读, 本文 GitHub org_hejianhui/JavaStudy 已经收录,欢迎 Star。