HTTP有两个主要的缺点:安全不足和性能不高
HTTPS通过引入SSL/TLS在安全性上达到了“极致”,但性能却没有提升,只优化了握手加密的环节,对于整体的数据传输没有提出更好的改进方案,还只能依赖于“长连接”
所以,在HTTPS逐渐成熟之后,HTTP就在性能方面开始“努力”,推出了HTTP/2
为什么不是HTTP/2.0
为什么HTTP/2不像之前的“1.0”、“1.1”那样叫做“2.0”呢?
官方解释:他们认为之前的“1.0”、“1.1”造成了很多的混乱和误解,让人在实际的使用中难以区分差异,所以就决定HTTP协议不再使用小版本号(minor version),只使用大版本(major version),从今而后HTTP协议不再出现HTTP/2.0、2.1,只会“HTTP/2”、“HTTP/3“…
这样就可以明确的辨别出协议版本的“跃进程度”,让协议在一段较长时间内保池稳定,每当发布新版本的HTTP协议都会有本质的不同,而不是小改良
兼容HTTP/1
因为必须保存功能是哪个的兼容,所以HTTP/2把HTTP分解成了“语义”和“语法”两个部分,“语义”成不做改动,与HTTP/1完全一致(即RFC7231)。比如请求方法、URI、状态码、头字段等概率都保持不变,这样基于HTTP的上层应用也不需要做任何修改,可以无缝转换到HTTP/2
特别的,与HTTPS不同,HTTP/2没有在URI里引入新的协议名,仍然用“http”表示明文协议,“https”表示加密协议
在“语义”保持稳定之后,HTTP/2在“语法”层做了“天翻地覆”的改造,完全变更了HTTP报文的传输方式
头部压缩
首先,HTTP/2对报头的头部做了一个“大手术”
- HTTP/1里可以用头字段
Content-Encoding
指定Body的编码方式,比如用gzip压缩来节约带宽,但报文的另一个组成部分------Header
却被无视了,没有针对它的优化手段 - 由于报文header一般会携带
User Agent
、Cookie
、Accept
、Server
等很多固定的头字段,多达几百字节甚至上千字节,但是Body往往只有十几字节(比如Get请求、204响应等)。更要命的是,成千上万的请求响应报文有很多字段值都是重复的,非常浪费,“长尾效应”导致大量带宽消耗在了这些冗余度极高的数据上 - 所以,HTTP/2把头部压缩作为性能改进的一个重点(优化方法就是压缩)
- 不过HTTP/2并没有使用传统的压缩算法,而是开发了专门的HPACK算法,在客户端和服务端两端建立“字典”,用索引号表示重复的字符串,还采用哈夫曼编码来压缩整数和字符串,可以达到50%~90%的高压缩率
二进制格式
HTTP/1是纯文本形式的报文,一目了然。
但是HTTP/2在这方面没有妥协,决定改变延续了十多年的现状,不再使用肉眼可见的ASCII码,而是向下层的TCP/IP协议靠拢,全面采用二进制格式
-
这样虽然对人不友好,但是却大大方便了计算机的解析。原来使用纯文本的时候容易出现多义性,比如大小写、空白字符、回车换行、多字少字等等,程序在处理的时候必须用复杂的状态机,效率低,还麻烦。
-
而二进制里面只有0和1,可以严格规定字段的大小、顺序、标志位等格式,“对就是对,错就是错”,解析起来没有歧义,实现简单,而且体积小,速度快,做到“内部提效”。
以二进制为基础,HTTP/2开始了改革:
- 它把TCP协议的部分特性挪到了应用层,把原来“header+body”的消息“打散”为整个小片的二进制“帧”(frame),用
HEADERS
帧存放头数据、DATA
帧存放实体数据 - 这种做法有点像
Chunked
分块编码的方式,也就是“化整为零”的思路,但HTTP/2数据分帧后Header+Body
的报文结构就完全消失了,协议里看到的只是一个个“碎片”
虚拟的“流”
消息的"碎片“到达目的地之后怎么组织起来呢?
-
HTTP/2为此定义了一个“流”(stream)的概念,它是二进制帧的双向传输序列,同一个消息往返的帧会分配一个唯一的流ID。你可以把它想象成一个虚拟的“数据流”,在里面流动的是一串有先后顺序的数据帧,这些数据帧按照次序组织起来就是HTTP/1里面的请求报文和响应报文
-
因为“流”是虚拟的,实际上并不存在,所以HTTP/2就可以在一个TCP连接上用“流”同时发送多个“碎片话”的消息,这就是常说的“多路复用”----多个往返通信都复用一个连接来处理
-
在“流”的层面来看,消息是一些有序的“帧”序列,而在“连接”的层面来看,消息却是乱序收发的“帧”。多个请求/响应之间没有了顺序关系,不需要排队等待,也就不会再出现“队头阻塞”问题,降低了延迟,大幅度提高了连接的利用率
-
为了更好的利用连接,加大吞吐量,HTTP/2还添加了一些控制帧来管理虚拟的“流”,实现了优先级和流量控制,这些特性也和TCP协议非常相似
-
HTTP/2还在一定程序上改变了传统的“请求-应答”机制,服务器不再是完全被动的响应请求,也可以新建“流”主动向客户端发送消息。比如,在浏览器刚请求HTML的时候就提前把可能会用到的JS、CSS文档发给客户段,减少等待的延迟。这被称为服务器推送(server push,也叫做cache push)
HTTP/2里的流可以实现HTTP/1里的“管道”功能,而且综合性能更好,所以“管道”在HTTP/2里就被废弃了
强化安全
处于兼容的考虑,HTTP/2延续了HTTP/1的“明文”特点,可以像以前一样使用明文传输数据,不强制使用加密通信,不过格式还是二进制,只是不需要解密。
但由于HTTPS已经是大势所趋,而且主流浏览器chrome、Firefox等都公开宣布只支持加密的HTTP/2,所以,“事实上”的HTTP/2都是加密的。也就是说,互联网上通常所能见到的HTTP/2都是使用“https”协议名,跑在TLS上面。
为了区分“加密”和“明文”这两个不同的版本,HTTP/2协议定义了两个字符串标识,h2
表示加密的HTTP/2,h2c
表示明文的HTTP/2,多出的那个字母c
表示clear text
.
在HTTP/2标准指定的时候(2015年)已经发现了很多SSL/TLS的弱点,而新的TLS1.3还没有发布,所以加密版本的HTTP/2在安全方面做了强化,要求下层的通信协议必须是TLS1.2以上,还要支持前向安全和SNI,并且把一些弱密码套件列入了“黑名单”,比如DES、RC4、CBC、SHA-1都不能在HTTP/2里面使用,相当于底层用的是“TLS1.25”
协议栈
从下面可以看出,HTTP/2是建立在HPACK
、Stream
、TLS1.2
基础之上的,比HTTP/1、HTTPS复杂了一些
总结
- http协议取消了小版本号,所以HTTP/2的正式名字不再是2.0
- HTTP/2在“语义”上兼容了HTTP/1,保留了请求方法、URI等传统概念
- HTTP/2使用
HPACK
算法压缩头部信息,消除冗余数据节约带宽 - HTTP/2的消息不再是
Header+Body
的形式,而是分散为多个二进制"帧" - HTTP/2使用虚拟的“流”传输消息,解决了“队头阻塞”问题,同时实现了“多路复用”,提高了连接的利用率
- HTTP/2也增强了安全性,要求至少是TLS1.2,而且禁用了很多不安全的密码套件