HTTP/2背景和新特性简介

techbrood 发表于 2016-01-30 22:45:38

标签: http2, protocol

- +

在前面的一篇文章中已经介绍了HTTP1.1的现状和问题

为了从协议本身上解决这些问题,业界推进了HTTP/2的制定,主要基于如下几个方面:

  1. 降低协议对延迟的敏感

  2. 修复pipelining和head of lineblocking的问题

  3. 防止主机需求更高的连接数量

  4. 保留所有现有的接口,内容,URI格式和结构

  5. 由IETF的HTTPbis工作组来制定

IETF和HTTPbis工作组

The Internet Engineering Task Force(IETF)是一个开发和推广互联网标准的组织。他们的重心是在协议层面。他们最出名的工作是制定了TCP、DNS、FTP和它们最佳实践的RFC规范,但HTTP和许多其他协议却进展缓慢。

IETF成立了独立的“工作小组”以便完成某些特定领域内的目标,他们建立了一个“章程”用以制定达到目标的指导方针和规范。在这里,任何人都可以参与讨论和开发,并且每个人有同等的话语权,没人关心你来自哪个公司或组织。

HTTPbis工作组(我们待会儿再解释这个名字)在2007年夏天成立之后就着手于HTTP1.1标准的更新。在组内,关于下一版本HTTP协议的讨论实际上在2012年后期才开始。而HTTP1.1的更新工作在2014年初完成,并被整理成RFC7320系列。

工作组名字中的“bis”来自拉丁语中表示“二”的副词,Bis通常被IETF用作名字的后缀来以表示标准的升级或者一些二次工作, 比如这里是针对HTTP1.1。

起源于SPDY的http2

SPDY是由Google牵头开发的协议。他们将其开源,使得每个人都可以参与开发。但很明显,他们通过控制浏览器的实现和 享用着优质服务的大量用户来获益。

当HTTPbis小组决定开始制定http2的时候,SPDY已经充分证实了它是一个非常好用的方案。当时已经有人在互联网上成功 部署SPDY,并且也有一些文章讨论他的性能。因此,http2便基于SPDY/3草案进行一些修改之后发布了http2的draft-00。

http2的基本概念

http2到底做了些什么呢?而HTTPbis小组究竟又应该把它制定到什么样的程度呢?

事实上,http2有着非常明确的职责限定,但有时这也会成为小组成员在开展工作时的掣肘。

它必须维持HTTP的范式。毕竟它只是一个让客户端发送请求到服务器的基于TCP的协议。

不能改变http://和https://这样的URL,也不能对其添加新的结构。使用这类URL的网站太多了,没法指望他们全部改变。

HTTP1的服务器和客户端依然会存在很久,所以我们必须提供HTTP1到HTTP2服务器的代理。

随后,我们也要让这种代理能够将HTTP2的功能一对一的映射到HTTP1.1的客户端。

删除或者减少协议里面那些可选的部分。虽然这并不算的上是一个需求,但是SPDY和Google的团队都非常喜欢这点。 通过让协议里所有的内容都成为了强制性要求,可以防止人们在实现的时候偷懒,从而规避一些将来可能会发生的问 题。

不再使用小版本号。服务器和客户端都必须确定自己是否完整兼容http2或者彻底不兼容。如果将来该协议需要被扩充或者变更,那么新的协议将会是http3,而不是http2.x。

http2和现有的URI结构

如上所述,现有的URI结构正在被HTTP1.x使用而不能被更换,所以http2也必须沿用该结构。因此不得不找到一种方式将使用的协议升级至http2,比如可以要求服务器让它作响应时使用http2来替代旧的协议。

HTTP1.1本身就制定过“升级”的方案:提供一个首部字段,表示允许服务器在收到旧协议请求的同时,可以向客户端发送新协议的响应。但这一方案往往需要多花费一次往返通信来作为升级的代价。

而这一代价是SPDY团队不想接受的。因为他们只实现了基于TLS的SPDY,所以他们开发了一个TLS的扩展去简化协议的协商。这个扩展被称作NPN(Next Protocol Negotiation),借助于此,服务器会通知客户端所有它支持的协议,让客户端从中选择一个合适的来进行通讯。

为https://所准备的http2

有相当多的人关注到了http2可以在TLS上正常的运作,而SPDY又只支持TLS,所以按理说TLS也应成为http2必需的组件,不过出乎大家意料的是http2将TLS标记成了可选。然而,全球两大浏览器领导者——Firefox和Chrome都明确地表示,他们只会实现基于TLS的http2.

