为什么要升级到http/2呢?我们用Chrome的控制台进行网站评测的时候,非http/2的网站会给一个建议:
意思是说要使用http/2,我之前只是听过http/2的一些介绍,其实http/2已经来了。http/2最大的特点是使用多路复用,对同一个域的服务器只建立一次TCP连接,加载多个资源,使用二进制帧传输,同时会对http头部进行压缩。使用http2会比http/1.1更加地高效,因此笔者对博客网站尝试进行了升级。
1. 升级到HTTP/2
要求nginx的最低版本是1.10.0,openssl的最低版本是1.0.2,http/2在实现上基本上只支持https。笔者的系统是centos 7,用yum安装的nginx是1.10.2,这个版本是可以的,但是系统的openssl是1.0.1,把系统的openssl update一下,变成1.0.2,但还是不可以。发现是因为nginx在编译的时候指定的openssl是1.0.1的,所以即使升级了系统的openssl也是没有用的,如下图所示:
那怎么升级呢?nginx官方提供了两种方法,第一种是升级操作系统,第二种是从源码编译新版本的nginx,我们用第二种方法。当前nginx最新的稳定版本是1.12.1,在服务器上执行以下命令:
# 下载
tar -zxvf nginx-1.12.1.tar.gz
configure的时候后面可以带参数,参数可以用原先老版本nginx的参数,包括安装路径之类的,这个可以通过执行nginx -V得到,使得新nginx的配置和老nginx一样。如果configure提示缺一些库的话就相应地做些安装,基本上就是它提示的库后面带上devel,如以下提示:
./configure: error: the Google perftools module requires the Google perftools
library. You can either do not enable the module or install the library.
可安装下面这个库解决:
sudo yum install gperftools-devel
新安装后的nginx的openssl版本就对了:
然后添加nginx配置,原本https的listen为:
listen
现在在后面加上http2:
listen
然后把nginx关了再开一下(因为新安装了一个nginx,要先关一下再开),这个时候再用浏览器访问,原本的http1.1:
就会变成http2:
有个细节是HTTP/2不叫2.0,这是故意的,因为1.x容易混淆,所以2的时候就不带小版本号了,所以上面firefox的显示其实是不对的。
整个传输模型如下图所示(图片来自nginx):
nginx和客户端是HTTP/2,而nginx和业务服务还是HTTP/1.1,因为nginx的服务和业务服务通常是处于同一个内网,速度一般会很快,而nginx和客户端的连接就不太可控了,如果业务服务本身支持HTTP/2,会更好。
然后我们先看一下传输的流量节省了多少,如下图所示,加载一个页面:
可以看到,HTTP 2在流量方面并没有太大的改进,只减少了4kB(1%)的流量,下面我们再分析原因。HTTP 2的优势还在于它的多路复用等方面。
2. HTTP/2的优势
(1)多路复用
多路复用的意义在于可以用同一个连接传输多个资源,进而使得以前在http 1.1所做一些优化就没有必要了,如:
a)使用雪碧图技术,把多张小图合成一张大图,减少请求数;
b)合并JS和CSS,减少请求数。
因为在http 1.1时代,由于需要建立多个TCP连接,服务器需要更多的线程来处理请求,同样地,浏览器也需要,所以浏览器会限制同一个域的同时请求数,Chrome是限制6个,总连接数是17个,其它浏览器的个数有所浮动,但差不多,读者可以这个网站测试自己所用的浏览器的限制情况。可以实际对比一下,http 1.1会让资源排队加载,如下图所示:
但当我们开启了http/2之后,个数几乎没有限制了,如下图所示:
你会发现这些资源都是同时加载的,后面加载的资源不需要进行排队。也就是说理论上带宽有多大,就能传多快。实际的效果在笔者的博客网站上,用了一个页面重复了5次,比较平均值,load时间只快了4%,在我这个例子并不是很明显,但并不能代表HTTP 2没什么用,如果nginx和业务服务的连接也是http/2应该会更好。
这个传输模型是这样的,如下图所示(图片来自谷歌开发者文档):
数据以帧(frame)的格式在同一个连接传输,并且服务还可以提前Push一些资源给客户端。
(2)Server Push
在上面两张加载图可以看到第一个资源html没加载之前,其它资源是不会开始加载的,因为它们是html触发加载,如img标签就会加载一张图片,所以要等html下载和解析了才能加载其它资源,而http2可以让服务先把其它很可能客户端会请求的资源先push发给你,不用等到请求的时候再发送,这样可以提高页面整体的加载速度。nginx官网说目前暂不支持Server Push,但可以下载一个http2的Node.js包写个demo感受一下,如下代码:
response.push('/img/banner.png');
(3)报文头压缩和二进制编码
由于https传输的数据是加密的,无法用抓包工具观察到这个过程,我们可以做些理论分析。
使用二进制传输的好处在于它更接近于计算机存储特点,传统的文本流传输形式存在一些解析上的复杂性,如对消息长度(Message Length)的解析要分为5种情况,详见w3c,而使用二进制方式则不存在这个问题。需要注意的是http/2的二进制形式并没有改变http/1.1的体系,只是把传输报文格式改了,如下图所示:
上图展示了两个帧,一个头部帧,还有一个数据帧。
而报文头压缩是这样的,如下图所示:
客户端发了两次请求,第一次请求有完整的http报文头部,第二次请求的时候只有一个path的字段不一样,但是这次报文头它只需要发送一个path的字段就好了,这样就大大减少了发送的量。这个的实现要求客户端和服务同时维护一个报文头表。上面提到的少了4kb的流量很可能是这个节省下来的。这个的意义还是很大的,因为动态请求有时候可能只需要发送几个字节的数据,但却需要发送一个几百个字节的报文头(500 ~ 800)。
最后一个问题,HTTP/2的兼容性如何?如caniuse所示:
IE11只在windows10支持,Chrome/Safari/Firefox/Opera等浏览器只支持https的http/2。
如果浏览器不支持http/2会怎么样呢?也是能够正常打开的,为什么呢?因为建立https连接的时候需要先握手,浏览器或者客户端会发送一个Client Hello的包,这个包里面会说明它是否支持http2,如下图所示:
nginx就能够根据握手信息决定是否使用http/2,如果客户端不支持就使用http/1.1。这里从一个侧面知道为什么浏览器需要使用https和服务支持ALPN了。
参考:
- Introduction to HTTP/2 - 谷歌开发者文档
- Why Everyone Should Be Moving To HTTP/2
- HTTP/2 Frequently Asked Questions
扩展:
- 为什么要把网站升级到HTTPS
- https连接的前几毫秒发生了什么
- WebSocket与TCP/IP