HTTP/2.0 多路复用如何与 TCP 一起工作?
Posted
技术标签:
【中文标题】HTTP/2.0 多路复用如何与 TCP 一起工作?【英文标题】:How does HTTP/2.0 multiplexing work with TCP? 【发布时间】:2020-07-10 00:19:08 【问题描述】:我不是专业的网络工程师,所以我希望我的问题不会显得含糊或幼稚。
HTTP/2.0 中的多路复用似乎利用单个 TCP 连接同时处理多个/不同的请求,这样我们就可以避免线头阻塞问题。我想知道在感觉数据重新组装中它如何与底层 TCP 连接工作/重叠。
TCP 还确保在接收方接收到的数据 (D) 被重建,即使构成 D 的数据包被乱序(或丢失)接收,以在接收方重新构建 D,然后将其移交给应用程序。
我的问题是:HTTP/2.0 中的帧概念如何适应 TCP 数据包重组以在接收端构成完整的消息?哪个先发生?或者,帧和数据包之间存在什么样的映射(一对一、一对多等)?简而言之,它们如何协同工作?
【问题讨论】:
【参考方案1】:一连串的HTTP/2帧,属于同一个流还是属于不同的流无所谓,就是一串字节。
TCP 不解释这些字节。 TCP 发送方只是将字节打包成 TCP 帧并一起发送。 TCP 接收方接收 TCP 帧并重新组合恰好形成一系列 HTTP/2 帧的字节。
TCP 和 HTTP/2 并不能真正协同工作,因为 TCP 不知道它正在传输什么 - 它只是一系列不透明的字节。
因此,TCP 帧和 HTTP/2 帧之间没有映射。
考虑到在大多数情况下 HTTP/2 是加密的,因此您有 TCP 传输恰好是 TLS 帧字节的不透明字节(可能是分段的 - 即 TCP 帧可能包含 1.5 个 TLS 帧,其余的 TLS 帧字节在后续TCP 帧);每个 TLS 帧都包含不透明字节,这些字节恰好是 HTTP/2 帧字节(也可能是碎片化的)。
【讨论】:
所以每一层都负责跟踪自己的 TCP/HTTP 帧(每个请求),以便它们可以在接收端重新组装它们? 正确,每一层都打包自己的帧,下层只是将上层的这些打包帧解释为不透明的字节流。 感谢您的清晰解释【参考方案2】:HTTP/2 数据包作为一个或多个 TCP 数据包发送。就像 TCP 数据包最终作为 IP 数据包(或数据报)发送一样。
这确实意味着即使 HTTP/2 在应用层 (HTTP) 进行了多路复用,但它在传输层 (TCP) 并没有真正独立的流,而 HTTP/2 的一个问题是我们刚刚移动了头部从 HTTP 层到 TCP 层的线路 (HOL) 阻塞问题。
我们来看一个例子:一个示例网页需要下载10张图片才能显示。
在 HTTP/1.1 下,浏览器会打开一个 TCP 连接,触发第一个请求,然后因为无法使用该 TCP 连接发出后续请求而被卡住。尽管事实上 TCP 连接在得到响应之前什么都不做,并且在 TCP 层没有任何东西阻止它。这纯粹是一个 HTTP 限制,主要是因为 HTTP/1 是基于文本的,因此不可能混合一些请求。 HTTP/1.1 确实有 HTTP 管道的概念,它允许发送后续请求,但它们仍然必须按顺序返回。而且它的支持很差。相反,作为一种解决方法,浏览器打开了多个连接(通常为 6 个),但这也有很多缺点(创建速度慢、无法跟上速度并且无法在它们之间划分优先级)。
HTTP/2 允许在同一个 TCP 连接上发送这些后续请求,然后以任何顺序接收所有请求的位并将它们拼凑在一起进行处理。因此,请求的第一个图像实际上可能是最后收到的图像。这对于慢速连接(发送延迟占总时间的很大一部分)或服务器可能需要一段时间处理某些请求(例如,如果必须从磁盘获取第一张图像但第二个已经在缓存中可用,那么为什么不使用连接来发送第二个图像)。这就是为什么 HTTP/2 通常比 HTTP/1.1 更快更好——因为它更好地使用 TCP 连接并且没有那么浪费。
但是,由于 TCP 是一种有保证的有序协议,它不知道更高级别的应用程序 (HTTP) 将其用于什么目的,如果 TCP 数据包丢失,这确实会给 HTTP/2 带来一些问题。
假设这 10 张图片都是按顺序返回的。但是来自第一个图像的数据包丢失了。理论上,如果 HTTP/2 真的由独立的流组成,浏览器可以显示最后 9 张图像,然后重新请求丢失的 TCP 数据包,然后显示第一张图像。相反,在 TCP 让上层 HTTP 知道收到了哪些消息之前,所有 10 个图像都被搁置等待丢失的 TCP 数据包重新发送。
因此,在有损环境中,HTTP/2 的性能明显低于具有 6 个连接的 HTTP/1.1。
这在创建 HTTP/2 时是众所周知的,但在大多数情况下,HTTP/2 更快,所以他们还是发布了它,直到他们能够修复这种情况。
HTTP/3 旨在解决剩下的情况。它通过从 TCP 转移到一个名为 QUIC 的新协议来实现这一点,该协议与 TCP 不同,它是根据内置的多路复用的思想制作的。 QUIC 建立在 UDP 之上,而不是尝试创建一个全新的低级协议,因为它得到了很好的支持。但是 QUIC 非常复杂,需要一段时间才能到达这里,这就是为什么他们没有坚持使用 HTTP/2 来拥有它,而是在此过程中发布了他们拥有的东西。
【讨论】:
我在想 10 个请求是完全相互独立的,这意味着每个请求都像它自己的子 tcp 连接一样,因此 TCP 可以一一识别它们。但显然,这将伴随 HTTP/3 提供。 正确。它们在 HTTP 层相互独立,但在 TCP 层不相互独立。尽管这也不是 100% 正确的,因为服务器可以在流中确定优先级以决定发送数据包的顺序 - 但这是依赖关系的好处。 HTTP/2 连接默认保持活动状态。规范对“HTTP/2 连接是持久的”这句话有点模糊。为了获得最佳性能,预计客户端不会关闭连接,直到确定不需要与服务器进行进一步通信(例如,当用户导航离开特定网页)或直到服务器关闭连接。” tools.ietf.org/html/rfc7540#section-9.1 只要接收到 TCP 数据包,TCP 堆栈就可以将它们释放给应用程序(在本例中为浏览器),并将它们视为字节流。所以浏览器认为流是独立的,即使它们不是 100% 独立的。在接收完整文件之前,可以处理许多资源(例如 HTML、渐进式 JPEG)。在 HTTP/1 和 HTTP/2 下确实如此。其他资源(例如 javascript、CSS、非渐进式 JPEG)不能真正以这种方式处理,浏览器只是读取然后缓冲字节,直到它拥有完整的文件。 我可以推荐一本关于这个主题的好书 :-) manning.com/books/http2-in-action以上是关于HTTP/2.0 多路复用如何与 TCP 一起工作?的主要内容,如果未能解决你的问题,请参考以下文章
Kafka核心技术与实战——13 | Java生产者是如何管理TCP连接的?