HTTP/2 常见问题回答

jopen 9年前

常见问题

为什么修订HTTP?

HTTP/1.1已经很好的服务Web超过15个年头,但它的劣势开始显现。

载入一个Web页面比之前占用更多的资源(详情可见HTTP压缩页大小统计),有效的载入这些资源很难,因为HTTP实际上对每个TCP连接,只允许一个优先的请求。

在过去,对于并发请求,浏览器使用多个TCP连接。然而,这也是有局限的;如果使用了过多的连接,这既是一种计数上的高产(TCP阻塞控制是被有效否定的,导致阻塞事件影响性能和网络),也基本上是不公平的(因为浏览器承受的资源大于它们享有的网络资源)。

同时,大量的请求意味着“线上”有很多重复的数据。

HTTP/1.1耗费大量的开销是与这两个因素有关。如果发生过多的请求,会影响性能。

这些导致了相关行业成为了像雪碧、数据内联、域共享和级联的最佳练习场地。这些问题被认为是底层协议自身的问题,并导致在使用它们时产生了一系列的问题。

谁正在做这件事?

IETFHTTPbis工作组正在开发HTTP/2,他们负责维护HTTP协议。是由若干HTTP实现者、用户、网络运营商和HTTP专家组成。

注意虽然工作组的邮件列表是托管在W3C网站上,不过却不是W3C的功劳。但是, Tim Berners-Lee 和 W3C TAG与WG的进程保持了一致。

大量的人对相关工作作出了共享,不过大部分活跃参与者是来自像Firefox、Chrome、推ter、Microsoft的HTTP栈、Curl和Akamai这样大项目的工程师,以及若干Python、Ruby和NodeJS的HTTP实现者。

要了解更多IETF参与者,请看IETF之道。你也可以在Github的贡献者图表中了解到哪些人正在对规范做贡献,在我们的实现者列表了解哪些人正在参与实施。

和SPDY是怎样的关系?

HTTP/2 第一次出现并被讨论的时候, SPDY 正得到厂商 (像 Mozilla 和 nginx)的青睐和支持, 并被看成是HTTP/1.x 基础上的重大改善.

经过建议和投票之后, SPDY/2 被列为HTTP/2 的基础.从那时起, 根据工作组的讨论和厂商的反馈,它已经有了很多变化。

在整个过程中,SPDY 的核心开发成员都参与了HTTP/2 的发展, 其中也包括 Mike Belshe 和 Roberto Peon. 事实上,已经发布的 SPDY/4 revision 正是基于 HTTP/2的 ,因为SPDY社区现在发现它是作为一种进一步实验反馈到 HTTP/x的工具, 而不是竞争对手.

是 HTTP/2.0 还是 HTTP/2?

工作组决定去掉小版本 (“.0”) 因为这在HTTP/1.x中导致了很多困惑.

也就是说, HTTP 的版本仅代表它的兼容性,不表示它的特性和 “亮点”

和HTTP/1.x的关键区别是什么?

在高版本 HTTP/2中:

  • 是二进制的,代替原有的文本

  • 是多路复用的, 代替原来的序列和阻塞机制

  • 所以可以在一个连接中并行处理

  • 压缩头部信息减小开销

  • 允许服务器主动推送应答到客户端的缓存中

HTTP/2为什么是二进制的?

比起像HTTP/1.x这样的文本协议,二进制协议解析起来更高效、“线上”更紧凑,更重要的是错误更少。因为它们对如空白字符的处理、大小写、行尾、空链接等的处理很有帮助。

例如,HTTP/1.1定义了四个不同的方法来解析一条消息;在HTTP/2中,仅需一个代码路径即可。

HTTP/2在telnet中将不可用,但是我们有一些工作提供支持,比如Wireshark plugin

为什么 HTTP/2 需要多路传输?

HTTP/1.x 有个问题叫线端阻塞(head-of-line blocking),  它是指一个连接(connection)一次只提交一个请求的效率比较高, 多了就会变慢.

HTTP/1.1 试过用流水线(pipelining)来解决这个问题, 但是效果并不理想(数据量较大或者速度较慢的响应, 会阻碍排在他后面的请求). 此外, 由于网络媒介(intermediary )和服务器不能很好的支持流水线, 导致部署起来困难重重. 

客户端使用一些启发式的方法(基本靠猜) 来决定通过哪些连接提交哪些请求; 由于一个页面加载的数据量, 往往比可用连接能处理的数据量的10倍还多, 对性能产生极大的负面影响, 结果经常引起瀑布式阻塞(waterfall of blocked requests).

而多路传输(Multiplexing)能很好的解决这些问题, 因为它能同时处理多个消息的请求和响应; 甚至可以在传输过程中将一个消息跟另外一个掺杂在一起.

所以客户端只需要一个连接就能加载一个页面

为什么只要一个 TCP 连接?

