HTTP/2.0 原理!与 1.x 相比,到底优化了什么?

大家好,这里是承香墨影!

HTTP 2.0 是一种安全、高效的 HTTP 传输协议。安全是因为 HTTP 2.0 建立在 HTTPS 协议的基础上,高效是因为它是通过二进制分帧来进行数据传输。

正因为这些特性,HTTP 2.0 协议也在被越来越多的网站支持。据统计,截止至 2020 年 5 月,已经有 88% 以上的网站支持 HTTP 2.0。

本文将从概述、原理、实战及检测等方面来详细介绍 HTTP 2.0,希望能够加深你的理解。

什么是 HTTP 2.0 协议

在 HTTP 2.0 官网的描述是:

HTTP/2 is a replacement for how HTTP is expressed “on the wire.” It is not a ground-up rewrite of the protocol; HTTP methods, status codes and semantics are the same, and it should be possible to use the same APIs as HTTP/1.x (possibly with some small additions) to represent the protocol.

The focus of the protocol is on performance; specifically, end-user perceived latency, network and server resource usage. One major goal is to allow the use of a single connection from browsers to a Web site.

The basis of the work was SPDY, but HTTP/2 has evolved to take the community’s input into account, incorporating several improvements in the process.

中文总结一下就是:

1、对 1.x 协议语意的完全兼容

2.0 协议是在 1.x 基础上的升级而不是重写,1.x 协议的方法,状态及 api 在 2.0 协议里是一样的。

2、性能的大幅提升

2.0 协议重点是对终端用户的感知延迟、网络及服务器资源的使用等性能的优化。

HTTP 2.0 优化内容

1、二进制分帧(Binary Format)- 2.0 的基石

HTTP 2.0 之所以能够突破 HTTP1.X 标准的性能限制,改进传输性能,实现低延迟和高吞吐量,就是因为其新增了二进制分帧层。

首先先介绍几个关键的概念:

  • 帧 (frame) 包含部分:类型 Type, 长度 Length, 标记 Flags, 流标识 Stream 和 frame payload 有效载荷。

  • 消息 (message):一个完整的请求或者响应,比如请求、响应等,由一个或多个 Frame 组成。

  • 流:连接中的一个虚拟信道,可以承载双向消息传输。每个流有唯一整数标识符。为了防止两端流 ID 冲突,客户端发起的流具有奇数 ID,服务器端发起的流具有偶数 ID。

  • 流标识:描述二进制 frame 的格式,使得每个 frame 能够基于 HTTP/2.0 发送,与流标识联系的是一个流,每个流是一个逻辑联系,一个独立的双向的 frame 存在于客户端和服务器端之间的 HTTP/2.0 连接中。一个 HTTP/2.0 连接上可包含多个并发打开的流,这个并发流的数量能够由客户端设置。

在二进制分帧层上,HTTP 2.0 会将所有传输信息,分割为更小的消息和帧,并对它们,采用二进制格式的编码将其封装,新增的二进制分帧层,同时也能够保证 HTTP 的各种协议支持的方法,首部都不受影响,兼容上一代 HTTP 标准。

其中,HTTP1.X 中的首部信息 header 封装到 Headers 帧中,而 request body 将被封装到 Data 帧中。

2、多路复用 (Multiplexing) /连接共享

在 HTTP/1.1 中,浏览器客户端在同一时间,针对同一域名下的请求有一定数量的限制,超过限制数目的请求会被阻塞。这也是为何一些站点会有多个静态资源 CDN 域名的原因之一。

而 HTTP/2.0 中的多路复用优化了这一性能。多路复用允许同时通过单一的 HTTP/2 连接发起多重的请求 - 响应消息。

有了新的分帧机制后,HTTP/2.0 不再依赖多个 TCP 连接去实现多流并行了。每个数据流都拆分成很多互不依赖的帧,而这些帧可以交错(乱序发送),还可以分优先级,最后再在另一端把它们重新组合起来。

HTTP/2.0 连接都是持久化的,而且客户端与服务器之间,也只需要一个连接(每个域名一个连接)即可。HTTP2 连接可以承载数十或数百个流的复用,多路复用意味着,来自很多流的数据包,能够混合在一起通过同样连接传输。当到达终点时,再根据不同帧首部的流标识符,重新连接将不同的数据流进行组装。

上图展示了一个连接上的,多个传输数据流:客户端向服务端传输数据帧 stream5,同时服务端向客户端乱序发送 stream1 和 stream3。这次连接上有三个响应请求乱序并行交换。

