HTTP的秘密(2)各版本特性详解 | 8月更文挑战

498 阅读12分钟

HTTP/1.0,协议都在摸索阶段,IETF已经正式发布的大版本有4个:

image.png

我看到网上都没有把HTTPS放到HTTP的版本演进中,我认为考虑到HTTPS的重要性和应用的广泛性,以及它对HTTP/1.1的安全性提升和对HTTP/2.0的铺垫,应该算在HTTP的版本演进中。

  • HTTP/1.0,1996年11月发布,文档号RFC 1945,目前已经过时,它的主要功绩是引入了HTTP消息头(content head)的概念,无论是对于请求还是响应,允许传输元数据(meta),使协议变得更具扩展性,支持除了HTML文件外其他文件的传输。

  • HTTP/1.1,IETF互联网工程任务组在1999年6月发布的 RFC 2616,定义了HTTP协议中现今广泛使用的一个版本,也是时间最长的版本,目前仍有不少网站使用,最引人注目的特性就是“长连接”,同时引入了缓存。

  • HTTPS,超文本传输安全协议(HyperText Transfer Protocol Secure),这是将HTTP传输建立在TLS/SSL层的安全加密传输协议,由在2000年5月公布的RFC 2818正式确定下来。这是一个应用广泛的更新,也成为之后HTTP/2.0的基础,我会在之后单独写一篇文章介绍。

  • HTTP/2.0,是目前最新版本,在2015年5月正式标准化后一直在演进,最关键的特性是引入帧结构,多路复用

  • HTTP/3.0, 仍在开发中...

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>
```

这是HTTP/1.0典型的请求消息,可以看到,它已经包含了head头部字段,并使用状态码。

HTTP/1.0的问题是“短链接”:

  • 短链接的意思是:每发起一个HTTP请求时都会创建一个新的TCP连接,并在收到应答时立即关闭。在95年那会儿来说,这并没有问题。因为网站内容还主要是文本。

  • 随着网站内容的丰富,这样的链接模式会导致3个问题:频繁创建新连接耗时+服务器信令开销+慢启动

    • TCP连接无法复用会导致每次客户端发起HTTP请求都要经历三次握手,这在高延迟的场景下对网页加载速度影响明显。

    • 释放TCP链接时还要握手4次,当网页访问量很大时,对服务器的压力和网络信令风暴都是不小冲击。其实TCP链接本身是可以支持热链接的(hot connection),HTTP短连接破坏了 TCP 具备的能力,新的冷连接降低了TCP性能。TCP热连接指的是活跃的连接,冷连接指新连接。

    • TCP链接的传输窗口有慢启动的(Slow Start),所谓慢启动是指每次TCP接收窗口收到确认ACK时带宽都会增大。但如果发生丢包,TCP就认为这是网络阻塞,就会采取措施减轻网络拥挤。而HTTP频繁断开链接,会导致TCP链接一致处于低带宽状态,对传输大文件有一定影响。

image.png

HTTP/1.1

可以说,HTTP/1.1是为了解决1.0的痛点而生。它支持一下关键特性:

  1. 支持长连接keep-alive

    • 所谓长链接,就是完成一次HTTP请求后,不立即释放TCP链接,而是保持一段时间,重复用于发送一系列HTTP请求。
    • 注意:这并不是说客户端和服务器只建立一个TCP连接,实际上,HTTP/1.1客户端和服务器之间依然会建立多个连接,但是传输效率要比1.0高了很多;
    • 长连接节省了新建 TCP 连接3次握手和释放4次握手的时间和信令开销,大幅降低了页面加载时延,还可以利用TCP的滑动窗口能力,保持足够的传输带宽。
    • 服务器可以设置长连接的最短保持时间,TCP连接在空闲一段时间后才会被释放。
    • 长连接的实现,是通过head中的Connection控制。该字段用来指示客户端与服务器端TCP的连接方式,connection:close则使用短连接,若connection:keep-alive则使用长连接 image.png
  2. 强制要求消息中包含Host头部字段

    • 在 HTTP/1.0 中认为每个IP地址都绑定一个域名,因此请求消息中并没有主机名(hostname)。
    • 但到HTTP/1.1时,一台服务器可以承载多个系统,支持多个host,IP地址不再唯一。增加host字段,能够使不同域名配置在同一个IP地址的服务器上,让互联网主机托管成为可能。
  3. 允许响应数据分块(chunked)

    • 一般情况下,服务器会将响应数据是作为一个整包发给客户端,在message中『Content-Length』字段表示响应消息正文的长度。这个长度必须要准确,如果Content-Length 比实际返回的长度短,那么就会造成内容被截断,如果比实体内容长,客户端就一直处于pendding状态,对于HTTP/1.0短链接影响还不大,但是对于HTTP/1.1长连接,如果前面的响应消息没有完成,会导致后面的数据阻塞,也就是队头拥塞(Head of Line blocking)
    • HTTP/1.1允许服务器将响应数据分块处理并发送,对于大数据文件来说非常有效,服务器发送数据时不再需要整体打包并预先告诉客户端发送内容的总大小,只需在HTTP响应头里面添加Transfer-Encoding: chunked,以此来告诉浏览器我使用的是分块传输编码,在浏览器端会将这些块重新组合。
    • 这样做的优势是,提高了传输效率以及成功率,降低了拥塞和时延。

image.png

  1. HTTP/1.1增加了缓存管理和控制,这个我会在之后文章单独讲

HTTP/1.1的缺点

事情都有两面性,在解决问题的同时,HTTP/1.1也引入了一些新的问题:

  1. 在空闲状态,长连接还是会消耗服务器资源,而且在重负载时,还有可能遭受DDoS攻击。

  2. 对头阻塞HOLB(Head of Line Blocking),长连接中,默认情况下HTTP请求与响应仍然是串行的,即HTTP请求按顺序发出,只有在当前请求收到应答过后,下一个请求才会被发出。如果某个请求出现网络阻塞等问题,会导致同一条TCP连接中的后续请求被阻塞。

    • 因此,HTTP/1.1中提出了pipelining方案:即客户端可以在一个请求发送完成后不等待响应便直接发起第二个请求,服务端在返回响应时会按请求到达的顺序依次返回,这样就可以一定程度上降低延迟。
    • 然而pipelining并没有实质解决HOLB的问题,因为协议规定收到请求的服务器必须按照请求收到的顺序发送响应。也就是说,如果第一个请求被堵塞,服务器即便收到之后的请求,也无法响应。pipelining也因此没有被广泛接受。

image.png

HTTP/2.0

一转眼15年过去了,HTTP/2.0才来到web的世界。相比于HTTP/1.1,HTTP/2.0的升级更彻底。首先HTTP/2.0的连接都是建立在TLS加密传输之上,不再像1.x那样的明文传输。其次,他将HTTP报文转变为了二进制帧,并引入stream机制,一举打破了HTTP串行限制!

二进制分帧Frame

image.png HTTP/2.0在应用层和传输层之间增加一个二进制分帧层binary framing。它的目的是在不改动 HTTP/1.1内容的情况下, 改进传输性能,实现低延迟和高吞吐量:

image.png

如上图,二进制分帧层将 HTTP/1.1的消息分成帧并嵌入到流 (stream) 中。数据帧和报头帧分离,这将允许针对报头进行压缩。

在二进制分帧层中,HTTP/2 会将所有传输的信息分割为更小的消息和帧(frame),并对它们采用二进制格式的编码,其中 HTTP1.1的头部会被封装到 HEADER frame,而相应的 message body 则封装到 DATA frame 里面。

头部压缩(Header Compression)

  • http1.1的报文头部带有大量信息一般在几百字节,而且每次请求都要重复发送,每个请求中又存在大量重复信息。

  • http/2使用二进制帧来压缩需要传输的header大小,同时客户端与服务器各自缓存一份头部字段表,既避免了重复header的传输,又减小了需要传输的大小。尤其对于同一资源的轮询请求,头部的信令开销就是零字节,此时所有header字段都自动使用之前请求发送的值。

  • 如果header发生了变化,则只需将变化的部分加入到二进制帧中,改变的部分会加入到缓存的头部字段表中,头部字段在 http 2.0 的连接存续期内始终存在,由客户端和服务器共同更新。

  • http/2使用的是专门为头部压缩设计的HPACK②算法。

多路复用(Multiplexing)

  • 在上一节,我们提到HTTP/1.1引入的HOLB队头拥塞的问题,是由于HTTP请求与响应的串行关系。同时HTTP/1.1还限制了客户端到同一域名的连接数目,比如chrome限制是16个,这也会影响到页面的加载效率,这也是为何一些站点会有多个静态资源 CDN 域名的原因之一。

  • HTTP/2.0多路复用引入了Stream机制,在一个TCP连接中,可以承载任意数量的stream的复用,将HTTP请求与响应放入二进制帧中,通过stream来实现多流并行。下图显示的就是一个TCP连接:

image.png

  • 每个stream都有多个互不依赖的帧,而这些帧可以乱序发送和响应,还可以分优先级,而并行地在同一个 TCP 连接上双向交换消息,当到达终点时,再根据不同帧首部的stream id重新连接将不同的stream进行组装。

  • 客户端给每一个请求打上stream id发给服务器,服务器响应时返回这个id,客户端就知道这个响应属于那个请求,从而实现了无序异步的多路复用,大大提高了客户端的并发能力并加快了资源的加载。

  • 还记得我在上文提到的TCP慢启动问题,在HTTP/2.0中,因为stream都使用同一个TCP连接,可以让这个连接变得非常“热”,可以承载更大的数据流,让高带宽也能真正的服务于 HTTP 的性能提升!

  • multiplexing多路复用技术将多个请求和响应的串行传输改为了并行或乱序,通过streamId来互相区别。这彻底解决了HOLB队头拥塞问题,同时还允许给每个请求设置优先级服务端会先响应优先级高的请求。

image.png

请求优先级(Request Priorities)

再多说两句请求优先级。在进行二进制分帧之后,打乱了请求的发送顺序,这回影响到页面组装的时间,所以HTTP/2.0增加了这个特性,可以针对一些特殊内容,标识优先级。

  • 服务器可以根据流的优先级,控制资源分配(CPU、内存、带宽),而在响应数据准备好之后,优先将优先级高的请求响应发送给客户端。

  • 但这个顺序并不是绝对的,不同优先级的混合也是必须的。因为绝对地准守,可能又会引入队头阻塞的问题:高优先级的请求慢导致阻塞其他资源交付。

  • 优先级别可以在运行时动态改变,当用户滚动页面时,可以告诉浏览器哪个图像是最重要的,就会提高这个图像的请求优先级。

  • 建议的优先级设置:主要的html > CSS文件 > js文件 > 图片/视频等

服务端推送(Server Push)

image.png

这个机制其实很好理解,在HTTP/1.X都是客户端请求什么,服务器就发送什么,这样的请求响应模式。HTTP/2.0为了更加提高传输效率,服务端可以在无需客户端请求的情况下,将客户端需要的资源伴随index.html一股脑儿主动发送给客户端,省去了客户端重复请求的步骤!

  • 这种服务端的推送的触发,仍然是基于客户端的请求对象来确定的。

  • 当服务端需要主动推送某个资源时,便会发送一个Type:PUSH_PROMISE 的 Frame,里面带了 PUSH 需要新建的 Stream id, 告知客户端:接下来我要用这个 ID 向你发送资源。客户端解析 Frame 时,识别到PUSH_PROMISE 类型,便会做好接收准备。


最后讲两句,HTTP/2.0姗姗来迟,有几个原因,第一前端技术的发展延缓了对协议演进的需求,很多应用层的问题可以通过前端技术解决,其次,计算机性能的大幅提升也让服务器端的压力减少了许多,并且,网络传输技术的发展也在一定程度上改善了HTTP的传输效率,可以看到HTTP/2.0使用的帧,stream其实并不新鲜,可以看作是网络技术在应用层上的应用。


感谢阅读,如有不准确或错误请留言指正,我会及时修正

总结不易,请勿私自转载,否则别怪老大爷不客气

欢迎喜欢技术的小伙伴和我交流,微信1296386616

参考资料:

HTTP中的分块传输编码是怎么回事? 刘志军 foofish.net/http-transf…

HTTP/2简介 by Ilya Grigorik & Surma developers.google.com/web/fundame…

Learning HTTP/2.0 server push filter by Helospark github.com/helospark/l…