HTTP/2核心特性
HTTP/1.1服役的这二十余年,互联网发生了翻天覆地的变化,由于各种业务场景的复杂度今非昔比,HTTP/1.1越来越容易遭受“嫌弃”,此时HTTP/2闪亮登场,本文主要列举描述其核心特性。
基于HTTPS
虽然RFC规范并没有约束HTTP/2必须基于HTTPS才能使用,但业界浏览器实现都依赖HTTPS前提条件,如果要升级到HTTP/2,第一步是升级到HTTPS。
二进制分帧
在HTTP/2之前,请求与响应的内容都是直接明文传输,比如客户端发起一个请求,请求的起始行内容GET /res HTTP/1.1
直接传输给服务器,服务器得到的数据就是GET /res HTTP/1.1
。
到了HTTP/2,所有的通信数据都经过了二进制分帧处理,帧就是火车,而数据就是乘客。针对上面的起始行数据,假设其二进制表示为10000000000
,那么我们的火车可能可能是这样的:
|火车|控制室|乘务员|餐车|10000000000|
一列装不下,那就两列:
|火车1号|控制室|乘务员|餐车|100000|
|火车2号|控制室|乘务员|餐车|00000|
到达目的地之后,服务器再从火车中提取“乘客”还原成一个整体10000000000
。
HTTP/2中“火车”的结构:
+-----------------------------------------------+
| Length (24) |
+---------------+---------------+---------------+
| Type (8) | Flags (8) |
+-+-------------+---------------+-------------------------------+
|R| Stream Identifier (31) |
+=+=============================================================+
| Frame Payload (0...) ...
+---------------------------------------------------------------+
上图括号中的数字表示固定位长,Frame Payload的长度由Length的值决定,此“火车”各车厢具体含义参见表一。
表一
描述 | 长度 | 含义 |
---|---|---|
Length | 24 | Frame Payload的长度 |
Type | 8 | 帧类型,参见表二 |
Flags | 8 | 帧标志位,配合帧类型使用,根据帧类型不同含义不同 |
R | 1 | 保留位,目前不允许使用 |
Stream Identifier | 31 | 流标志,流是一系列帧组成的抽象概念,此ID表明帧所在的流 |
Frame Payload | Length Value | 数据负载 |
表二
Type | 作用 |
---|---|
SETTINGS | 接收者向发送者通告己方设定,设置项参见表三 |
HEADER | 用于发送HTTP请求或响应头并打开一个流,HTTP/1.1中的请求行与响应行被拆分到标头中表示 |
CONTINUATION | 协助HEADERS/PUSH_PROMISE等单帧无法包含完整的报头剩余部分数据 |
DATA | 请求与响应的内容载体 |
PUSH_PROMISE | 服务器端通知对端初始化一个新的推送流准备稍后推送数据 |
PING | 发送者测量最小往返时间,心跳机制用于检测空闲连接是否有效 |
PRIORITY | 表达发送方对流优先级权重的建议值 |
WINDOW_UPDATE | 通知对方流量控制端口大小变化 |
RST_STREAWM | 允许立即终止流,表明请求取消或出现错误 |
GOAWAY | 正常关闭流 |
表三
设置项 | 作用 |
---|---|
SETTINGS_HEADER_TABLE_SIZE | 接收者报头表的字节数最大值,默认为4096字节 |
SETTINGS_ENABLE_PUSH | 0禁止服务器推送,1允许推送 |
SETTINGS_MAX_CONCURRENT_STREAMS | 发送者允许可打开流的最大值,建议值100,0表示不能打开新的流 |
SETTINGS_INITIAL_WINDOW_SIZE | 发送端流量控窗口大小,用于控制整个连接的传输速度 |
SETTINGS_MAX_HEADER_LIST_SIZE | 发送端通告自己准备接收的报头集合最大字节数 |
多路复用
如果用火车比喻帧那么可用车次比作流,不同车次上的乘客互不影响。假设有三个旅行团的人在远方等待被接回到旅行社,因为旅行团的人一旦混在一起就无法区分,我们可以选择同时派出三个车次的火车把三批人接回旅行社,这就是多车次复用。
车次跑在铁轨上对应流建立在TCP连接上,铁轨上可同时存在不同的车次且可重复使用,TCP连接上的流也一样,同一个连接中可以有大量同时处于传输中的流,这就是多路复用概念。
在HTTP/1.1中,一个TCP连接上只能同时存在一个进行中的HTTP事务,假设客户端与服务器只打开一个TCP连接,如果需要完成10个HTTP事务,只能one-by-one逐个处理,其中任何一个出现网络异常,都会阻塞其后面的请求,这就是队头阻塞,多路复用完美解决了HTTP/1.1中存在的队头阻塞问题。
多路复用,体现出HTTP/2与HTTP/1.1的一个重要差异:
- HTTP/2只打开一个TCP连接然后通过多个(SETTINGS_MAX_CONCURRENT_STREAMS)虚拟流与服务器通信
- HTTP/1.1浏览器端针对单域名默认使用6个TCP连接与服务器进行通信
在做网站优化时,使用6个TCP连接的并发能力,还是使用单个TCP连接的多路复用特性,不同的情况下可能各具优势,比如极端情况“网站只展示6张1M的图片”,6个TCP连接的方式可能更有利,这主要是因为TCP的慢启动、拥塞机制,内容过多不本文不做讨论。
服务器推送
预留车次从服务器端发车,这种功能就称为服务器推送,这个特性利用的好可以让部分请求节省半个RTT时长。假设只考虑报文的传输耗时,如果没有服务器推送,客户端获取资源耗时至少是数据包在网络上的一次往返时延(RTT),借助服务器推送可以省下数据包由客户端传输到服务器的时延。
假设用户访问页面index.html包含index.js,如果没有服务器推送,需要先获取index.html再获取index.js,需要2个RTT时长,而借助服务器推送可以做到用户访问index.html的同时返回index.js内容,较前者可节省1个RTT时长。所节省的1个RTT时长中有半个消耗在顺序依赖等待上,没有把它看成主动推送节省的时间,而是主动推送时机所带来的优化。
要用好服务器推送,关键问题在于决定推送时机与推送内容。推送内容一般是可缓存资源,不能用户一访问index.html就推送index.js,在缓存有效的情况下这种推送就是在浪费带宽,也不能在用户一访问网站,就把可能用到的所有资源都推送过去。
流控制
流控制,是指可以控制对端发送数据的速度,使负载在自己的处理能力范围内。在HTTP/1.1中,请求或响应数据一当开始发送,接收端只有选择全盘接收或丢弃,而在HTTP/2中,通过流控制接收方可以与发送方协商合理的发送速率,避免接收端处理不过来。
标头压缩
由于不同HTTP事务请求与响应中Header部分重复内容过多,导致网络上传输了过多重复信息,影响到传输效率,所以在HTTP/2中引入了标头压缩以减少数据传输大小。比如要传送cache-control: public, max-age=600
,在HTTP/2中可能只需要传送1:2, 3=600
即可,此方法针对大cookie的情况,优化效果更为显著。
实现原理基于标头静态表+动态表,比如上述例中的1:2, 3=600
按照下表还原即可得到原始内容,当然这里只是随意举例,并不严谨。
code | value |
---|---|
1 | cache-control |
2 | public |
3 | max-age |
数据流优先级
将HTTP消息分解为很多独立的帧之后,我们就可以复用多个数据流中的帧,客户端和服务器交错发送和传输这些帧的顺序就成为关键的性能决定因素。为了做到这一点,HTTP/2标准允许每个数据流都有一个关联的权重和依赖关系:
- 可以向每个数据流分配一个介于 1 至 256 之间的整数
- 每个数据流与其他数据流之间可以存在显式依赖关系
数据流依赖关系和权重的组合让客户端可以构建和传递“优先级树”,表明它倾向于如何接收响应。反过来,服务器可以使用此信息通过控制CPU、内存和其他资源的分配设定数据流处理的优先级,在资源数据可用之后,带宽分配可以确保将高优先级响应以最优方式传输至客户端。
本小段内容摘录自Google HTTP/2简介,这个特性要用好,感觉比服务器推送更有挑战
总结
HTTP协议成为21世纪目前最流行的、应用最广的应用层协议,我认为与协议本身的简单性有着莫大关联,HTTP/2对于性能的提升毋庸置疑,但也让HTTP更为复杂,大家又要学更多内容了,哎!