上图就是 HTTP/1.X 和 HTTP/2.0 在传输数据时的区别。以货物运输为例再现 HTTP/1.1 与 HTTP/2.0 的场景:

HTTP/1.1 过程:货轮 1 从 A 地到 B 地去取货物,取到货物后,从 B 地返回,然后货轮 2 在 A 返回并卸下货物后才开始再从 A 地出发取货返回,如此有序往返。

HTTP/2.0 过程:货轮 1、2、3、4、5 从 A 地无序全部出发,取货后返回,然后根据货轮号牌卸载对应货物。

显然,第二种方式运输货物多,河道的利用率高。

3、头部压缩(Header Compression)

HTTP/1.x 的头带有大量信息,而且每次都要重复发送。

HTTP/2.0 使用 encoder 来减少需要传输的 header 大小,通讯双方各自缓存一份头部字段表,既避免了重复 header 的传输,又减小了需要传输的大小。

对于相同的数据,不再通过每次请求和响应发送,通信期间几乎不会改变通用键值对 (用户代理、可接受的媒体类型等等) 只需发送一次。

事实上, 如果请求中不包含首部 (例如对同一资源的轮询请求),那么,首部开销就是零字节,此时所有首部都自动使用之前请求发送的首部。

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

需要注意的是,HTTP/2.0 关注的是首部压缩,而我们常用的 gzip 等是报文内容(body)的压缩,二者不仅不冲突,且能够一起达到更好的压缩效果。

HTTP/2.0 使用的是专门为首部压缩而设计的 HPACK 算法。

从上图可以看到 HTTP/1.X 不支持首部压缩,而 HTTP/2.0 的压缩算法效果最好,发送和接受的数据量都是最少的。

4、压缩原理

用 header 字段表里的索引,代替实际的 header。

HTTP/2.0 的 HPACK 算法使用一份索引表,来定义常用的 HTTP Header,把常用的 HTTP Header 存放在表里,请求的时候便只需要发送在表里的索引位置即可。

例如 :method=GET 使用索引值 2 表示,:path=/index.html 使用索引值 5 表示,如下图:

完整的列表参考:HPACK Static Table。

只要给服务端发送一个 Frame,该 Frame 的 Payload 部分存储 0x8285,Frame 的 Type 设置为 Header 类型,便可表示这个 Frame 属于 HTTP Header,请求的内容是:

GET /index.html

为什么是 0x8285,而不是 0x0205?这是因为高位设置为 1 表示这个字节是一个完全索引值(key 和 value 都在索引中)。

类似的,通过高位的标志位可以区分出这个字节是属于一个完全索引值,还是仅索引了 key,还是 key 和 value 都没有索引 (参见:HTTP/2.0 首部压缩的 OkHttp3 实现)。

因为索引表的大小的是有限的,它仅保存了一些常用的 HTTP Header,同时每次请求还可以在表的末尾动态追加新的 HTTP Header 缓存,动态部分称之为 Dynamic Table。Static Table 和 Dynamic Table 在一起组合成了索引表:

HPACK 不仅仅通过索引键值对来降低数据量,同时还会将字符串进行霍夫曼编码来压缩字符串大小。

以常用的 User-Agent 为例,它在静态表中的索引值是 58,它的值是不存在表中的,因为它的值是多变的。第一次请求的时候它的 key 用 58 表示,表示这是一个 User-Agent ,它的值部分会进行霍夫曼编码(如果编码后的字符串变更长了,则不采用霍夫曼编码)。

服务端收到请求后,会将这个 User-Agent 添加到 Dynamic Table 缓存起来,分配一个新的索引值。客户端下一次请求时,假设上次请求 User-Agent 的在表中的索引位置是 62, 此时只需要发送 0xBE(同样的,高位置 1),便可以代表:User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.146 Safari/537.36。

其过程如下图所示:

最终,相同的 Header 只需要发送索引值,新的 Header 会重新加入 Dynamic Table。

5、请求优先级(Request Priorities)

把 HTTP 消息分为很多独立帧之后,就可以通过优化这些帧的交错和传输顺序进一步优化性能。每个流都可以带有一个 31 比特的优先值:0 表示最高优先级;2 的 31 次方 - 1 表示最低优先级。

服务器可以根据流的优先级,控制资源分配(CPU、内存、带宽),而在响应数据准备好之后,优先将最高优先级的帧发送给客户端。高优先级的流都应该优先发送,但又不会绝对的。绝对地准守,可能又会引入首队阻塞的问题:高优先级的请求慢导致阻塞其他资源交付。

