基础概念
帧: 消息通信的最小单位,一个请求或者响应由一个或多个帧组成。
流: 链接中的一个虚拟通道,每个流有一个唯一的整数ID。
HTTP/2采用二进制格式传输数据(HTTP/1采用的文本格式),二进制协议解析起来更高效。
HTTP/2中,同域名下的所有通信都是再单个链接上完成的,该链接可以承载任意数量的双向数据流。多个帧可以乱序发送,根据帧首部流标识重新组装。
多路复用
所有请求都是通过一个TCP连接并发完成。
HTTP/1中如果想并发多个请求,必须使用多个TCP链接,且浏览器为了控制资源,会对单个域名有6-8个TCP链接请求的限制,超过限制会被挂起等待。
HTTP/2使用二进制分帧之后,不在依赖TCP链接去实现多流并行了。再HTTP/2中:
-
同域名下所有通信都在单个链接上完成
-
单个链接可以承载任意数量的双向数据流
-
数据流以消息的形式发送,一个消息由一个或者多个帧组成,多个帧之间可以乱序发送,根据帧首部流标识重新组装。
这一特性使性能有极大提升:
-
同个域名只需要一个TCP链接,消除了因多个TCP链接而带来的延时和内存消耗/
-
单个连接上并行交错的请求和响应,互不影响
-
HTTP/2中每个请求都可以携带优先级标识,采取不同策略发送流消息和帧。
服务器推送
HTTP/1 中,同一个TCP链接里,上一个回应response发送完了,服务器才能发送下一个,但在HTTP/2中可以多个response一起发送。
举个例子:一个简单的HTML页面
<html>
<head>
<link rel="stylesheet" href="style.css">
</head>
<body>
<img src="example.png">
</body>
</html>
这里面有三个文件需要处理:1. HTML页面 2. css文件style.css 3. 图片example.png。在HTTP/1 中client需要发送三个请求给Server
GET /index.html HTTP/1.1
GET /style.css HTTP/1.1
GET /example.png HTTP/1.1
平时为了解决这种问题,一般采用:
-
png转base64 嵌入css文件或这html文件
-
preload预加载,加载上一个页面的时候以前下载相关资源,但是这种方式没有减少http请求次数。
<link rel="preload" href="/style.css" as="style">
但是HTTP/2的server push可以实现,当client只请求index.html
的时候,server把index.html
、style.css
、example.png
全部发送给client,只需要一轮HTTP通信,Client就得到了全部资源。
头部压缩
HTTP/2对消息头进行压缩,节省消息头占用的网络流量。
在client与服务器之间:
-
维护一份相同的静态表,包含常见的头部名称,以及特别常见的头部名称与值的组合
-
维护一份相同的动态表,可以动态添加内容;
-
基于静态哈夫曼码表的哈夫曼编码;
在HTTP头里,有些key:value是固定的,eg:
:method: GET
:scheme: http
在编码时,它们直接用一个index编号代替,例如:method:GET是2,这些在一个静态表定义。对于静态表里的字段,原来需要N个字符表示的现在只需要一个索引即可 。
如何判断是否支持HTTP/2
Chrome控制台中,打开network tab,看到请求的protocol为h2的就是HTTP/2的请求。
如何开启HTTP/2
需要后端支持 linux。也可以在nodejs中启用HTTP/2,nodejs官网示例:
// 服务器端示例
const http2 = require('http2');
const fs = require('fs');
const server = http2.createSecureServer({
key: fs.readFileSync('localhost-privkey.pem'),
cert: fs.readFileSync('localhost-cert.pem')
});
server.on('error', (err) => console.error(err));
server.on('stream', (stream, headers) => {
// 流是双工的
stream.respond({
'content-type': 'text/html; charset=utf-8',
':status': 200
});
stream.end('<h1>Hello World</h1>');
});
server.listen(8443);