选择TLS的原因的其中之一是希望保护以及尊重用户的隐私,而早期的评估结果也表明,在TLS上建立新的协议更有可能获得成功。而这其中部分原因是人们普遍认为任何来自80端口的流量都是基于HTTP1.1亦或者是其某个变种的,而不是另外一种全新的协议。

关于是否应该强制使用TLS的主题在邮件组内和会议上引起了不小的争议——这到底是好是坏呢?不管怎么样,对于这种备受争议的话题还是请谨慎讨论,尤其是当你面对一个HTTPbis小组成员的时候。

诸如此类,还有一个激烈而长期的讨论,即:如果选择了使用TLS,那http2是否应该强制规定密码列表,也许应该建立起一个黑名单,又或者它根本就不需要从TLS层得到任何东西。不过这个问题还是留给TLS工作组去解决吧,最后的规范中指定了TLS最低版本为1.2,并且会有加密组的限制。

基于TLS之上的http2协商

Next Protocol Negotiation(NPN)是一个用来在TLS服务器上协商SPDY的协议。IETF将这个非正式标准进行规范化,从而演变成了ALPN(Application Layer Protocol Negotiation)。ALPN会随着http2的应用被推广,而SPDY的客户端与服务器则会继续使用NPN。

由于NPN先于ALPN诞生,而ALPN又经历了一些标准化过程,所以许多早期的http2客户端和服务器在协商http2时会将这两者同时实现。与此同时,考虑到SPDY会使用NPN,而许多服务器又会同时提供SPDY以及http2,所以在这些服务器上同时支持ALPN以及NPN显然会成为最理所当然的选择。

ALPN和NPN的主要区别在于:谁来决定该次会话所使用的协议。在ALPN的描述中,是让客户端先发送一个协议优先级列表给服务器,由服务器最终选择一个合适的。而NPN则正好相反,客户端有着最终的决定权。

为http://所准备的http2

正如我们之前所提到的,对于纯文本的HTTP1.1来说,协商http2的方法就是通过给服务器发送一个带升级头部的报文。如果 服务器支持http2,它将以“101Switching”作为回复的状态码,并从此开始在该连接上使用http2。也许你很容易就发现这样一 个升级的流程会需要消耗掉一整个的往返时延,但好处是http2连接相比HTTP1可以被更大限度地重用和保持。

虽然有些浏览器厂商的发言人宣称他们不会实现这样的http2会话方式,但IE团队已公开表示他们会实现,与此同时,curl也 已经支持了这种方式。

http2协议

背景介绍就到此为止了,历史的脚步已经将我们推到了今天。现在让我们深入看看该协议的规范,看看那些细节和概念。

二进制

http2是一个二进制协议。

仔细想想,如果你是一个曾经跟互联网协议打过交道,那你很可能会本能反对二进制协议,你甚至准备好了一大堆理由来证明基于文本/ascii的协议是多么的有用,正如你曾无数次地通过telnet等应用手工地输入HTTP来发起请求。

基于二进制的http2可以使成帧的使用变得更为便捷。在HTTP1.1和其他基于文本的协议中,对帧的起始和结束识别起来相当 复杂。而通过移除掉可选的空白符以及其他冗余后,再来实现这些会变得更容易。

而另一方面,这项决议同样使得我们可以更加便捷的从帧结构中分离出那部分协议本身的内容。而在HTTP1中,各个部分相互交织,犹如一团乱麻。

事实上,由于协议提供了压缩这一特性,而其经常运行在TLS之上的事实又再次降低了基于纯文本实现的价值,反正也没办法直接从数据流上看到文本。因此通常情况下,我们必须习惯使用类似Wireshark这样的工具对http2的协议层一探究竟。

我们可以使用curl这样的工具来调试协议,而如果要进一步地分析网络数据流则需要诸如Wireshark这样的http2解析器。

二进制格式

http2会发送有着不同类型的二进制帧,但他们都有如下的公共字段:

Type,Length,Flags,SteamIdentifier和framepayload

规范中一共定义了10种不同的帧,其中最基础的两种分别对应于HTTP1.1的DATA和HEADERS。之后我会更详细的介绍它 们其中的一部分。

多路复用的流

上一节提到的Stream Identifier定义了二进制帧的格式,http2连接上传输的每个帧都关联到一个“流”。流是一个逻辑上的联合,一个独立的,双向的帧序列可以通过一个http2的连接在服务端与客户端之间不断的交换数据。

每个单独的http2连接都可以包含多个并发的流,这些流中交错的包含着来自两端的帧。流既可以被客户端/服务器端单方面的建立和使用,也可以被双方共享,或者被任意一边关闭。在流里面,每一帧发送的顺序非常关键。接收方会按照收到帧的顺序来进行处理。

