深入浅出http2

62 阅读9分钟

一、先来看看HTTP/1.1的优化:

  • 首先是增加了持久连接
  • 浏览器为每个域名最多维持6个TCP持久连接
  • 使用CDN的实现域名分片机制

可以通过图片来直观感受下:

Image1.png

在图中,引入了CDN,并同时为每个域名维护6个连接,这样就能大大减轻整个资源的下载时间。

二、再来看看HTTP/1.1存在的问题

HTTP/1.1对宽带的利用率并不理想,这时HTTP/1.1的一个核心问题。

带宽是指每秒最大能发送或者接受的字节数。

之所以说HTTP/1.1对带宽利用率不理想,主要有以下三个原因导致的。

1. 第一个原因:TCP慢启动

最初的TCP的实现方式是,在建立连接成功后就开始发送大尺寸的数据包,这时候假如网络出现问题了,那么很多这样的包就会积攒在路由器上,很容易导致网络中的路由器缓存空间耗尽,从而发生拥塞。

因此,现在的TCP协议规定了,新建立的连接不能够一开始就发送大尺寸的包,只能从一个小尺寸的包开始发送,在发送到数据被对方确认的过程中去计算对方的接收速度,从而逐步郑家每次发送的数据量。最后达到一个稳定值,进入高速传输阶段。

对于大文件大流量来说慢启动消耗的时间是微不足道的,但是对于网页来说,页面中一些常用的关键资源文件本来就不大,通常这些文件在TCP建立好就要发起请求的,但是这个过程是慢启动,所以耗费的时间要比正常直接获取的时间要多很多,这就会导致首次渲染的页面的时间变长了。

2. 第二原因,同时开启了多条TCP连接,那么这些连接会竞争固定的带宽。

当系统同时建立了多条TCP连接,当带宽充足时,每条连接的收发速度会慢慢向上增加;一万发现带宽不足时,TCP连接会动态减慢收发速度,这样就会出现一个问题:当有的TCP在下载的是关键的资源,比如CSS文件,JS文件等,而有的TCP下载的是图片和音频等大的普通资源文件。这时候多条TCP之间不能协商让哪些资源先下载,所以就有可能影响到关键资源的下载速度了.

3. 第三个原因,HTTP/1.1队头阻塞问题。

虽然HTTP/1.1使用持久连接时,可以公用一个TCP通道,但是在一个通道中同一时刻只能处理一个请求,在这一个包很大需要时间很长的情况下通道就会处于阻塞状态。

会导致阻塞请求的因素有很多,而且都是一些不确定性的因素,加入有的请求被阻塞了5秒,那么后续排队的请求都要延迟等待5秒,在等待的过程中,CPU,带宽都被白白浪费了。

三、基于上面的问题的解决方案。

分析主要问题:慢启动,TCP连接之间竞争宽带,HTTP/1.1的队头阻塞。 前面两个是由于TCP本身的机制导致的。我们暂时无法换掉TCP那我们就想办法规避TCP的慢启动和TCP连接之间的竞争。 后者是由于HTTP/1.1机制导致的,这个就由新的Http2解决。

基于这样的想法,所以HTTP/2的解决方案就是一个域名只是用一个TCP长连接来传输数据,这样整个页面资源的下载过程只需要一次慢启动,而只有一个长连接也就不用考虑连接竞争宽带问题了。 而队头阻塞,则采用新的二进制分帧层机制实现并行请求且互不影响来解决。

四、HTTP/2的多路复用

多路复用靠的是二进制数据帧和流的概念。

1. 流的概念(Stream)

stream,服务器与客户端在HTTP/2连接内用于交换帧数据的独立双向序列。逻辑上就是一次完整的请求-响应流程。特点如下:

  • 一个HTTP/2可以同时保持着多个打开的流
  • 流可被任一端关闭。
  • 流与流之间是并行,互相独立的存在。
2. 数据帧(Frame)

Image2.png

在HTTP/2新加入的二进制分帧层中,HTTP/2 会将所有传输的信息分割为更⼩的消息并封装在帧(frame)中,并对它们采⽤⼆进制格式的编码 ,其中 HTTP1.1的⾸部信息会被封装到 HEADER frame,⽽相应的 Request Body 则封装到 DATA frame ⾥⾯。如上图的HEADER frame和DATA frame分别对应http1.1的请求头和请求体。

