[译] 关于 HTTP/3 的一些心得

关于 HTTP/3 的一些心得

HTTP/3 即将成为之后的标准。做为协议的老用户,我以为本身应该写点什么。html

Google (pbuh) 拥有最受欢迎的浏览器(Chrome)和两个最受欢迎的网站(#1 Google.com #2 Youtube.com)。所以,谷歌手握将来 Web 协议的开发权。他们把第一次升级的协议称为 SPDY(发音同 "speedy"),也就是以后被标准化的 HTTP 的第二个版本,即 HTTP/2。他们将第二次升级的协议称为 QUIC(发音同 "quick"),也就是即将被标准化的 HTTP/3。前端

目前主流的 Web 浏览器(chrome、Firefox、Edge、safari)和主流的 Web 服务器(Apache、Nginx、IIS、CloudFlare)都已经对 SPDY (HTTP/2) 进行了支持。许多受欢迎的网站也对其进行了支持(甚至是非谷歌站点),尽管你不太可能在网上看到它(用 wireshark 或者 tcpdump 进行检测),由于它一直是用 SSL 进行加密的。尽管该标准容许 HTTP/2 在 TCP 上运行,但实际上全部的应用都是 SSL 进行的。android

这里有一个关于标准的知识点。在互联网范围之外,标准一般都是依附于法律之上,由政府进行管理。全部的主要利益相关人员在一个会议室进行制定,而后用法律强迫人们承认并使用它。然而在互联网上则有所不一样,人们首先会实现标准,而后由用户的承认度决定是否开始使用。标准一般都是事实,RFC 文档是为了在互联网上已经正常工做的内容所编写的,用于记录人们已经在使用的内容。SPDY 被浏览器/服务器采用的缘由不只仅是由于它被标准化,还由于互联网的主要厂商都开始添加对它的支持。QUIC 也是如此:它正在被标准化为 HTTP/3 则反映了它正在被使用,这不只仅是一个里程碑,由于大多数人都已经开始在实践中开始应用这一协议。ios

QUIC 实际上更像是 TCP(TCP/2)的新版本,而不是 HTTP(HTTP/3)的新版本。由于它并无真正改变 HTTP/2 所作的事情,而是改变了传输所作的工做方式。所以,我下面的评论重点是传输问题,而不是 HTTP 问题。git

主要的重点特性是更快的链接设置和更短的延迟。TCP 须要在链接创建以前来回发送多个数据包。SSL 会在加密创建之间请求来回发送多个数据包。若是有大量网络延迟,好比人们使用半秒每次的卫星互联网 ping 通讯,它可能须要很长的时间来创建一个链接。经过减小往返次数,链接能够更快地创建,所以当你点击一个连接时,连接资源就会马上响应并反馈。github

下一个重点特性是带宽。因为带宽限制这样的存在,在网络通讯中双方若是处于带宽限制不对等的状况下,数据包接收慢的一方会由于来不及接收而产生丢包,致使发送方数据重发从而耗费了更多的网络资源,因此只有在双方带宽对等时才能到达网络最大利用率。web

为何传统的 HTTP 在这方面会表现得如此差强人意。与网站的交互须要同时传输多种内容,而 HTTP 使用单独的 TCP 没法达到这一目的,所以浏览器会和 Web 服务器进行多个链接 (通常为 6)。可这又破坏了对带宽的估计,所以每一个 TCP 链接都试图独立地执行,就好像其余链接不存在同样。SPDY 经过多路复用的特性解决了这一问题,它将浏览器/服务器之间的多个交互与单个带宽计算结合在一块儿。chrome

QUIC 扩展了这种多路复用方式,促使在浏览器/服务器之间的多个交互处理变得更加简单,这不会致使交互之间彼此阻塞,这须要通用的带宽估算。从用户的角度来看,这将使交互更加顺畅,同时减小路由拥塞的用户体验。编程

如今咱们讨论一下 user-mode stacks。这是来自于 TCP 的问题,尤为是在服务器上的表现,TCP 链接由操做系统内核处理,而服务自己运行在用户模式。跨内核/用户模式边界操做资源会致使性能问题。追踪大量 TCP 链接会致使可伸缩性问题。有些人尝试将服务器放入内核来避免过分使用的状况,这显然不是好主意。由于它会破坏操做系统的稳定性。个人解决方案是使用 BlackICE IPS 和 masscan,这是一个用户模式驱动的硬件。从网络芯片中直接获取数据包到用户模式进程,绕过内核 (参阅 PoC||GTFO #15),使用自定义的 TCP 堆栈。这几年在 DPDK kit 中开始流行起来。后端

但在没有用户模式驱动的状况下,将 TCP 迁移到 UDP 也会带来相同的性能提高。与调用熟悉的 recv() 函数一次接收单个数据包不一样,你能够调用 recvmmsg() 来同时接收一组 UDP 数据包。它仍然是一个内核/用户模式转换的状况,但在同时收到一百个包进行分摊比对每一个包进行转换要好得多。

在个人测试中,使用典型的 recv() 函数限制每秒为 5 十万个 UDP 数据包,但使用 recvmmsg() 和其余一些优化手段(使用 RSS 的多核),在低端四核服务器上每秒能够获取 5 百万个 UDP 数据包。这是因为每一个核都带有良好的可伸缩性,移动到有 64 个核的强大服务器应该会进一步改善结果。

顺便说一句,“RSS” 是网络硬件的一个特性,它将传入的数据包分红多个接收队列。多核可伸缩性的最大问题是当两个 CPU 内核须要同时读取/修改同一件事时,共享相同的 UDP 数据包队列会变成最大的瓶颈。所以,Intel 首先和其余以太网供应商增长了 RSS,使每一个核都有本身的非共享数据包队列。Linux 和其余操做系统升级了 UDP,以支持单个套接字(SO_REUSEPORT)的多个文件描述符来处理多个队列。如今,QUIC 利用这些进步,容许每一个核管理本身的 UDP 数据包流,而不存在与其余 CPU 核共享东西的可伸缩性问题。我之因此说起,是由于我曾在 2000 年时,与 Intel 硬件工程师讨论过创建多个分组队列的问题。这是一个常见的问题,也是一个显而易见的解决方案,在过去的二十年里,看到它的进展一直颇有趣,知道它以 HTTP/3 的形式出如今顶层。若是没有网络硬件中的 RSS,QUIC 也不太可能会成为标准。

QUIC 另外一个很酷的解决方案是移动支持。你的电脑或者手机会随着你移动到不一样的 WIFI 网络环境而改变自身的 IP 地址。操做系统和协议没有优雅地关闭旧链接并打开新的链接。而后在 QUIC 协议下,链接的标识符再也不是“套接字”(源/目的端口/地址协议组合)的传统概念,而是分配给链接的 64 位标识符。

这意味着,当你移动时,你能够在 IP 地址不断变化的状况下,持续地从 YouTube 获取一个数据流,或者继续进行视频电话呼叫,而不会被中断。几十年来,互联网工程师一直在与“移动 IP”做斗争,试图想出一个可行的解决方案。他们关注的是端到端的原则,即在你移动的时候,以某种方式保持一个固定的 IP 地址,但这不是一个实际的解决方案。很荣幸能够看到在现实世界中有 QUIC/HTTP/3 这样一个可行的方案,最终解决了这个问题。

如何使用这些新的传输工具?几十年来,网络编程的标准一直是传输层 API,即“套接字”。也就是调用像 recv() 这样的函数来接收代码中的数据包。在 QUIC/HTTP/3,咱们再也不有操做系统传输层 API。取而代之的是一个更高层次的特性,你刻意在相似 GO 编程语言中使用它,或者在 OpenResty Nginx Web 服务器中使用 Lua。

我之因此会说起,是由于你在 OSI 模型的教育学习中错过了一件事,那就是它最初设想的是每一个人都会编写到应用层(7)API,而不是传输层(4)API。应该有像应用程序服务元素之类的能以标准方式为不一样的应用程序处理像文件传输和消息传递之类的操做。尤为是包括 Go、QUIC、protobufs 等在 Google 的驱动下,人们会愈来愈倾向于这种模式。

我之因此会提到这一点,是由于 Google 和 微软之间造成了鲜明的对比。微软拥有一个流行的操做系统,因此它的创新是有它在该操做系统内所能作的事情驱动的。Google 的创新是由它能在操做系统上安装的东西所驱动的。还有 Facebook 和 Amazon 自己,它们必须在谷歌所提供给他们的堆栈之上(或者外部)进行创新。世界上排名前五位的公司依次是 Apple、Google、Microsoft、Amazon、Facebook,所以每一个公司推进创新的位置都很重要。

结论

当 TCP 在 20 世纪 70 年代被建立时,它是伟大的。它所处理的操做,诸如拥塞,远远好过相互竞争的协议。尽管人们声称 IPv4 没有预料到超过 40 亿个地址的事情,但它比 70 年代和 80 年代的竞争设计要好得多。IPv4 升级到 IPv6 很大程度上维持了 IP 的发展。相似的,TCP 升级到 QUIC 也延续了 TCP 的发展,不一样的是在于它是为了知足现代需求而进行的扩展。而使人惊讶的,TCP 居然能够在一直未升级的状态下维持了这么长时间还仍在被使用着。

若是发现译文存在错误或其余须要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可得到相应奖励积分。文章开头的 本文永久连接 即为本文在 GitHub 上的 MarkDown 连接。


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOS前端后端区块链产品设计人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划官方微博知乎专栏