流的多路复用意味着在同一连接中来自各个流的数据包会被混合在一起。就好像两个(或者更多)独立的“数据列车”被拼凑 到了一辆列车上,但它们最终会在终点站被分开。下图就是两列“数据火车”的示例:

blob.png

它们就是这样通过多路复用的方式被组装到了同一列火车上:

blob.png

在http2里面,我们很容易可以看到10个甚至100个同时存在的流,而创建一个新的流的代价也非常低。

优先级和依赖性

每个流都包含一个优先级(也就是“权重”),它被用来告诉对端哪个流更重要。当资源有限的时候,服务器会根据优先级来 选择应该先发送哪些流。

借助于PRIORITY帧,客户端同样可以告知服务器当前的流依赖于其他哪个流。该功能让客户端能建立一个优先级“树”,所 有“子流”会依赖于“父流”的传输完成情况。

优先级和依赖关系可以在传输过程中被动态的改变。这样当用户滚动一个全是图片的页面的时候,浏览器就能够指定哪个图 片拥有更高的优先级。或者是在你切换标签页的时候,浏览器可以提升新切换到页面所包含流的优先级。

头压缩

HTTP是一种无状态的协议。简而言之,这意味着每个请求必须要携带服务器需要的所有细节,而不是让服务器保存住之前 请求的元数据。因为http2并没有改变这个范式,所以它也需要这样(携带所有细节)。

这也保证了HTTP可重复性。当一个客户端从同一服务器请求了大量资源(例如页面的图片)的时候,所有这些请求看起来 几乎都是一致的,而这些大量一致的东西则正好值得被压缩。

当每个页面资源的个数上升的时候,cookies和请求的大小都会增加,而每个请求都会包含的cookie几乎是一模一样的。

HTTP1.1请求的大小正变得越来越大,有时甚至会大于TCP窗口的初始大小,这会严重拖累发送请求的速度。因为它们需要 等待带着ACK的响应回来以后,才能继续被发送。这也是另一个需要压缩的理由。

压缩是非常棘手的课题 HTTPS和SPDY的压缩机制被发现有受BREACH和CRIME攻击的隐患。通过向流中注入一些已知的文本来观察输出的变化, 攻击者可以推导出原始发送的数据。

为协议的动态内容进行压缩并使其免于被攻击,需要仔细且全面的考虑,而这也正是HTTPbis小组尝试去做的。

HPACK,HTTP/2头部压缩,顾名思义它是一个专为http2头部设计的压缩格式。确切的讲,它甚至被制定写入在另外一个单 独的草案里。新的格式同时引入了一些其他对策让破解压缩变得困难,例如采用帧的可选填充和用一个bit作为标记,来让中 间人不压缩指定的头部。

用Roberto Peon(HPACK的设计者之一)的话说

“HPACK旨在提供一个一致性的实现使信息量的损失尽可能少,使编解码快速而方便,使接收方能控制压缩文本的大 小,允许代理重新建立索引(如,通过代理在前后端共享状态),以及对哈夫曼编码串的更快速比较”

重置 - 后悔药

HTTP1.1的有一个缺点是:当一个含有确切值的Content-Length的HTTP消息被送出之后,你就很难中断它了。当然,通常你可以断开整个TCP链接(但也不总是可以这样),但这样导致的代价就是需要通过三次握手来重新建立一个新的TCP连 接。

一个更好的方案是只终止当前传输的消息并重新发送一个新的。在http2里面,我们可以通过发送RST_STREAM帧来实现这种需求,从而避免浪费带宽和中断已有的连接。

服务器推送

这个功能通常被称作“缓存推送”。主要的思想是:当一个客户端请求资源X,而服务器知道它很可能也需要资源Z的情况下,服务器可以在客户端发送请求前,主动将资源Z推送给客户端。这个功能帮助客户端将Z放进缓存以备将来之需。

服务器推送需要客户端显式的允许服务器提供该功能。但即使如此,客户端依然能自主选择是否需要中断该推送的流。如果不需要的话,客户端可以通过发送一个RST_STREAM帧来中止。

流量控制

http2上面每个流都拥有自己的公示的流量窗口,它可以限制另一端发送数据。如果你正好知道SSH的工作原理的话,这两者非常相似。

对于每个流来说,两端都必须告诉对方自己还有更多的空间来接受新的数据,而在该窗口被扩大前,另一端只被允许发送这么多数据。

而只有数据帧会受到流量控制。


文章主体内容来自:

https://www.gitbook.com/book/bagder/http2-explained/

possitive(0) negative(1) views2810 comments0

发送私信

最新评论

请先 登录 再评论.
相关文章