两个端点都可以通过在构成连接序的
SETTINGS
帧中包括SETTINGS_INITIAL_WINDOW_SIZE
的值来调整新流的初始窗口大小。
以及接收器增加连接和流控制窗口的方法:
WINDOW_UPDATE
帧的有效载荷是一个保留比特加上一个无符号的31比特整数,该整数指示除了现有流控制窗口之外发送器还可以发送的八位字节的数量。流控制窗口增量的合法范围是1到2^31-1(2147483647)个八位字节。[…]
接收到WINDOW_UPDATE帧的发送器按帧中指定的量更新相应的窗口。
接收器同时增加或减少所有流(但不是连接)的流控制窗口的一种方法:
当
SETTINGS_INITIAL_WINDOW_SIZE
的值发生变化时,接收器必须通过新值和旧值之间的差值来调整其维护的所有流流控制窗口的大小。
但据我所知,接收器无法在不改变初始窗口大小的情况下减少单个流的流控制窗口。
这是正确的吗?如果是,为什么不呢?如果您在一个连接上多路复用许多长寿命流,那么这样做似乎是合理的。您可能有一些BDP控制的内存预算用于整个连接,在流之间分配,并根据其最近的带宽需求调整每个流获得的比例。如果其中一个暂时空闲,您希望能够将其窗口重置为较小,这样它就不会占用内存预算,而不会影响其他流,也不会使其无法接收新的流。
(当然,我知道存在竞争,发送者可能在收到递减量之前已经发送了数据。但由于上面的SETTINGS_INITIAL_WINDOW_SIZE
机制,窗口已经被允许为负,所以在这里允许负窗口似乎也是合理的。)
如果不依靠发件人的转发进度来吃掉流控制窗口中的滞留字节,真的不可能做到这一点吗?
以下是我对这个问题感兴趣的更多细节,因为我意识到XY问题。
我正在考虑如何解决RPC流控制问题。我有一个内存预算有限的服务器,并且传入流的优先级不同,应该允许它们消耗多少内存。我想在它们之间实现加权的最大-最小公平性,调整它们的流控制窗口,使它们的总和不超过我的内存预算,但当我们不受内存限制时,我们可以获得最大吞吐量。
出于效率的原因,希望在单个连接上多路复用不同优先级的流。但是,随着需求的变化,或者随着其他连接的出现,我们需要能够向下调整流量控制窗口,这样它们的总和仍然不超过预算。当流B出现或接收到更高的优先级,但流a处于一堆流量控制预算中时,我们需要减少a的窗口并增加B的窗口。
即使没有多路复用,同样的问题也适用于连接级别:据我所知,在不改变初始窗口大小的情况下,无法向下调整连接流控制窗口当然,随着客户端发送数据,它会向下调整,但我不想依赖客户端的转发进度,因为这可能需要任意长的时间。
有可能有更好的方法来实现这一点!
一个有N个流的服务器,其中一些是空闲的,一些是正在向客户端下载数据,通常会将连接窗口重新分配给活动流。
例如,假设您正在观看电影,并且同时从同一服务器下载一个大文件。
连接窗口是100,每个流也有一个100的窗口(显然,在许多流的情况下,所有流窗口的总和将被连接窗口所限制,但如果只有一个流,则它可以是最大值)。
现在,当你观看和下载时,每个流得到50。
如果您暂停电影,并且服务器知道这一点(即,它不会耗尽电影流窗口),那么服务器现在必须只提供一个流,连接窗口为100,单个流(下载流)也有窗口为100。因此,将整个窗口重新分配给活动流。
只有当客户端没有告诉服务器电影已经暂停时,你才会遇到问题。在这种情况下,服务器将继续发送电影数据,直到电影流窗口耗尽(或准耗尽),而客户端不会确认该数据,因为它已暂停。此时,服务器会注意到数据未被一个流确认,并停止向其发送数据,但当然会占用部分连接窗口,从而减少活动下载流的窗口。
从服务器的角度来看,它有一个非常好的连接,其中一个流(下载流)以最高速度工作得很好,但另一个流会打嗝并耗尽窗口,导致另一个流量减慢(可能停止),即使是同一个连接!
显然,这不可能是连接/通信问题,因为一个流(下载流)在最高速度下运行得非常好。因此,这是一个应用程序问题。
服务器上的HTTP/2实现不知道其中一个流是可以暂停的电影——应用程序必须将此信息传达给服务器,并保持连接窗口尽可能大。
将新的HTTP/2帧引入到";暂停";下载(或改变现有帧的语义以适应"暂停"命令)会使协议变得相当复杂,因为这是一个100%由应用程序驱动的功能——是应用程序必须触发"暂停"的发送;暂停";命令,但此时它可以发送自己的";暂停";消息发送到服务器,而不会使HTTP/2规范复杂化。
这是一个有趣的案例,HTTP/1.1和HTTP/2的行为非常不同,需要不同的代码才能以类似的方式工作。
使用HTTP/1.1,您将有一个用于电影和下载的连接,它们将是独立的,并且客户端应用程序将不需要与服务器通信电影被暂停——它可以停止从电影连接读取,直到它变得TCP拥塞而不影响下载连接——假设服务器是非阻塞的以避免可扩展性问题。