摘要:由于学习了 gRPC, 并且 gRPC 是基于 HTTP2.0 协议的,那我们今天就来学习 HTTP 协议的第二个版本。
简介
简单来说,HTTP/2(超文本传输协议第2版),主要基于 Google 提出的 SPDY 协议。特点是:在不改动 HTTP 语义、方法、状态码、URI 及首部字段的情况下,大幅度提高了 web 的性能。
HTTP1.X 的缺点
1:请求串行
HTTP/1.0 一次只运行在一个 TCP 连接上发起一个请求,HTTP/1.1 使用的流水线技术也只能部分处理请求并发,依然会存在对头阻塞的问题,因此客户端在需要发起多次请求时,通常会采用建立多连接[1]来减少延迟。
2:单向请求
只能由客户端主动发起,服务端进行返回,服务端不能主动推送。
3:首部信息冗余量大
请求报文与响应报文首部信息冗余量大。
SPDY 协议
SPDY 介绍
SPDY 是 Speedy 的昵音,意味“更快”。它是 Google 开发的基于 TCP 协议的应用层协议。目标是优化 HTTP 协议的性能,通过压缩、多路复用和优先级等技术,缩短网页的加载时间并提高安全性。SPDY 协议的核心思想是尽量减少 TCP 的连接次数。SPDY 并不是一种用于替代 HTTP 的协议,而是对 HTTP 协议的增强。
SPDY 对 HTTP/1.x 的优化
1:降低延迟:针对 HTTP 高延迟的问题, SPDY 优雅的采取了多路复用(multiplexing)。多路复用通过多个请求 stream 共享一个 TCP 连接的方式,解决了 HOL blocking[2] 的问题,降低了延迟,同时提高了宽带的利用率。
2:请求优先级:多路复用带来一个新的问题是,在连接共享的基础之上有可能会导致关键请求被阻塞。SPDY 允许给每个 request 设置优先级,这样重要的请求就会优先得到响 应。比如浏览器加载首页。
3:header压缩:前面提到 HTTP/1.x 的 header 很多时候都是重复多余的,选择合适的压缩算法可以减少包的大小和数量, 对头部使用 deflate 算法进行压缩。
4:基于 HTTPS 的加密协议传输(SPDY 强制要求使用 SSL/TLS),大大提高了传输数据的可靠性。
5:服务端推送:服务器可主动推送给客户端文件,保存到客户端的缓存中,当客户端再次尝试获取该资源时,就可以直接从缓冲中获取到,不用再发请求了。
HTTP2.0 新特征
二进制分帧
HTTP/1.x 的解析是基于文本。基于文本协议的格式解析存在天然的缺陷,文本的表现形式有多样性,要做到健壮性考虑的场景必然很多;二进制只认 0 & 1 的组合,实现方便并且健壮。
多路复用
即连接共享。在 http/2.0 中,有两个概念非常重要:帧(frame) 和 流(stream), 帧是最小的数据单位,每个帧都会标识出该帧属于哪个流,流是各个帧组成的数据流。所谓多路复用,即在一个 TCP 连接中存在多个流,每个流都有一个ID, 即可以同时发送多个请求,不再依赖多个 TCP 连接去实现多流并行了。通过该技术,可以避免 HTTP 旧版本的堆头阻塞问题,极大提高传输性能。
Header 压缩
在 HTTP1.x 中,我们使用文本的形式传输 header, 在 header 中携带 cookie 的话,每次都需要重复传输几百到几千的字节。在 HTTP2.0 中,我们使用了 HPACK[3](头部压缩算法) 压缩格式对传输的 header 进行编码,减少了 header 的大小,并在两端维护了索引表,用于记录出现过的 header, 后面在传输过程中就可以传输已经记录过的 header 的键名,对端收到后就可以通过键名找到对应的值。
服务器 PUSH
在 HTTP2.0 中,服务器可以在客户端某个请求后,主动推送其它资源。这个是唯一一个需要客户端进行配置的功能。具体怎么配置[ref1]可参见文末参考文献部分。
文中相关问题解释
【1】建立多连接问题:
在 HTTP1.x 中,一半都是通过建立多个 TCP 连接来实现并行请求,但是浏览器对同一个域名的资源请求是有并发数限制的,比如 chrome 浏览器支持并发数量为 6,具体可参考[ref1],我们可以通过这个测试链接来对 HTTP2.0 有一个直观的感受,点击[ref2]进行测试,我们也可以验证下我们的 chrome 并发数,下图是我的 chrome 浏览器在加载图片时候的情景,通过下图,我们可以看到:http/1.1 建立了 6 个 tcp 连接, 而 http/2.0 只建立了一个 tcp 连接。
【2】HTTP HOL blocking 的问题
HTTP/1.1 增加了管道化技术,允许客户端不用等到服务器的响应就能发送下一个请求。目的为 在一次 TCP 连接上可以并行发送多个请求,来提高网络利用率。但是它有一个很大的缺点,服务器必须按照请求的顺序来响应。即后续请求的响应必须等到第一个响应发送之后才能发送,即使后续响应已经完成。这就是 HTTP 队头阻塞的来由。主要原因是请求是没有编号的,客户端无法区分回复是哪一个请求的。
【3】HPACK
基本原理:简单来说,HPACK 使用两个索引表(静态索引表和动态索引表,发送端和接收端共同维护)来把头部映射到索引值,并对不存在的头部使用 静态huffman 编码,并动态缓存到索引,从而达到压缩头部的效果。
实现细节:静态索引表是预先定义再 RFC 里的固定的头部,也就是说当要发送的值符合静态表时,用对应的 index 替换即可,这样就大大压缩了头部的大小,这个表是预先定义号的,只有固定的几十个值,如果遇到不在静态表中的值,就会用我们的动态表;动态索引表是一个由先进先出队列维护的有空间限制的表,里面同样维护的是头部和对应的索引。每个动态表只针对一个连接(一般是指 TCP 连接),当一个头部没有出现过的时候,就把他插入动态表中,下次同名的值就可能会在表中擦回到索引并替换掉头部。
参考资料:
[href1]: http://www.ruanyifeng.com/blog/2018/03/http2_server_push.html
[href2]: https://www.cnblogs.com/sunsky303/p/8862128.html
[href3]: https://http2.akamai.com/demo