HTTP/2

* 本页面的内容总结和归纳了HTTP/2相关的知识点。

因为最近工作中涉及项目的技术改造,新的服务中涉及HTTP/2相关的知识,所以今天就把这两天了解和学习到的HTTP/2相关的内容做一下整理和总结,产出这篇文档。当然,学习HTTP/2之前,肯定是要先对HTTP有所了解和认识的,到时候会在 HTTP/HTTPS 这篇文章中详细总结。

关于HTTP/2的所有内容,是可以有一本136页的《HTTP/2基础教程》来讲解的,所以,这里只会把普通使用过程中需要了解的核心知识点进行总结,全面的学习,大家还是可以通过学习这本书来进行。

这篇总结将从HTTP/2的背景、特点、协议分析和各方支持几个方面进行总结。另外在最后,我还把我网站从HTTP/1.1升级至HTTP/2的实践过程,总结出来分享给大家,方便理论联系实践。现在,我们就开始吧!

HTTP/2
HTTP/2
一、HTTP/2出现的背景:为什么要引入?

要回答这个问题,我假设的前提是你已经对HTTP(或HTTPS)有所了解了。

HTTP/2 是 HTTP 协议自 1999 年 HTTP 1.1 发布后的首个更新,主要基于 SPDY 协议。由互联网工程任务组(IETF)的 Hypertext Transfer Protocol Bis(httpbis)工作小组进行开发。该组织于2014年12月将HTTP/2标准提议递交至IESG进行讨论,于2015年2月17日被批准。HTTP/2标准于2015年5月以RFC 7540正式发表。

HTTP/2主要是为了解决现HTTP 1.1性能不好的问题才出现的。从上面的发展时间线上可以看出来,Google为了提高HTTP性能,做出了SPDY(相比HTTP,加载时间减少64%),它就是HTTP/2的前身,后来也发展成为HTTP/2的标准。

所以,HTTP 2.0 的出现,在与 HTTP/1.1 完全语义兼容的基础上,进一步减少了网络延迟,大幅度的提升了 web 性能。

二、HTTP/2的秘诀:做了什么来提升速度?

HTTP/2通过以下方法减少延迟(latency)以提升页面的加载速度,即吞吐量(throughput):

  • HTTP Header的压缩,采用的是哈夫曼编码(HPack算法)
  • HTTP/2的一个非常重要的特性:Server Push
  • 二进制分帧
  • 修复在HTTP 1.x的队头阻塞问题
  • 在单个TCP连接里多路复用 (Multiplexing)请求

看了这些措施,是不是有很多专业名词不是很了解,那么我们一一来学习下:

用哈夫曼编码压缩的HTTP Header