目前的浏览器, 每个点 (origin) 打开 4 到 8 个连接(Connection). 而很多网站都支持多点传输(multiple origins), 也就是说, 光加载一个网页, 打开的连接数量就超过 30 个. 

一个应用同时打开这么多连接, 已经远远超出了当初设计 TCP 时的预期; 每一个连接接收过多的数据, 又存在网络缓存溢出的风险, 结果导致网络堵塞和数据重传.

此外, 使用这么多连接还会强占许多网络资源. 这些资源都是从别人那 “偷” 来的. 你说这些应用够遵纪守法吧? ( VoIP 就是个很好的例子).

服务器推送的好处是什么?

当浏览器请求一个网页时,服务器将会发回HTML,在服务器开始发送JavaScript、图片和CSS前,服务器需要等待浏览器解析HTML和发送所有内嵌资源的请求。

服务器推送服务通过“推送”那些它认为客户端将会需要的内容到客户端的缓存中,以此来避免往返的延迟。

消息头为什么需要压缩?

来自Mozilla的Patrick McManus通过计算消息头对平均页面负载的印象,对此进行了形象且充分的说明.

假定一个页面有80个资源需要加载(这个数量对于今天的Web而言还是挺保守的), 而每一次请求都有1400字节的消息头(着同样也并不少见,因为Cookie和引用等东西的存在), 至少要7到8个来回去“在线”获得这些消息头。这还不包括响应时间——那只是从客户端那里获取到它们所花的时间而已.

这全都由于TCP的慢启动机制,它会基于对已知有多少个包,来确定还要来回去获取哪些包 – 这很明显的限制了最初的几个来回可以发送的数据包的数量.

相比之下,即使是头部轻微的压缩也可以是让那些请求只需一个来回就能搞定——有时候甚至一个包就可以了。

这种开销是可以被节省下来的,特别是当你考虑移动客户端应用的时候,即使是良好条件下,一般也会看到几百毫秒的来回延迟。

为什么是 HPACK?

SPDY/2 提出在每一方都使用一个单独的GZIP上下文用于消息头压缩,者实现起来很容易,也很高效、.

从那时候开始,就有了一个被证明能针对算法中使用流压缩(如GZIP)的重要攻击方式 CRIME.

CRIME 让那些能向加密流中诸如数据的攻击者获得了“探测”原文并进行还原的可能性。因为是Web,JavaScript使其成为了可能,而且已经有了使用针对受到TLS保护的HTTP资源的CRIME恢复cookie和认证令牌信息的证明.

因此,我们不应该使用GZIP。由于找不到其它适合使用在这种用例下的安全有效的算法,我们创造了一种新的,特别针对消息头的压缩方案,它能进行粗粒度的操作;因为HTTP消息头并不常常需要改变,我们仍然可以得到很好的压缩效率,而且更加的安全.

HTTP/2 能让cookie(以及其他的消息头)更好么?

这一尝试被许可在网络协议的一个修订版本上运行 – 例如,HTTP消息头、方法等等如何才能在不改变HTTP语义的前提下放到"网络上“.

这事因为HTTP的使用是如此广泛。如果我们使用了这个版本的HTTP,引入了一种新的状态机制(例如之前讨论过的例子)或者改变的核心方法(幸好,这还没有发生过), 这可能就意味着新的协议将不会同现有的Web兼容.

具体地,我们是想要能够从HTTP/1转移到HTTP/2,并且不会有信息的丢失. 如果我们开始”清理”消息头(大多数人会认同,HTTP消息头现在简直是一团糟), 我们就不得不要去面对现有Web的诸多问题.

那样做只会为采用新协议的过程中制造麻烦.

总而言之,工作组会对所有的HTTP负责,而不仅仅只是HTTP/2. 因此,我们才可以在版本独立的新机制下运作,同时它们也能向后同现有的网络兼容.

非浏览器用户的HTTP会是什么情况呢?

人们期望非浏览器应用程序也能够用上HTTP/2,如果它们曾今使用过HTTP的话。

早起收到针对HTTP的“API”,HTTP/2具有性能好等特点这样的反馈,是因为API的设计中不需要考虑像请求开销这样一些事情。

之前说过,我们所要考虑的主要的提升重点是在典型的浏览器用例下, 因为这是协议主要的使用场景。

我们的章程里面是这样说的:

正在组织的规范能满足现在已经普遍部署了的HTTP的功能要求;   具体的,(桌面端和移动端的)Web浏览,(“HTTP API”形式的)非浏览器, (大范围的)Web服务,  还有各种(借助代理,企业防火墙,反向代理以及内容分发网络实现的)中介。  同样的,对HTTP/1.x当前和未来的语义扩展 (例如,消息头,方法,状态码,缓存指令) 都应该在新的协议中支持。    注意,这里没有涵盖将HTTP用于非特定行为所依赖的(例如超时,连接状态以及拦截代理)场景中;  这些使用可能不会被最终的产品启用。

