HTTP2协议特性解析
随着web技术的飞速发展,1999年制定的HTTP 1.1已经无法满足大家对性能的要求,Google推出协议SPDY,旨在解决HTTP 1.1中广为人知的性能问题。SPDY得到了Chrome、Firefox和Opera的支持,很多大型网站(如谷歌、Twitter、Facebook、淘宝)都对兼容客户端使用SPDY。SPDY在被行业采用并证明能够大幅提升性能之后,已经具备了成为一个标准的条件。
HTTP工作组采用了SPDY v2草案作为制定HTTP 2.0标准的起点,2014年12月将HTTP/2标准提议递交至IESG进行讨论,于2015年2月17日被批准。HTTP/2标准于2015年5月以RFC 7540正式发表。至此,SPDY完成了历史的使命,即将退出历史的舞台,HTTP/2粉墨登场。
在介绍http2之前先总结一下http的几个重要概念
连接:Connection
一个传输层的实际环流,它是建立在两个相互通讯的应用程序之间。
在http1.1,request和reponse头中都有可能出现一个connection的头,此header的含义是当client和server通信时对于长链接如何进行处理。
在http1.1中,client和server都是默认对方支持长链接的, 如果client使用http1.1协议,但又不希望使用长链接,则需要在header中指明connection的值为close;如果server方也不想支持长链接,则在response中也需要明确说明connection的值为close。不论request还是response的header中包含了值为close的connection,都表明当前正在使用的tcp链接在当天请求处理完毕后会被断掉。以后client再进行新的请求时就必须创建新的tcp链接了。
消息:Message
HTTP通讯的基本单位,包括一个结构化的八元组序列并通过连接传输。
请求:Request
一个从客户端到服务器的请求信息包括应用于资源的方法、资源的标识符和协议的版本号。
响应:Response
一个从服务器返回的信息包括HTTP协议的版本号、请求的状态(例如“成功”或“没找到”)和文档的MIME类型。
资源:Resource
由URI标识的网络数据对象或服务。
实体:Entity
数据资源或来自服务资源的回映的一种特殊表示方法,它可能被包围在一个请求或响应信息中。一个实体包括实体头信息和实体的本身内容。
客户机:Client
一个为发送请求目的而建立连接的应用程序。
用户代理:UserAgent
初始化一个请求的客户机。它们是浏览器、编辑器或其它用户工具。
服务器:Server
一个接受连接并对请求返回信息的应用程序。
源服务器:Originserver
是一个给定资源可以在其上驻留或被创建的服务器。
代理:Proxy
一个中间程序,它可以充当一个服务器,也可以充当一个客户机,为其它客户机建立请求。请求是通过可能的翻译在内部或经过传递到其它的服务器中。一个代理在发送请求信息之前,必须解释并且如果可能重写它。
代理经常作为通过防火墙的客户机端的门户,代理还可以作为一个帮助应用来通过协议处理没有被用户代理完成的请求。
网关:Gateway
一个作为其它服务器中间媒介的服务器。与代理不同的是,网关接受请求就好象对被请求的资源来说它就是源服务器;发出请求的客户机并没有意识到它在同网关打交道。
网关经常作为通过防火墙的服务器端的门户,网关还可以作为一个协议翻译器以便存取那些存储在非HTTP系统中的资源。
通道:Tunnel
是作为两个连接中继的中介程序。一旦激活,通道便被认为不属于HTTP通讯,尽管通道可能是被一个HTTP请求初始化的。当被中继的连接两端关闭时,通道便消失。当一个门户(Portal)必须存在或中介(Intermediary)不能解释中继的通讯时通道被经常使用。
缓存:Cache
反应信息的局域存储。
在HTTP的语义、HTTP方法、状态码、URI和首部字段等核心概念不变的情况下,HTTP/2实现了性能优化,HTTP/2具体有哪些变化呢?
二进制分帧(Binary Framing)
HTTP1.x以换行符作为纯文本的分隔符。
HTTP/2将所有传输的信息分割为更小的消息和帧,并对它们采用二进制格式的编码,我们先了解几个概念:
帧(Frame):HTTP/2通信的最小单位,每个帧包含帧首部,至少也会标识出当前帧所属的流。
消息(Message):由一个或多个帧组合而成,例如请求和响应。
连接(Connection):与 HTTP/1 相同,都是指对应的 TCP 连接;
流(Stream):已建立的连接上的双向字节流。
在HTTP/2中,数据流以消息的形式发送,而消息由一个或多个帧组成,帧可以在数据流上乱序发送,然后再根据每个帧首部的流标识符重新组装。二进制分帧是HTTP/2的基石,其他优化都是在这一基础上来实现的。
多路复用(Request and Response Multiplexing)
HTTP1.x中,如果想并发多个请求,必须使用多个TCP链接,且浏览器为了控制资源,还会对单个域名有6-8的个数限制,如下图,红色圈出来的请求就因域名链接数已超过限制,而被挂起等待了一段时间:
Alt text
针对这一问题,我们做了很多优化,例如合并请求、图片精灵、散列域名等
在 HTTP/2 中,有了二进制分帧之后,HTTP 2.0不再依赖TCP链接去实现多流并行了,在HTTP/2:
同域名下所有通信都在单个连接上完成。
单个连接可以承载任意数量的双向数据流。
数据流以消息的形式发送,而消息又由一个或多个帧组成,多个帧之间可以乱序发送,因为根据帧首部的流标识可以重新组装。
这一特性,性能会有极大的提升,因为:
同个域名只需要占用一个TCP连接,消除了因多个TCP连接而带来的延时和内存消耗。
单个连接上可以并行交错的请求和响应,之间互不干扰。
流优先级( Stream priority)
在HTTP/2中,每个请求都可以带一个31bit的优先值,0表示最高优先级, 数值越大优先级越低。有了这个优先值,客户端和服务器就可以在处理不同的流时采取不同的策略,以最优的方式发送流、消息和帧。
服务器推送(Server push)
Server push是HTTP/2中一个很强大的功能:
服务器除了响应客户端的请求外,还可以向客户端额外推送资源。
服务器推送的资源有自己独立的URL, 可以被浏览器缓存,可以达到多页面共享。
资源推送遵守同源策略,服务器不可随便推送第三方资源给客户端。
客户端可以拒绝推送过来的资源。
有了这一特性,我们可以做什么?
应用可以通过额外的http头部,列出需要服务器推送哪些资源。
服务器可以解析请求的html,推测出客户端接下来需要请求的资源,然后提前向客户端推送。
等等
头部压缩(Header Compression)
HTTP每一次通信都会携带一组头部,用于描述这次通信的的资源、浏览器属性、cookie等,例如
在HTTP 1.x中,这些信息都是以纯文本协议发送的,给每个请求增加了不小的负荷。
为了减少这块的开销并提升性能, HTTP/2会压缩这些首部:
HTTP/2在客户端和服务器端使用“首部表”来跟踪和存储之前发送的键-值对,对于相同的数据,不再通过每次请求和响应发送;
首部表在HTTP/2的连接存续期内始终存在,由客户端和服务器共同渐进地更新;
每个新的首部键-值对要么被追加到当前表的末尾,要么替换表中之前的值。
例如:下图中的两个请求, 请求一发送了所有的头部字段,第二个请求则只需要发送差异数据,这样可以减少冗余数据,降低开销。
我们来看一个实际的例子,下面是用WireShark抓取的访问google首页的包:
上图是是访问https://www.google.com/抓到的第一个请求的头部,可以看到头部的内容,总共占用了437 bytes,我们选中头部的cookie,可以看到cookie总共占用了118 bytes。接下来我们看看第二个请求的头部:
从上图可以看到,得益于头部压缩,第二个请求中cookie只占用了1个字节,我们来看看变化了的Accept字段:
由于Accept字段与请求一中的内容不同,需要发送给服务器,所以占用了29 bytes。
应用层协商协议(APLN:Aplication Layer Protocol Negotiation)
客户端、服务器都需要升级才能支持HTTP 2.0,升级过程中就存在HTTP1.1、HTTP 2.0并存的情况,然而他们都使用的80端口,那么如何来选择使用什么协议通信呢?
APLN就是为了解决这个问题的,通过协商来选择通信的协议:
客户端发起请求,如果支持HTTP/2,则带upgrade头部:
GET /page HTTP/1.1
Host: server.example.com
Connection: Upgrade, HTTP2-Settings
Upgrade: HTTP/2.0
HTTP2-Settings: (SETTINGS payload)
服务器不支持,则拒绝升级,通过HTTP1.1返回响应
HTTP/1.1 200 OK
Content-length: 243
Content-type: text/html
(... HTTP 1.1 response ...)
服务器支持,则接受升级,切换到新分帧,使用HTTP/2通信。
HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: HTTP/2.0
(... HTTP 2.0 response ...)
使用协议协商,无论是哪一种情况,都不需要额外的往返,如果客户端通过记录或者其他方式,知道服务器支持HTTP/2,则直接使用HTTP/2通信,无需再协议协商。
如何判断网站是否使用了HTTP/2
使用谷歌浏览器在控制台console下,运行如下代码:
(function(){ // 保证这个方法只在支持loadTimes的chrome浏览器下执行 if(window.chrome && typeof chrome.loadTimes === 'function') { var loadTimes = window.chrome.loadTimes(); var spdy = loadTimes.wasFetchedViaSpdy; var info = loadTimes.npnNegotiatedProtocol || loadTimes.connectionInfo; // 就以 「h2」作为判断标识 if(spdy && /^h2/i.test(info)) { return console.info('本站点使用了HTTP/2'); } } console.warn('本站点没有使用HTTP/2'); })();
总结
最后,简而概之,HTTP/2的通过支持请求与响应的多路复用来减少延迟,通过压缩HTTP首部字段将协议开销降至最低,同时增加对请求优先级和服务器端推送的支持。
HTTP/2性能得到了极大的提升,我们在HTTP 1.1时代做的有些优化反而成了鸡肋,在升级过程中,如何让HTTP/2 和 HTTP 1.1的用户都能得到最优的性能,这是对于我们的另外一大挑战。
参考资料来源:
1. https://blog.csdn.net/yinbucheng/article/details/64461202?locationNum=12&fps=1
2. https://www.cnblogs.com/yingsmirk/p/5248506.html