HTTP/3 协议即将标准化。作为一个老协议使用者,我想我该写一些看法了。
Google(pbuh) 公司拥有最流行的 web 浏览器(Chrome)和两个最流行的网站(#1 Google.com #2 Youtube.com)。因此谷歌可以控制 web 协议的发展。他们的第一次升级称之为 SPDY (发音"speedy"),这次更新最终成为 HTTP 协议第二版标准,即 HTTP/2 。他们的第二次升级称之为 QUIC(发音"quick"),将成为 HTTP/3 协议标准。
SPDY (HTTP/2)已经得到了主流web浏览器(Chrome、Firefox、Edge、Safari)和主流web服务器(Apache、Nginx、IIS、CloudFlare)的支持。许多最流行的网站都支持它(即使是非google站点),尽管您不太可能在网上看到它(使用Wireshark或tcpdump进行嗅探),因为它总是使用SSL加密的。虽然标准允许HTTP/2在TCP上运行raw,但是所有实现都是SSL上使用它。
这里有一个关于标准很好的一课。在互联网之外,标准通常是法律上的,由政府管理,由所有主要利益相关者在一个房间里讨论,然后使用规则迫使人们采用它。在互联网上,人们首先实现东西,然后如果其他人喜欢它,他们也会开始使用它。标准通常是事实上的,rfc是为已经在互联网上运行良好的内容,记录人们已经在使用的内容。浏览器/服务器采用SPDY并不是因为它是标准化的,而是因为主要的参与者只是简单地开始添加它。同样的情况也发生在QUIC上:它被标准化为HTTP/3的事实是它已经被使用,而不是人们可以开始使用它的标准化里程碑。
QUIC实际上更像是TCP (TCP/2???)的新版本,而不是HTTP (HTTP/3)的新版本。它并没有真正改变HTTP/2的功能,而是改变了传输的工作方式。因此,我下面的评论集中在传输问题上,而不是HTTP问题。
主要的标题特性是更快的连接设置和延迟。TCP要求在建立连接之前来回发送大量数据包。SSL同样需要在建立加密之前来回发送大量数据包。如果网络延时很大,比如人们使用半秒ping时间的卫星互联网,建立连接需要相当长的时间。通过减少往返,连接可以更快地建立,这样当您单击链接时,链接的资源就会立即弹出
下一个主要特性是带宽。网络连接的源和目的之间总是存在带宽限制,这几乎总是由于拥塞。双方都需要使用这个速度,以便他们能够以适当的速度发送数据包。如果发送数据包太快,那么它们就会被丢弃,这会在不提高传输速率的情况下给其他数据包造成更大的拥塞。发送数据包太慢意味着不能最优地使用网络。
HTTP 传统上这一点做得很糟糕。 使用单个 TCP 连接不适用于 HTTP,因为与网站的交互需要同时传输多个内容,因此浏览器打开了与 Web 服务器的多个连接(通常为 6 个)。但是,这会打破对带宽的估计,因为每个 TCP 连接都尝试独立完成,就像其他连接不存在一样。SPDY 通过其多路复用功能解决了这个问题,该功能将浏览器/服务器之间的多个交互与单个带宽计算相结合。
QUIC 扩展了这种多路复用,使得处理浏览器/服务器之间的多个交互变得更加容易,而没有任何一个交互阻止另一个交互,且具有共同带宽。 从用户的角度来看,这将使交互更加顺畅,同时减少路由器遇到的拥塞。
我们现在来谈谈用户模式栈。 特别是在服务器上,TCP连接由操作系统内核处理,而服务本身在用户模式中运行。跨内核/用户模式边界移动会导致性能降低。追踪大量TCP连接会导致扩展性问题。有些人尝试将服务放入内核来避免转换,这并不可取,因为它破坏了操作系统的稳定性。我的解决方案是用BlackICE IPS和masscan,使用自定义TCP栈,利用硬件的用户模式驱动程序,将数据包从网络芯片直接传送到用户模式进程,绕过内核(参见PoC || GTFO#15)。近年来,DPDK套件已经变得流行。
但是,从TCP迁移到UDP可以在没有用户模式驱动程序的情况下获得相同的性能。您可以调用recvmmsg()一次接收一堆UDP数据包,而不是调用众所周知的recv()函数来一次接收一个数据包。它仍然是内核/用户模式转换,但是一次性收到的一百个数据包分摊,而不是每个数据包的转换。
在我自己的测试中,使用典型的recv()函数限制为大约500,000 UDP数据包 /秒,但使用recvmmsg()和其他一些优化(使用RSS的多核),可以在低端四核服务器上获得5,000,000 UDP数据包/秒。由于每个核心的扩展性很好,因此迁移到具有64个核心的强大服务器可以进一步提高。
BTW,“RSS”是网络硬件的一个特点,它将传入的数据包分成多个接收队列。多核扩展性的最大问题是两个CPU核心需要同时读取/修改同一个东西,因此共享相同的UDP队列成为最大的瓶颈。因此,首先英特尔和其他以太网供应商添加了RSS,为每个核心提供了自己的非共享数据包队列。 Linux和其他操作系统升级UDP以支持单个套接字(SO_REUSEPORT)的多个文件描述符来处理多个队列。现在,QUIC使用这些改进使得每个核心管理自己的UDP数据包流,不会有与其他CPU核心共享内容的导致可扩展性问题。之所以提到这一点,是因为在2000年,我亲自与英特尔硬件工程师讨论过有多个数据包队列问题。这个问题很普遍,也有对应的解决方案,在HTTP / 3出现之前,看看在过去二十年中的发展也是很有意思。如果没有网络硬件中的RSS,QUIC就不太可能成为标准。
QUIC 的另一个优美的解决方案是对移动的支持。当你带着你的笔记本电脑四处移动到不同的 WIFI 网络时,或者带着你的手机四处移动时,你设备的 IP 地址会发生变化的。操作系统以及协议不会优雅的关闭掉老的连接而打开新的连接。然而,QUIC,网络连接的标识符并不是传统概念上的一个“socket”(源/目标 端口/地址 协议的绑定),而是一个64位的赋值到连接上的标识符。
这意味着当你移动时,即使 IP 地址改变了,你依然能够和 YouTube 继续保持一个持续不间断的视频流,或者继续拨打一个视频电话而不被异常中断。网络工程师们已经和“移动IP”的技术问题攻关了几十年,试图想出一个有效的解决方案。他们专注于端到端原则,也就是在你移动时以某种方式保持一个恒定的 IP 地址,这不是一个实际的解决方案。很高兴看到 QUIC / HTTP/3 最终解决了这个问题。
如何使用这种新的交通工具?几十年来,网络编程的标准一直是被称为“sockets”的传输层API。调用recv()之类的函数来接收代码中的包。使用QUIC/HTTP/3,我们不再拥有操作系统传输层API。相反,它是一个更高层次的特性,可以在go编程语言中使用,或者在OpenResty nginx web服务器中使用Lua。
我之所以提到这一点,是因为在您对OSI模型的学习中,有一件事遗漏了,那就是它最初设想每个人都编写应用层(7)api,而不是传输层(4)api。应该有一些应用程序服务元素,它们可以以标准的方式为不同的应用程序处理文件传输和消息传递之类的事情。我认为人们正越来越多地转向这种模式,尤其是由带有go、QUIC、protobufs等的谷歌驱动。
我之所以提到这一点,是因为谷歌和微软之间的差异。微软拥有一个流行的操作系统,所以它的创新是由它在该操作系统中所能做的事情驱动的。谷歌的创新是由它可以放在操作系统上的东西驱动的。然后是Facebook和亚马逊自己,它们必须在谷歌提供的堆栈之上(或之外)进行创新。世界上排名前五的公司依次是苹果、谷歌、微软、亚马逊和facebook,因此,每一家公司推动创新的地方都很重要。
结论
在 20 世纪 70 年代,TCP 被创造出来的时候,是非常了不起的。它处理的事情,如拥塞,比其他竞争性协议要好很多。人们没有办法预料到会有 40 亿个 IPV4 地址的出现,那时候预计现代互联网的竞争性设计会比七八十年代要好。
IPv4 到 IPv6 的升级,很大程度上保持了 IP 的优势。 从 TCP 到 QUIC 的升级同样是基于 TCP 的优点的,并将其扩展到现代需求上。实际上令人惊讶的是,TCP 已经持续了如此长的时间,它在没有升级的情况下,做得很好。