HTTP/2 需要加密吗?

不需要。在广泛的讨论后,工作组没有就新协议是否使用加密(如TLS)而达成共识。

不过,有些实现者说,只有HTTP/2使用加密链接他们才提供支持。

HTTP/2为提高安全性做了什么?

目前,HTTP/2定义了TLS的轮廓,包括版本、密码套件和用到的扩展。

细节参见相关规范

也讨论了额外的机制,如对HTTP://URLs(所谓的“机会主义加密”)使用TLS;参见 issue #315

现在可以用HTTP/2吗?

HTTP/2暂时在主流浏览器中还不可用,不过还是有一些体验版的可以用,或许在“每夜(nightly)”频道可以找到。

还是有几个服务器可用的(包括 Akamai 和 推ter主流网站提供了测试服务器),以及几个开源版的,你可以用来部署和测试。

HTTP/2会代替HTTP/1.x

工作组的目的是让那些使用HTTP/1.x也可以使用HTTP/2,并能感受到HTTP/2所带来的好处。工作组说过,由于人们部署代理和服务器的方式的原因,我们不能强迫整个世界进行迁移,所以HTTP/1.x很可能仍要使用了一段时间。

HTTP/3 会出现么?

如果通过HTTP/2引入的沟通协作机制运行良好, 那就有可能比过去更加容易的支持新版本的HTTP.

实现中的问题

为什么规则会围绕消息头帧的数据接续?

数据接续的存在是由于一个值(例如cookie)可以超过 16kb, 这意味着它不可能全部装进一个帧里面. 所以就决定以最不容易出错的方式让所有的消息头数据以一个接一个帧的方式传递, 这样就使得对消息头的解码和缓冲区的管理更加的容易.

HPACK状态的最小和最大尺寸是多少 ?

接收一方总是会控制HPACK中内存的使用量, 并且最小能设置到0,最大则要看SETTING帧中能表示的最大整型数是多少,目前是 2^32 - 1.

我怎样才能避免保持 HPACK 状态?

发送一个 SETTINGS 帧将状态尺寸 (SETTINGS_HEADER_TABLE_SIZE) 设置到0, 然后 RST 所有的流,知道一个带有ACT设置位的 SETTINGS 帧发送了过来.

为什么会有一个单独的压缩/流控制上下文?

简洁.

原来的提案里面流分组这个概念,它可以共享上下文,流控制等等. 那样有利于代理 (也有利于经过它们的用户的体验), 而这样做相应也会增加一点复杂度. 所以我们就决定先以一个简单的东西开头,看看它会有多糟糕的问题,并且在未来的协议版本中解决这些问题(如果有的话).

在HPACK中为什么会有EOS符号?

由于CPU效率和安全的原因,HPACK的霍夫曼编码填充了霍夫曼编码字符串的下一个字节边界。因此对于任何特定的字符串可能需要0-7个比特的填充。

如果单独考虑霍夫曼解码,任何比所需要的填充长的符号都可以正常工作。但是,HPACK的设计允许按字节对比霍夫曼编码的字符串。通过填充EOS符 号需要的比特,我们确保用户在做霍夫曼编码字符串字节级比较时是相等的。反之,许多 headers 可以在不需要霍夫曼解码的情况下被解析。

实现 HTTP/2 的时候我可以不用去实现 HTTP/1.1 么?

可以,完全可以.

对于运行在 TLS (h2) 之上的 HTTP/2 而言, 如果你没有实现 http1.1 的 ALPN 标识, 那你就就准备不需要支持任何 HTTP/1.1 特性的.

对于运行在 TCP (h2c) 之上的HTTP/2 而言, 你需要实现最原始的升级(Upgrade)请求.

只支持h2c的客户将需要生成一个请求 OPTIONS 的请求,因为 “*” 或者一个针对“/”的 HEAD 请求已经相当安全,并且也很容易构建.  寻求实现 HTTP/2 的客户将只需要把没有带上101状态码的HTTP/1.1响应看做一个错误就行了.

只支持h2c的服务器可以使用一个固定的101响应来接收一个包含升级(Upgrade)消息头字段的请求 . 没有h2c的Upgrade令牌的请求可以使用一个包含了Upgrade消息头字段的505(HTTP版本不支持)状态码来拒绝.  那些不希望处理 HTTP/1.1 响应的服务器应该在发送了带有鼓励用户在升级了的HTTP/2连接上重试的连接序言之后立即用带有 REFUSED_STREAM 错误码拒绝该请求的第一份数据流.

部署的问题

怎么调试加密过的 HTTP/2 ?

存取应用程序数据的方法很多, 最简单的方法是使用 NSS keylogging 加 Wireshark 插件 (包含在最新开发版中). 这种方式对 Firefox 和 Chrome 都适用.