分配处理资源和客户端与服务器间的带宽,不同优先级的混合也是必须的。客户端会指定哪个流是最重要的,有一些依赖参数,这样一个流可以依赖另外一个流。优先级别可以在运行时动态改变,当用户滚动页面时,可以告诉浏览器哪个图像是最重要的,你也可以在一组流中进行优先筛选,能够突然抓住重点流。

  • 优先级最高:主要的 html;

  • 优先级高:CSS 文件;

  • 优先级中:js 文件;

  • 优先级低:图片;

6、服务端推送(Server Push)

服务器可以对一个客户端请求发送多个响应,服务器向客户端推送资源无需客户端明确地请求。并且,服务端推送能把客户端所需要的资源伴随着 index.html 一起发送到客户端,省去了客户端重复请求的步骤。

正因为没有发起请求,建立连接等操作,所以静态资源通过服务端推送的方式可以极大地提升速度。Server Push 让 HTTP1.x 时代使用内嵌资源的优化手段变得没有意义;如果一个请求是由你的主页发起的,服务器很可能会响应主页内容、logo 以及样式表,因为它知道客户端会用到这些东西,这相当于在一个 HTML 文档内集合了所有的资源。

不过与之相比,服务器推送还有一个很大的优势:可以缓存!也让在遵循同源的情况下,不同页面之间可以共享缓存资源成为可能。

注意两点:

  1. 送遵循同源策略;

  2. 这种服务端的推送是基于客户端的请求响应来确定的;

当服务端需要主动推送某个资源时,便会发送一个 Frame Type 为 PUSH_PROMISE 的 Frame,里面带了 PUSH 需要新建的 Stream ID。意思是告诉客户端:接下来我要用这个 ID 向你发送东西,客户端准备好接着。客户端解析 Frame 时,发现它是一个 PUSH_PROMISE 类型,便会准备接收服务端要推送的流。

HTTP 2.0 性能瓶颈

启用 HTTP 2.0 后会给性能带来很大的提升,但同时也会带来新的性能瓶颈。因为现在所有的压力集中在底层一个 TCP 连接之上,TCP 很可能就是下一个性能瓶颈,比如 TCP 分组的队首阻塞问题,单个 TCP packet 丢失导致整个连接阻塞,无法逃避,此时所有消息都会受到影响。未来,服务器端针对 HTTP 2.0 下的 TCP 配置优化至关重要。

1、如何升级 HTTP 2.0 协议

nginx 服务器升级 HTTP 2.0 协议需要满足如下条件:

  1. nginx 版本高于 1.9.5;

  2. --with-HTTP_ssl_module 跟 --with-HTTP_v2_module;

--with-HTTP_ssl_module 模块是因为 HTTP/2.0 协议是一种 HTTPS 协议。

2、查看你的 nginx 配置

nginx -V

这个是已经添加了对应模块。没有这两个模块的需要手动编译安装。

3、找到 nginx 文件目录

4、编译安装 nginx 文件

./configure --prefix=/usr/local/nginx  --with-HTTP_stub_status_module  --with-HTTP_ssl_module  --with-HTTP_v2_module

然后执行如下命令,进行编译安装。

make
make install

5、更改 nginx 配置

安装结束后将 nginx.config 文件中 443 端口添加 HTTP2;

6、启动 nginx

最后一步,重启 nginx nginx restart(注意不要直接 nginx -s reload)。这时候你的站点就升级为了 HTTP 2.0 协议了。

检测

升级完成后,怎么确定自己的站点是 HTTP 2.0 协议呢?一般有如下几种方法:

chrome devtool

打开 chrome 调试工具,在 network 勾选 protocol 项,h2 代表的是 HTTP/2.0 协议,可以看到笔者的网站已经都升级好了;

网站

SSL lab 一个 SSL 服务器检测的网站,对网站进行安全评级,并将检测结果自动生成一个详细的评价报告;

插件

HTTP/2 and SPDY indicator 这是一款检测 HTTP/2.0 和 SPDY 协议(Google 开发的基于 TCP 的会话层协议)的插件。

-- End --

references:

  • https://HTTP2.github.io/

  • http://HTTP2.github.io/HTTP2-spec/compression.html

  • http://HTTP2.github.io/HTTP2-spec/compression.html#rfc.p.A

  • http://neyoufan.github.io/2017/01/06/android/OkHttp3中的HTTP2首部压缩

  • http://www.ssllabs.com/ssltest/analyze.html

本文对你有帮助吗?留言、转发、点好看是最大的支持,谢谢!

推荐阅读:

try-catch OOM,可行吗?

把RecyclerView撸出花儿来,自定义无限循环的LayoutManager

安卓原生运行Win11 再跑 Apk,搁着套娃呢!

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值