3. 多路复用

这是概念上的多路复用

Image3.png

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

按照上面的说法,其实这才是实际上传输的情况

Image4.png

来个HTTP/2的请求和接受的流程:

  • 首先浏览器准备好了请求数据,包括请求行,请求头,如果是POST,那还有请求体。
  • 数据进入二进制分帧层后,会转化为一个个带有请求ID编号的帧,通过协议栈将这些帧发送给服务器。
  • 服务器接收到所有帧后,会把所有同一ID的帧合称为一条完整的请求信息。
  • 然后服务器处理请求,并将处理的响应行,响应头,响应体分别发送至二进制分帧层
  • 同样的,二进制分帧层会将其转化为一个个带有请求ID编号的帧,经过协议栈发送给浏览器。
  • 浏览器接收到响应帧之后,会根据 ID 编号将帧的数据提交给对应的请求
4. 为什么HTTP/1.1不支持多路复用?

简单的来看就是HTTP/2是基于二进制帧的协议,而HTTP/1.1是基于文本切割的协议。

例子:

对于HTTP/1.1来说浏览器要按顺序发送hello world,服务器才能明白这是hello world 对于HTTP/2来说,他会给hello world每个字母都标记上顺序,h对应1,e对应2,按这样来看,浏览器随便发送heworld llo,服务器也能识别出来。

五、HTTP/2的其他特性

首先我们知道了多路复用是基于二进制分帧层作为核心的。它能实现资源的并行传输。对于二进制分帧层,HTTP/2还附带了很多其他功能。

1.请求优先级

我们知道像HTML,js,css对于网页是很重要的,而首页的渲染对于用户体验也是相当重要。如果在发送请求时,重要的请求可能会晚于那些不怎么重要的请求,且服务器按照请求的顺序来回复数据,那么这个重要的数据就有可能推迟很久才能送达浏览器,这对于用户体验来说是非常不友好的。

为了解决这个问题,HTTP/2 提供了请求优先级(priority),可以在发送请求时,标上该请求的优先级,这样服务器接收到请求之后,会优先处理优先级高的请求。

2.服务器推送

除了设置优先级以外,HTTP/2还可以将数据提前推送到浏览器。想象这样一个场景,当用户请求一个 HTML 页面之后,服务器知道该 HTML 页面会引用几个重要的 JavaScript 文件和 CSS 文件,那么在接收到 HTML 请求之后,附带将要使用的 CSS 文件和 JavaScript 文件一并发送给浏览器,这样当浏览器解析完 HTML 文件之后,就能直接拿到需要的 CSS 文件和 JavaScript 文件,这对首次打开页面的速度起到了至关重要的作用。

3. 头部压缩

http1.x的头带有大量信息,而且每次都要重复发送。http/2使用encoder来减少需要传输的header大小,通讯双方各自缓存一份头部字段表,既避免了重复header的传输,又减小了需要传输的大小。

对于相同的数据,不再通过每次请求和响应发送,通信期间几乎不会改变通用键-值对(用户代理、可接受的媒体类型,等等)只需发送一次。事实上,如果请求中不包含首部(例如对同一资源的轮询请求),那么,首部开销就是零字节,此时所有首部都自动使用之前请求发送的首部。

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

压缩原理:

头部压缩需要在支持 HTTP/2 的浏览器和服务端之间:维护一份相同的静态字典(Static Table),包含常见的头部名称,以及特别常见的头部名称与值的组合;维护一份相同的动态字典(Dynamic Table),可以动态地添加内容;支持基于静态哈夫曼码表的哈夫曼编码(Huffman Coding);静态字典的作用有两个:1)对于完全匹配的头部键值对,例如 :method: GET,可以直接使用一个字符表示;2)对于头部名称可以匹配的键值对,例如 cookie: xxxxxxx,可以将名称使用一个字符表示。

六、已经不适用于HTTP/2的HTTP/1.1的HACK

那么回过头来再看看我们以前针对HTTP1.1的优化,我觉得很多其实都是应对HTTP1.1不足的HACK,HTTP/2中这些都已经不是问题了,所以HACK可以去掉了,比如下面这些。

  • 雪碧图
  • 分域名
  • 接口合并请求
  • 内联资源