You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

135 lines
13 KiB
Markdown

2 years ago
# 30HTTP/2如何提升网络速度
[上一篇文章](https://time.geekbang.org/column/article/147501)我们聊了HTTP/1.1的发展史虽然HTTP/1.1已经做了大量的优化但是依然存在很多性能瓶颈依然不能满足我们日益变化的新需求所以就有了我们今天要聊的HTTP/2。
本文我们依然从需求的层面来谈先分析HTTP/1.1存在哪些问题然后再来分析HTTP/2是如何解决这些问题的。
我们知道HTTP/1.1为网络效率做了大量的优化,最核心的有如下三种方式:
1. 增加了持久连接;
2. 浏览器为每个域名最多同时维护6个TCP持久连接
3. 使用CDN的实现域名分片机制。
通过这些方式就大大提高了页面的下载速度,你可以通过下图来直观感受下:
![](https://static001.geekbang.org/resource/image/91/c5/91c3e0a8f13ebc4d81f08d8604f770c5.png)
HTTP/1.1的资源下载方式
在该图中引入了CDN并同时为每个域名维护6个连接这样就大大减轻了整个资源的下载时间。这里我们可以简单计算下如果使用单个TCP的持久连接下载100个资源所花费的时间为100 \* n \* RTT若通过上面的技术就可以把整个时间缩短为100 \* n \* RTT/(6 \* CDN个数)。从这个计算结果来看,我们的页面加载速度变快了不少。
## HTTP/1.1的主要问题
虽然HTTP/1.1采取了很多优化资源加载速度的策略也取得了一定的效果但是HTTP/1.1**对带宽的利用率却并不理想**这也是HTTP/1.1的一个核心问题。
**带宽是指每秒最大能发送或者接收的字节数**。我们把每秒能发送的最大字节数称为**上行带宽**,每秒能够接收的最大字节数称为**下行带宽**。
之所以说HTTP/1.1对带宽的利用率不理想是因为HTTP/1.1很难将带宽用满。比如我们常说的100M带宽实际的下载速度能达到12.5M/S而采用HTTP/1.1时也许在加载页面资源时最大只能使用到2.5M/S很难将12.5M全部用满。
之所以会出现这个问题,主要是由以下三个原因导致的。
**第一个原因TCP的慢启动。**
一旦一个TCP连接建立之后就进入了发送数据状态刚开始TCP协议会采用一个非常慢的速度去发送数据然后慢慢加快发送数据的速度直到发送数据的速度达到一个理想状态我们把这个过程称为慢启动。
你可以把每个TCP发送数据的过程看成是一辆车的启动过程当刚进入公路时会有从0到一个稳定速度的提速过程TCP的慢启动就类似于该过程。
慢启动是TCP为了减少网络拥塞的一种策略我们是没有办法改变的。
而之所以说慢启动会带来性能问题是因为页面中常用的一些关键资源文件本来就不大如HTML文件、CSS文件和JavaScript文件通常这些文件在TCP连接建立好之后就要发起请求的但这个过程是慢启动所以耗费的时间比正常的时间要多很多这样就推迟了宝贵的首次渲染页面的时长了。
**第二个原因同时开启了多条TCP连接那么这些连接会竞争固定的带宽。**
你可以想象一下系统同时建立了多条TCP连接当带宽充足时每条连接发送或者接收速度会慢慢向上增加而一旦带宽不足时这些TCP连接又会减慢发送或者接收的速度。比如一个页面有200个文件使用了3个CDN那么加载该网页的时候就需要建立6 \* 3也就是18个TCP连接来下载资源在下载过程中当发现带宽不足的时候各个TCP连接就需要动态减慢接收数据的速度。
这样就会出现一个问题因为有的TCP连接下载的是一些关键资源如CSS文件、JavaScript文件等而有的TCP连接下载的是图片、视频等普通的资源文件但是多条TCP连接之间又不能协商让哪些关键资源优先下载这样就有可能影响那些关键资源的下载速度了。
**第三个原因HTTP/1.1队头阻塞的问题。**
通过[上一篇文章](https://time.geekbang.org/column/article/147501)我们知道在HTTP/1.1中使用持久连接时虽然能公用一个TCP管道但是在一个管道中同一时刻只能处理一个请求在当前的请求没有结束之前其他的请求只能处于阻塞状态。这意味着我们不能随意在一个管道中发送请求和接收内容。
这是一个很严重的问题因为阻塞请求的因素有很多并且都是一些不确定性的因素假如有的请求被阻塞了5秒那么后续排队的请求都要延迟等待5秒在这个等待的过程中带宽、CPU都被白白浪费了。
在浏览器处理生成页面的过程中,是非常希望能提前接收到数据的,这样就可以对这些数据做预处理操作,比如提前接收到了图片,那么就可以提前进行编解码操作,等到需要使用该图片的时候,就可以直接给出处理后的数据了,这样能让用户感受到整体速度的提升。
但队头阻塞使得这些数据不能并行请求,所以队头阻塞是很不利于浏览器优化的。
## HTTP/2的多路复用
前面我们分析了HTTP/1.1所存在的一些主要问题慢启动和TCP连接之间相互竞争带宽是由于TCP本身的机制导致的而队头阻塞是由于HTTP/1.1的机制导致的。
那么该如何去解决这些问题呢?
虽然TCP有问题但是我们依然没有换掉TCP的能力所以我们就要想办法去规避TCP的慢启动和TCP连接之间的竞争问题。
基于此HTTP/2的思路就是一个域名只使用一个TCP长连接来传输数据这样整个页面资源的下载过程只需要一次慢启动同时也避免了多个TCP连接竞争带宽所带来的问题。
另外就是队头阻塞的问题等待请求完成后才能去请求下一个资源这种方式无疑是最慢的所以HTTP/2需要实现资源的并行请求也就是任何时候都可以将请求发送给服务器而并不需要等待其他请求的完成然后服务器也可以随时返回处理好的请求资源给浏览器。
所以HTTP/2的解决方案可以总结为**一个域名只使用一个TCP长连接和消除队头阻塞问题**。可以参考下图:
![](https://static001.geekbang.org/resource/image/0a/00/0a990f86ad9c19fd7d7620b2ef7ee900.jpg)
HTTP/2的多路复用
该图就是HTTP/2最核心、最重要且最具颠覆性的**多路复用机制**。从图中你会发现每个请求都有一个对应的ID如stream1表示index.html的请求stream2表示foo.css的请求。这样在浏览器端就可以随时将请求发送给服务器了。
服务器端接收到这些请求后会根据自己的喜好来决定优先返回哪些内容比如服务器可能早就缓存好了index.html和bar.js的响应头信息那么当接收到请求的时候就可以立即把index.html和bar.js的响应头信息返回给浏览器然后再将index.html和bar.js的响应体数据返回给浏览器。之所以可以随意发送是因为每份数据都有对应的ID浏览器接收到之后会筛选出相同ID的内容将其拼接为完整的HTTP响应数据。
HTTP/2使用了多路复用技术可以将请求分成一帧一帧的数据去传输这样带来了一个额外的好处就是当收到一个优先级高的请求时比如接收到JavaScript或者CSS关键资源的请求服务器可以暂停之前的请求来优先处理关键资源的请求。
## 多路复用的实现
现在我们知道为了解决HTTP/1.1存在的问题HTTP/2采用了多路复用机制那HTTP/2是怎么实现多路复用的呢你可以先看下面这张图
![](https://static001.geekbang.org/resource/image/86/6a/86cdf01a3af7f4f755d28917e58aae6a.png)
HTTP/2协议栈
从图中可以看出HTTP/2添加了一个**二进制分帧层**那我们就结合图来分析下HTTP/2的请求和接收过程。
* 首先浏览器准备好请求数据包括了请求行、请求头等信息如果是POST方法那么还要有请求体。
* 这些数据经过二进制分帧层处理之后会被转换为一个个带有请求ID编号的帧通过协议栈将这些帧发送给服务器。
* 服务器接收到所有帧之后会将所有相同ID的帧合并为一条完整的请求信息。
* 然后服务器处理该条请求,并将处理的响应行、响应头和响应体分别发送至二进制分帧层。
* 同样二进制分帧层会将这些响应数据转换为一个个带有请求ID编号的帧经过协议栈发送给浏览器。
* 浏览器接收到响应帧之后会根据ID编号将帧的数据提交给对应的请求。
从上面的流程可以看出,**通过引入二进制分帧层就实现了HTTP的多路复用技术**。
[上一篇文章](https://time.geekbang.org/column/article/147501)我们介绍过HTTP是浏览器和服务器通信的语言在这里虽然HTTP/2引入了二进制分帧层不过HTTP/2的语义和HTTP/1.1依然是一样的也就是说它们通信的语言并没有改变比如开发者依然可以通过Accept请求头告诉服务器希望接收到什么类型的文件依然可以使用Cookie来保持登录状态依然可以使用Cache来缓存本地文件这些都没有变发生改变的只是传输方式。这一点对开发者来说尤为重要这意味着我们不需要为HTTP/2去重建生态并且HTTP/2推广起来会也相对更轻松了。
## HTTP/2其他特性
通过上面的分析我们知道了多路复用是HTTP/2的最核心功能它能实现资源的并行传输。多路复用技术是建立在二进制分帧层的基础之上。其实基于二进制分帧层HTTP/2还附带实现了很多其他功能下面我们就来简要了解下。
### 1\. 可以设置请求的优先级
我们知道浏览器中有些数据是非常重要的,但是在发送请求时,重要的请求可能会晚于那些不怎么重要的请求,如果服务器按照请求的顺序来回复数据,那么这个重要的数据就有可能推迟很久才能送达浏览器,这对于用户体验来说是非常不友好的。
为了解决这个问题HTTP/2提供了请求优先级可以在发送请求时标上该请求的优先级这样服务器接收到请求之后会优先处理优先级高的请求。
### 2\. 服务器推送
除了设置请求的优先级外HTTP/2还可以直接将数据提前推送到浏览器。你可以想象这样一个场景当用户请求一个HTML页面之后服务器知道该HTML页面会引用几个重要的JavaScript文件和CSS文件那么在接收到HTML请求之后附带将要使用的CSS文件和JavaScript文件一并发送给浏览器这样当浏览器解析完HTML文件之后就能直接拿到需要的CSS文件和JavaScript文件这对首次打开页面的速度起到了至关重要的作用。
### 3\. 头部压缩
无论是HTTP/1.1还是HTTP/2它们都有请求头和响应头这是浏览器和服务器的通信语言。HTTP/2对请求头和响应头进行了压缩你可能觉得一个HTTP的头文件没有多大压不压缩可能关系不大但你这样想一下在浏览器发送请求的时候基本上都是发送HTTP请求头很少有请求体的发送通常情况下页面也有100个左右的资源如果将这100个请求头的数据压缩为原来的20%,那么传输效率肯定能得到大幅提升。
## 总结
好了,今天就介绍这里,下面我来总结下本文的主要内容。
我们首先分析了影响HTTP/1.1效率的三个主要因素TCP的慢启动、多条TCP连接竞争带宽和队头阻塞。
接下来我们分析了HTTP/2是如何采用多路复用机制来解决这些问题的。多路复用是通过在协议栈中添加二进制分帧层来实现的有了二进制分帧层还能够实现请求的优先级、服务器推送、头部压缩等特性从而大大提升了文件传输效率。
HTTP/2协议规范于2015年5月正式发布在那之后该协议已在互联网和万维网上得到了广泛的实现和部署。从目前的情况来看国内外一些排名靠前的站点基本都实现了HTTP/2的部署。使用HTTP/2能带来20%60%的效率提升至于20%还是60%要看优化的程度。总之我们也应该与时俱进放弃HTTP/1.1和其性能优化方法去“拥抱”HTTP/2。
## 思考时间
虽然HTTP/2解决了HTTP/1.1中的队头阻塞问题但是HTTP/2依然是基于TCP协议的而TCP协议依然存在数据包级别的队头阻塞问题那么你觉得TCP的队头阻塞是如何影响到HTTP/2性能的呢
欢迎在留言区与我分享你的想法,也欢迎你在留言区记录你的思考过程。感谢阅读,如果你觉得这篇文章对你有帮助的话,也欢迎把它分享给更多的朋友。