HTTP/2文件下载比HTTP/1.1慢一些,主要有两个原因:帧开销和流量控制。
在HTTP/1.1中,如果使用Content-Length
实现分隔下载,那么只会下载内容字节,而在HTTP/2中,每个DATA
帧都需要额外的9个字节用于帧头。对于常规的最大帧大小16384字节,这是一个很小的开销,但确实存在。
可能导致缓慢的主要原因是HTTP/2流量控制。客户端必须确保扩大默认会话和流量控制窗口,即默认情况下为65535字节。
HTTP/2的工作原理是,服务器为每个HTTP/2会话(连接)和该会话中的每个流保留一个发送窗口。当下载开始时,服务器只能发送由发送窗口允许的字节数,无论是对于该流还是该会话,以先用完者为准。然后它必须等待客户端发送WINDOW_UPDATE帧,这些帧会重新填充流和会话流量控制窗口,告诉服务器客户端已经准备好接收更多数据。
对于默认大小的小窗口,由于客户端和服务器之间的网络延迟,特别是如果实现得不好,此机制可能会降低下载性能。大部分时间服务器将被阻塞,等待客户端发送WINDOW_UPDATE以便服务器可以发送更多数据。
Multiplexing具有双重作用。它不仅允许同时启动多个文件的下载(可能比HTTP/1.1更多,因为后者只能打开较少的连接),而且每个流下载的数据都有助于减少会话发送窗口。每个流可能仍然有未耗尽的发送窗口(因此可以发送更多数据),但会话窗口已经用完,所以服务器必须停止。各个流相互竞争以消耗会话发送窗口。服务器实现也很重要,因为它必须正确地交错来自多个流的帧。
话虽如此,只要您拥有先进的客户端和服务器实现以及足够的调整参数来控制关键参数,HTTP/2仍然可以与HTTP/1.1实现同等水平。
理想情况下,在客户端:
- 能够控制会话和流的初始流量控制窗口
- 良好的实现,可以在服务器仍在下载时向服务器发送
WINDOW_UPDATE
帧,以便服务器永远不会停顿;这可能需要自我调节特性,具体取决于带宽延迟乘积(类似于TCP所做的)
理想情况下,在服务器上:
- 能够正确地交错来自同一会话的多个流的帧(例如,避免下载第一个流的所有帧,然后下载第二个流的所有帧,等等,而是先下载第一个流的一帧,然后是第二个流的一帧,然后再是第一个流的一帧,等等)
【免责声明,我是Jetty的HTTP/2维护人员】
Jetty 9.4.x支持以上所有功能,因为我们与社区和客户合作,确保HTTP/2下载尽可能快速。
我们在服务器上实现了适当的交错,并且Jetty的
HttpClient
和
HTTP2Client
分别提供了处理HTTP和HTTP/2请求的高级别和低级别API。
BufferingFlowControlStrategy
中实现了流量控制,允许调整何时发送
WINDOW_UPDATE
帧(尽管目前还不是动态的)。客户端还可以配置初始流量控制窗口。Jetty中的所有内容都是可插拔的,因此您可以编写更高级的流量控制策略。
即使您不使用Java或Jetty,请确保剖析(或编写)客户端和服务器上使用的库,以便它们提供上述功能。
最后,您需要尝试并测量;通过适当的HTTP/2实现和配置,多路复用效应应该发挥作用,从而增加并行性并减少客户端和服务器的资源利用率,因此您将比HTTP/1.1具有优势。