- 原文地址:HTTP/2 Frequently Asked Questions
- 原文做者:HTTP/2
- 译文出自:掘金翻译计划
- 本文永久连接:github.com/xitu/gold-m…
- 译者:YueYong
- 校对者:Ranjay, ziyin feng
如下是有关 HTTP/2 的常见问题解答。php
HTTP/1.1 已经在 Web 上服役了十五年以上,但其劣势也开始显现。html
加载一个网页比以往更加耗费资源(详见 HTTP Archive’s page size statistics)。与此同时,有效地加载全部这些静态资源变得很是困难,由于事实上,HTTP 只容许每一个 TCP 链接有一个未完成的请求。前端
在过去,浏览器使用多个 TCP 链接来发出并行请求。然而这种作法是有限制的。若是使用了太多的链接,就会产生相反的效果(TCP 拥塞控制将被无效化,致使的用塞事件将会损害性能和网络)。并且从根本上讲这对其余程序来讲也是不公平的(由于浏览器会占用许多本不应属于他的资源)。node
同时,大量的请求意味着“在线上”有大量重复的数据。android
这两个因素都意味着 HTTP/1.1 请求有不少与之相关的开销;若是请求太多,则会影响性能。ios
这使得业界在有哪些是最好的实践上达成共识,它们包括,好比,Spriting(图片合并)、data: inlining(数据内嵌)、Domain Sharding(域名分片)和 Concatenation(文件合并)等。这些不规范的解决方案说明了协议自己存在一些潜在问题,而且在使用的时候会出现不少问题。nginx
HTTP/2 是由 IETF 的 HTTP 工做组开发的,该组织负责维护 HTTP 协议。该组织由众多 HTTP 实现者、用户、网络运营商和 HTTP 专家组成。git
值得注意的是,虽然工做组的邮件列表托管在 W3C 网站上,不过这并非 W3C 的功劳。可是, Tim Berners-Lee 和 W3C TAG与 WG 的进度保持一致。github
许多人为这项工做作出了本身的贡献,尤为是一些来自“大”项目的工程师,例如 Firefox、Chrome、Twitter、Microsoft 的 HTTP stack、Curl 和 Akamai。以及若干 Python、Ruby 和 NodeJS 的 HTTP 实现者。算法
为了更好的了解有关 IETF 的信息,你能够访问 Tao of the IETF;你也能够在 Github 的贡献者图表上查看有哪些人为该项目作出了贡献,一样的,你也能够在 implementation list 上查看谁正在参与该项目。
HTTP/2 第一次出现并被讨论的时候,SPDY 正获得厂商 (像 Mozilla 和 nginx)的青睐和支持,并被当作是 HTTP/1.x 基础上的重大改善。
在不断的征求建议以及投票选择以后,SPDY/2 被选为 HTTP/2 的基础。从那时起,根据工做组的讨论和用户的反馈,它已经有了不少变化。
在整个过程当中,SPDY 的核心开发人员参与了 HTTP/2 的开发,其中包括 Mike Belshe 和 Roberto Peon。
2015 年 2 月,谷歌宣布计划取消对 SPDY 的支持,转而支持 HTTP/2。
工做组决定删除次要版本(“.0”),由于它在 HTTP/1.x 中形成了不少混乱。也就是说,HTTP 的版本仅表明它的兼容性,不表示它的特性和“亮点”。
在高版本 HTTP/2 中:
和 HTTP/1.x 这样的文本协议相比,二进制协议解析起来更高效、“线上”更紧凑,更重要的是错误更少。由于它们对如空白字符的处理、大小写、行尾、空连接等的处理颇有帮助。
举个栗子 🌰,HTTP/1.1 定义了四种不一样的方法来解析一条消息;而在HTTP/2中,仅需一个代码路径便可。
HTTP/2 在 telnet 中不可用,可是咱们已经有一些工具能够提供支持,例如 Wireshark plugin。
HTTP/1.x 有个问题叫“队头阻塞(head-of-line blocking)”,它是指在一次链接(connection)中,只提交一个请求的效率比较高,多了就会变慢。
HTTP/1.1 尝试使用管线化(pipelining)来解决这个问题,可是效果并不理想(对于数据量较大或者速度较慢的响应,依旧会阻碍排在他后面的请求)。此外,因为许多网络媒介(intermediary)和服务器不能很好的支持管线化,致使其部署起来也是困难重重。
这也就迫使客户端使用一些启发式的方法(基本靠猜)来决定经过哪些链接提交哪些请求;因为一个页面加载的数据量,每每比可用链接能处理的数据量的 10 倍还多,对性能产生极大的负面影响,结果常常引发瀑布式阻塞(waterfall of blocked requests)。
而多路传输(Multiplexing)能很好的解决这些问题,由于它能同时处理多个消息的请求和响应;甚至能够在传输过程当中将一个消息跟另一个掺杂在一块儿。
因此在这种状况下,客户端只须要一个链接就能加载一个页面。
若是使用 HTTP/1,浏览器打开每一个点(origin)就须要 4 到 8 个链接(Connection)。而如今不少网站都使用多点传输(multiple origins),也就是说,光加载一个网页,打开的链接数量就超过 30 个。
一个应用同时打开这么多链接,已经远远超出了当初设计 TCP 时的预想;同时,由于每一个链接都会响应大量的数据,使其能够形成网络缓存溢出的风险,结果可能致使网络堵塞和数据重传。
此外,使用这么多链接还会强占许多网络资源。这些资源都是从那些“遵纪守法”的应用那“偷”的(VoIP 就是个很好的例子)。
当浏览器请求页面时,服务器发送 HTML 做为响应,而后须要等待浏览器解析 HTML 并发出对全部嵌入资源的请求,而后才能开始发送 JavaScript,图像和 CSS。
服务器推送服务经过“推送”那些它认为客户端将会须要的内容到客户端的缓存中,以此来避免往返的延迟。
可是,推送的响应并非“万金油”,若是使用不当,可能会损害性能。正确使用服务器推送是一个长期的实验及研究领域。
来自 Mozilla 的 Patrick McManus 经过计算消息头对平均页面负载的印象,对此进行了形象且充分的说明。
假定一个页面有 80 个资源须要加载(这个数量对于今天的 Web 而言仍是挺保守的),而每一次请求都有 1400 字节的消息头(这一样也并很多见,由于 Cookie 和引用等东西的存在),至少要 7 到 8 个来回去“在线”得到这些消息头。这还不包括响应时间——那只是从客户端那里获取到它们所花的时间而已。
这全都因为 TCP 的慢启动机制,它根据能够确认的数据包数量对新链接上发送数据的进行限制 — 这有效地限制了最初的几回来回能够发送的数据包数量。
相比之下,即便是头部轻微的压缩也能够是让那些请求只需一个来回就能搞定——有时候甚至一个包就能够了。
这些额外的开销是至关多的,特别是当你考虑对移动客户端的影响的时候。这些往返的延迟,即便在网络情况良好的状况下,也高达数百毫秒。
SPDY/2 提出在每一方都使用一个单独的 GZIP 上下文用于消息头压缩,这实现起来很容易,也很高效。
从那时起,一个重要的攻击方式 CRIME 诞生了,这种方式能够攻击加密文件内部的所使用的压缩流(如GZIP)。
使用 CRIME,那些具有向加密数据流中注入数据能力的攻击者得到了“探测”明文并还原的可能性。由于是 Web,JavaScript 使其成为了可能,并且已经有了经过对受到 TLS 保护的 HTTP 资源的使用CRIME来还原出 cookies 和认证令牌(Toekn)的案例。
所以,咱们不该该使用 GZIP 进行压缩。因为找不到其它适合在这种用例下使用的安全有效的算法,因此咱们创造了一种新的,针对消息头的,进行粗粒度操做的压缩模式;由于HTTP消息头并不经常须要改变,咱们仍然能够获得很好的压缩效率,并且更加的安全。
这一努力被许可在网络协议的一个修订版本上运行 – 例如,HTTP 消息头、方法等等如何才能在不改变 HTTP 语义的前提下放到“网络上”。
这是由于 HTTP 的应用很是普遍。若是咱们使用了这个版本的 HTTP,它就会引入一种新的状态机制(例如以前讨论过的例子)或者改变其核心方法(幸亏,这尚未发生过),这可能就意味着新的协议将不会兼容现有的 Web 内容。
具体地,咱们是想要可以从 HTTP/1 转移到 HTTP/2,而且不会有信息的丢失。若是咱们开始”清理”消息头(大多数人都认为如今的 HTTP 消息头简直是一团糟),咱们就不得不去面对现有 Web 的诸多问题。
这样作只会对新协议的普及形成麻烦。
总而言之,工做组 会对全部的 HTTP 负责,而不只仅只是 HTTP/2。所以,咱们才能够在版本独立的新机制下运做,只要它们也能同现有的网络向下兼容。
若是非浏览器应用已经使用过 HTTP 的话,那他们也应该可使用 HTTP/2。
先前收到过 HTTP “APIs” 在 HTTP/2 中具备良好性能等特色这样的反馈,那是由于 API 的设计不须要考虑相似请求开销这样一些事情。
话虽如此,咱们正在考虑的改进重点是典型的浏览用例,由于这是协议主要的使用场景。
咱们的章程里面是这样说的:
正在组织的规范须要知足如今已经广泛部署了的 HTTP 的功能要求;具体来讲主要包括,Web 浏览(桌面端和移动端),非浏览器(“HTTP APIs” 形式的),Web 服务(大范围的),还有各类网络中介(借助代理,企业防火墙,反向代理以及内容分发网络实现的)。一样的,对 HTTP/1.x 当前和将来的语义扩展 (例如,消息头,方法,状态码,缓存指令) 都应该在新的协议中支持。
值得注意的是,这里没有包括将 HTTP 用于非特定行为所依赖的场景中(例如超时,链接状态以及拦截代理)。这些可能并不会被最终的产品启用。
不须要。在激烈的讨论后,工做组没有就新协议是否使用加密(如 TLS)而达成共识。
不过,有些观点认为只有在加密链接上使用时才会支持 HTTP/2,而目前尚未浏览器支持未加密的 HTTP/2。
HTTP/2 定义了所需的 TLS 文档,包括版本,密码套件黑名单和使用的扩展。
细节详见相关规范。
还有对于一些额外机制的讨论,例如对 HTTP:// URLs(所谓的“机会主义加密”)使用 TLS;详见 RFC 8164。
浏览器中,最新版本的 Edge、Safari、Firefox 和 Chrome都支持 HTTP/2。其余基于 Blink 的浏览器也将支持HTTP/2(例如 Opera 和 Yandex 浏览器)。详见 caniuse。
还有几个可用的服务器(包括来自 Akamai,Google 和 Twitter 的主要站点的 beta 支持),以及许多能够部署和测试的开源实现。
有关详细信息,请参阅实现列表。
工做组的目的是让那些使用 HTTP/1.x 的人也可使用 HTTP/2,并能得到 HTTP/2 所带来的好处。他们说过,因为人们部署代理和服务器的方式不一样,咱们不能强迫整个世界进行迁移,因此 HTTP/1.x 仍有可能要使用了一段时间。
若是经过 HTTP/2 引入的沟通协做机制运行良好,支持新版本的 HTTP 就会比过去更加容易。
数据接续的存在是因为一个值(例如 cookie)能够超过 16kb,这意味着它不可能所有装进一个帧里面。
因此就决定以最不容易出错的方式让全部的消息头数据以一个接一个帧的方式传递,这样就使得对消息头的解码和缓冲区的管理变得更加容易。
接收一方老是会控制 HPACK 中内存的使用量, 而且最小能设置到 0,最大则要看 SETTING 帧中能表示的最大整型数是多少,目前是 2^32 - 1。
发送一个 SETTINGS 帧,将状态尺寸(SETTINGS_HEADER_TABLE_SIZE)设置到 0,而后 RST 全部的流,直到一个带有 ACT 设置位的 SETTINGS 帧被接收。
简单说一下。
原来的提案里面提到了流分组这个概念,它能够共享上下文,进行流控制等等。尽管那样有利于代理(也有利于用户体验),可是这样作相应也会增长一点复杂度。因此咱们就决定先以一个简单的东西开始,看看它会有多糟糕的问题,而且在将来的协议版本中解决这些问题(若是有的话)。
因为 CPU 效率和安全的缘由,HPACK 的霍夫曼编码填充了霍夫曼编码字符串的下一个字节边界。所以对于任何特定的字符串可能须要 0-7 个比特的填充。
若是单独考虑霍夫曼解码,任何比所须要的填充长的符号均可以正常工做。可是,HPACK 的设计容许按字节对比霍夫曼编码的字符串。经过填充 EOS 符号须要的比特,咱们确保用户在作霍夫曼编码字符串字节级比较时是相等的。反之,许多 headers 能够在不须要霍夫曼解码的状况下被解析。
一般/大部分时候能够。
对于运行在 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 消息头字段的 505(HTTP 版本不支持)状态码来拒绝。那些不但愿处理 HTTP/1.1 响应的服务器应该在发送了带有鼓励用户在升级了的 HTTP/2 链接上重试的链接序言以后当即用带有 REFUSED_STREAM 错误码拒绝该请求的第一份数据流.
不,那是正确的。流 B 的权重为 4,流 C 的权重为 12。为了肯定每一个流接收的可用资源的比例,将全部权重(16)相加并将每一个流权重除以总权重。所以,流 B 接收四分之一的可用资源,流 C 接收四分之三。所以,正如规范所述:流 B 理想地接收分配给流 C 的资源的三分之一。
是的,有可能。即便对于仅使用单个流下载大量数据的客户端,仍然须要一些数据包以相反的方向发回以实现最大传输速度。在没有设置 TCP_NODELAY(仍然容许 Nagle 算法)的状况下,能够传输的数据包将被延迟一段时间以容许它们与后续分组合并。
例如,若是这样一个数据包告诉对等端有更多可用的窗口来发送数据,那么将其发送延迟数毫秒(或更长时间)会对高速链接形成严重影响。
存取应用程序数据的方法不少,最简单的方法是使用 NSS keylogging 配上 Wireshark 插件(包含在最新开发版中)。这种方法对 Firefox 和 Chrome 都适用。
HTTP/2 服务器推送容许服务器向客户端提供内容而无需等待请求。这能够提升检索资源的时间,特别是对于具备大带宽延迟产品的链接,其中网络往返时间占了在资源上花费的大部分时间。
推送基于请求内容而变化的资源多是不明智的。目前,浏览器只会推送请求,若是他们不这样作,就会提出匹配的请求(详见 Section 4 of RFC 7234)。
有些缓存不考虑全部请求头字段的变化,即便它们列在 Vary
header 字段中。为了使推送资源被接收的可能性最大化,内容协商是最好的选择。基于 accept-encoding
报头字段的内容协商受到缓存的普遍尊重,可是其余报头字段可能不受支持。
若是发现译文存在错误或其余须要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可得到相应奖励积分。文章开头的 本文永久连接 即为本文在 GitHub 上的 MarkDown 连接。
掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 Android、iOS、前端、后端、区块链、产品、设计、人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划、官方微博、知乎专栏。