HTTP/2对消息头采用HPACK(专为http/2头部设计的压缩格式,详细可参考:https://tools.ietf.org/html/rfc7541)进行压缩传输,能够节省消息头占用的网络的流量。而HTTP/1.x每次请求,都会携带大量冗余头信息(描述这次通信的的资源、浏览器属性、cookie等),浪费了很多带宽资源。

下面这张图直观的描述了HTTP/2中头部压缩的原理:

HTTP/2头部压缩原理
HTTP/2头部压缩原理

简单说,HTTP头压缩需要在HTTP/2 Client和服务端之间:

  • 维护一份相同的静态表(Static Table),包含常见的头部名称,以及特别常见的头部名称与值的组合;
  • 维护一份相同的动态表(Dynamic Table),可以动态地添加内容;
  • 基于静态哈夫曼码表的哈夫曼编码(Huffman Coding)

比如上面的:method:GET的编码索引是2,这些固定的key:value在静态表中定义,总共有61个,详细可以点开这里查看:https://tools.ietf.org/html/rfc7541#appendix-A

这样,就可以通过这些手段极大地提升Header头信息的压缩效果,原来需要N个字符表示的,现在只需一个字符即可。

另外,动态表可以来跟踪和存储之前发送的键-值对,对于相同的数据,不再通过每次请求和响应发送,见下图:

HTTP/2头部压缩原理:差异数据
HTTP/2头部压缩原理:差异数据

在图中的两次请求, 请求一发送了所有的头部字段,第二个请求则只需要发送差异数据,这样可以减少冗余数据,降低开销。

Server Push

这是HTTP/2很重要的一个新特性。是一种在客户端请求之前发送数据的机制。即服务端可以在发送页面HTML时主动推送其它资源(遵守同源策略),而不用等到浏览器解析到相应位置,发起请求再响应。例如服务端可以主动把JS和CSS文件推送给客户端,而不需要客户端解析HTML时再发送这些请求。当然,浏览器也会根据自己的缓存策略选择是否接收推送来的资源。

下图描述了Server Push的流程机制:

Server Push示意图
Server Push示意图
二进制分帧

不改动 HTTP/1.x 的语义、方法、状态码、URI 以及首部字段等的情况下, HTTP/2 是如何做到“突破 HTTP1.1 的性能限制,改进传输性能,实现低延迟和高吞吐量”的 ?

关键之一就是在应用层(HTTP/2)传输层(TCP or UDP)之间增加了一个二进制分帧层

  • 帧:HTTP/2 数据通信的最小单位消息:指 HTTP/2 中逻辑上的 HTTP 消息。例如请求和响应等,消息由一个或多个帧组成
  • 流:存在于连接中的一个虚拟通道。流可以承载双向消息,每个流都有一个唯一的整数ID

HTTP/2 采用二进制格式传输数据,而非 HTTP 1.x 的文本格式,二进制协议解析起来更高效。HTTP/2 中,同域名下所有通信都在单个连接上完成,该连接可以承载任意数量的双向数据流。且这些流中的帧可以乱序发送,最后再根据帧首部的流标识重新组装起来。这样单个连接,减少了服务端的链接压力,内存占用更少,连接吞吐量更大;而TCP连接少了,使网络拥塞状况得以改善,整体上,就使性能和速度提升起来了!神奇吧~

在二进制分帧层里面,HTTP/2会将所有传输的信息分割为更小的信息和帧。首部信息会封装到HEADER frame,而相应的Request Body则封装到DATA frame里面。见下图:

二进制分帧示意图
二进制分帧示意图
多路复用 (Multiplexing)

多路复用允许同时通过单一的 HTTP/2 连接发起多重的请求-响应消息。

在 HTTP/1.1 协议中"浏览器客户端在同一时间,针对同一域名下的请求有一定数量限制,超过限制数目的请求会被阻塞"。不同的浏览器对这个数量的支持还有所不同,比如在HTTP/1.1下,Chrome4+ 是6,IE 8+是6,Safari是4。所以,我们可以看到我们有一个好的前端速度提升方案就是把静态资源“多域名CDN化”,这样就可以变相的绕过了这个数量限制。但网络请求次数其实并没有减少,只是“并行”了而已!

但HTTP/2的多路复用技术就改变了这个情况,如下图所示:

HTTP/2多路复用和HTTP/1.1 Baseline对比
HTTP/2多路复用和HTTP/1.1 Baseline对比

所以HTTP/2的多路复用技术较少了TCP的连接次数,极大地提升了数据交换的效率。同时,也消除了因多个 TCP 连接而带来的内存消耗

三、HTTP/2都被哪些浏览器和网络服务支持了?

基本上大部分浏览器在2015年底都支持HTTP/2了,包括Chrome、Opera、Firefox、IE 11、Safari,Edge。在Chrome上,可以下载插件HTTP Indicator,判断访问的网站是否支持HTTP/2。也可以打开Chrome的开发者工具,打开Network tab,可以看到Protocol为h2的就是HTTP/2请求。如果Initiator为push的,说明开启了Server Push模式。

更多服务端软件等的最新最全的支持列表可以点开这个链接查看:https://github.com/httpwg/http2-spec/wiki/Implementations

四、实践:如何让我自己的网站也支持HTTP/2?

那么理论就总结到这里,改到动手实战的时候了,既然HTTP/2能够带来这么大的性能提升,那么如何能让我自己的网站 凯冰科技 也开启HTTP/2呢?说干就干!

首先,看看我网站服务器的现状:

  • 主站HTTPS已开启,这个是必须的,HTTP/2只能在HTTPS下开启
  • nginx version: nginx/1.14.0(nginx的最低版本是1.10.0)
  • Server version: Apache/2.4.18 (Unix)
  • OpenSSL 1.0.1e-fips 11 Feb 2013(OpenSSL的最低版本是1.0.2)

显然,服务器的OpenSSL版本不符合要求,所以首先需要升级OpenSSL!如果你也是这个情况,可以按照下面的步骤升级你的OpenSSL版本:

wget https://www.openssl.org/source/openssl-1.0.2k.tar.gz
tar -zvxf openssl-1.0.2k.tar.gz
cd openssl-1.0.2k
./config
make
make test
make install

升级之后,切记一定要重新编译安装你的nginx,否则OpenSSL的版本不会更新的。

./configure --prefix=/usr/local/nginx --user=www --group=www --with-http_stub_status_module --with-http_v2_module --with-http_ssl_module --with-ipv6 --with-http_gzip_static_module --with-http_realip_module --with-http_flv_module --with-ld-opt='-ljemalloc' --with-openssl=/soft/openssl-1.0.2k
make && make install

nginx -V
nginx version: nginx/1.18.0
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-4) (GCC) 
built with OpenSSL 1.0.2k  26 Jan 2017
TLS SNI support enabled
configure arguments: --prefix=/usr/local/nginx --user=www --group=www --with-http_stub_status_module --with-http_v2_module --with-http_ssl_module --with-ipv6 --with-http_gzip_static_module --with-http_realip_module --with-http_flv_module --with-ld-opt=-ljemalloc --with-openssl=/soft/openssl-1.0.2k

好了,终于把OpenSSL版本升级到了可以支持的版本:OpenSSL 1.0.2k 26 Jan 2017。

接下来,在你的nginx项目配置文件中加入下面的配置信息:

listen 443 ssl http2;

修改完成之后,重新启动nginx,刷新页面,你会发现已经成功通过HTTP/2来传输数据了:

凯冰科技全面启用HTTP/2
凯冰科技全面启用HTTP/2

同时,由于凯冰科技旗下所有网站的静态资源中心建立在七牛云存储服务上,所以七牛云上可以通过下面的方式开启HTTP/2:

七牛云存储启用HTTP/2
七牛云存储启用HTTP/2

至此,我的网站凯冰科技以及其下面的各个开启了HTTPS的子站点全部都从HTTP/1.1切换到了HTTP/2。理论转实践成功!开心~


* 本页内容参考以下数据源:

  • https://zhuanlan.zhihu.com/p/26559480
  • https://http2.github.io/
  • https://www.cnblogs.com/confach/p/10108437.html
  • https://www.zhihu.com/question/34074946
  • http://velocityconf.com/devops-web-performance-2015/public/schedule/detail/42385
  • OpenSSL升级https://jszbug.com/6944

凯冰科技 · 代码改变世界,技术改变生活
Next Page→