先说说 HTTP/ 1.1 协议的性能问题
原因:
- 延迟难以下降
- 并发连接有限
- 队头阻塞问题
- HTTP 头部巨大且重复
- 不支持服务器推送消息
HTTP/1.1自身采用的优化手段:
- 将多张小图合并成一张大图供浏览器 JavaScript 来切割使用;
- 将图片的二进制数据通过 base64 编码后,把编码数据嵌入到 HTML 或 CSS 文件中,以此来减少网络请求;
- 将多个体积较小的 JavaScript 文件使用 webpack 等工具打包成一个体积更大的 JavaScript 文件,以一个请求替代多个请求;
同时也会带来一个问题:当某个 js 文件变化了,需要重新请求同一个包里的所有 js 文件。
- 将同一个页面的资源分散到不同域名,提升并发连接上限,因为浏览器通常对通一域名的 HTTP 连接最大只能是 6 个。
尽管对 HTTP/1.1 协议的优化手段如此之多,但是效果还是不尽人意,因为这些手段都是对 HTTP/1.1 协议的“外 部”做优化。
兼容 HTTP/1.1
如何做到
- HTTP/2 没有在 URI 里引入新的协议名。
- 只在应用层做了改变,且还是基于 TCP 协议传输。
HTTP/2 把 HTTP 分 解成了「语义」和「语法」两个部分,「语义」层不做改动,与 HTTP/1.1 完全一致,如请求方法、状态码、头 字段等规则保留不变。
但是,HTTP/2 在「语法」层面做了很多改造,基本改变了 HTTP 报文的传输格式。
头部压缩
HTTP/2 使用了 HPACK 算法来对头部(called:Header)进行处理。
HPACK 算法
包含三部分:
- 静态字典
- 动态字典
- Huffman 编码(压缩算法)
实现过程:
客户端和服务器两端都会建立和维护【字典】(用长度较小的索引号表示重复的字符串),再用 Huffman 编码压缩数据,可达 50%~90% 的高压缩率。
静态字典(called:静态编码表)
HTTP/2 为高频出现在头部的字符串和字段建立了一张静态表(不会变化),共 61 组。
动态字典(called:动态编码表)
该表的 index 从 62 起步。
如,第一次发送时头部中的 【user-agent】字段数据有上百个字节,经过 Huffman 编码发送出去后,客户端和服务器双方都会更新自己的动态表,添加一个新的 index 号 62。那么在下一次发送的时候,就不用重复发这个字段的数据,只需要发 1 个字节的 index 号就行,因为双方都可以根据自己的动态表获取到字段的数据。
但是,动态表越大,占用的内存也就越大。也会影响服务器性能。
因此 Web 服务器都会提供类似 http2_max_request 的配置,用于限制一个连接上能够传输的请求数量,避免动态表无限增大,请求数量到达上限后,就会关闭 HTTP/2 连接来释放内存。
二进制帧
HTTP/2 将 HTTP/1 的文本格式改成二进制格式传输数据。
二进制数据使用 位运算 能高效解析。
具体细节:
帧类型:
HTTP/2 总共定义了 10 种类型的帧。分为数据帧和控制帧两类。
标志位:
用于携带简单的控制信息。(含:优先级......)
流标识符:
用来标识该 Frame 属于哪个 Stream。接收方可以据此从乱序的帧里找到相同Stream ID 的帧,从而有序组装信息。
故不同 Stream 的帧是可以乱序发送的。但同一 Stream 内部的帧必须是严格有序的。
帧数据:
存放的是通过 HPACK 算法压缩过的 HTTP 头部和包体。
并发传输
多个Stream 复用同一条 TCP 连接,达到并发的效果。
解决了 HTTP 层面的队头阻塞的问题。但仍存在 TCP 层面的队头阻塞问题。
具体细节:
由上图可知:
- 1 个 TCP 连接包含一个或多个 Stream;
- 1 个 Stream 里可包含 一个或多个 Message(content:请求或响应);
- 1 个 Message 里可包含 一个或多个 Frame(content:一个或多个 TCP 报文);
谁可以建立 Stream
客户端和服务器双方都可以建立 Stream。
关于 Stream ID
- 客户端建立的 Stream 的 Stream ID 必须是奇数,服务器建立的必须是偶数。
- 同一个连接中的 Stream ID 是不能复用的,只能顺序递增。
- 当 ID 数耗尽时,需要发一个控制帧 【GOAWAY】来关闭 TCP 连接。
服务器主动推送资源
如何实现:
服务器在推送资源时,会通过【PUSH_PROMISE】帧传输 HTTP 头部,并通过帧中的 Promise Stream ID 字段告知客户端,接下来会在哪个偶数号 Stream 中发送包体。
仍存问题:HTTP/2 在 TCP 层面上的队头阻塞问题
HTTP/2 是基于 TCP 协议来传输数据的,TCP 是字节流协议,TCP 层必须保证收到的字节数据是完整且连续的, 这样内核才会将缓冲区里的数据返回给 HTTP 应用,那么当「前 1 个字节数据」没有到达时,后收到的字节数据只 能存放在内核缓冲区里,只有等到这 1 个字节数据到达时,HTTP/2 应用层才能从内核中拿到数据 。
标签:HTTP,1.1,Stream,TCP,服务器,字节 From: https://www.cnblogs.com/tiddler/p/16651121.html