(六) HTTP/2流的优先级

来源:互联网 发布:马哥python 编辑:程序博客网 时间:2024/06/17 13:19

客户端可以通过在打开流的HEADERS帧中包含优先次序信息来为一个新流指定优先级。在其它任意时间,可以使用PRIORITY帧来改变流的优先级。

优先次序的目的是允许端点表达希望对端在管理并发流的时候如何分配资源。更重要的是,当发送能力受限的时候,优先级能够用来选择流发送帧。

通过让流依赖其它流的完成,可以为流排列优先级。每个依赖都被分配了一个相对权重,即一个用来决定分配给依赖相同流的各个流的可用资源相对比例的数字。

显式地为一个流设置优先级是对一个优先级处理过程的输入,它不能保证这个流相对于其它流具有任何特定的处理和传输顺序。一个端点不能强迫对端使用优先级按照特定的顺序处理并发的多个流。因此,优先级的表达仅仅是一个建议。

优先级信息可以从消息中省略。如果没有提供优先级,那么就使用默认的优先级。

流依赖

每个流都可以显式地依赖其它流。依赖表达了希望优先分配资源给被依赖的流,而不是依赖其它流的流。

不依赖任何其它流的流被分配了值为0x0的依赖。也就是说,实际上并不存在的“流0”构成了依赖树的树根。

一个依赖其它流的流是“从属流”。一个被其它流依赖的流是“父亲流”。依赖当前树中不存在的流(例如,流的状态是“空闲”)会导致流被赋予默认的优先级。当依赖其它流时,当前的这个流就被添加为父亲流的一个新的依赖。共享相同的父亲流的各个从属流之间并没有固定的排序。例如,如果流B和流C依赖流A,并且,新创建的流D也依赖流A,那么结果就是,流A同时被流B、C和D依赖,但是,流B、C和D之间并没有确定的顺序。过程如下图所示:

       A                 A      / \      ==>      /|\     B   C             B D C

“排他”标识允许插入一个新的层级的依赖。“排他”标识导致新插入的流成为它的“父亲流”的唯一“从属流”,而“父亲流”原来的其它“从属流”变成了新插入流的“从属流”。在前面的例子中,如果新创建的流D(具有“排他”标识)依赖流A,那么,流D就变成了流B和流C所依赖的“父亲流”。过程如下图所示:

                         A       A                 |      / \      ==>       D     B   C              / \                       B   C

在依赖树中,只有在以下情况下才应该给“从属流”分配资源:该“从属流”依赖的所有流都已经被关闭或者无法取得进展。(这里的“所有流”包括从父亲流一直向上到达树根所经过的路径中的所有流,也就是当前流在依赖树中的所有“祖先流”)

一个流不能依赖它自己。客户端或服务端必须将这种情况视为类型为PROTOCOL_ERROR的流错误。

依赖权重

所有“从属流”都被分配了1到256之间的一个整数权重。

依赖相同“父亲流”的多个“从属流”应该按照权重比例被分配资源。因此,如果流B依赖流A,权重是4;流C依赖流A,权重是12,并且,流A没有任何进展,那么,理论上流B获取的资源相当于流C的三分之一。

调整优先级

使用PRIORITY帧可以改变流的优先级。设置一个依赖将导致一个流变得依赖某个特定的“父亲流”。

如果“父亲流”的优先级被重新调整,那么“从属流”们跟随它们的“父亲流”一起在依赖树中移动。为一个重新调整优先级的流设置一个具有“排他”标识的依赖,会导致所有之前依赖新的“父亲流”的“从属流”变为依赖调整优先级的流的“从属流”。

如果一个流被设置为依赖它的一个“从属流”,那么,这个“从属流”首先被调整为依赖它的前任“父亲流”的“父亲流”。被移动的依赖保持它们的权重不变。

举个例子吧,最初的依赖树中,流B和流C依赖流A,流D和流E依赖流C,流F依赖流D。如果A被改为依赖D,那么D取代A的位置。所有其它依赖关系保持不变,但F是个例外,如果调整优先级时使用了“排他”标识,那么F最终变为依赖A。调整过程如下图所示:

       x                x                x                 x       |               / \               |                 |       A              D   A              D                 D      / \            /   / \            / \                |     B   C     ==>  F   B   C   ==>    F   A       OR      A        / \                 |             / \             /|\       D   E                E            B   C           B C F       |                                     |             |       F                                     E             E                     (中间步骤)          (非排他)           (排他)

优先级状态管理

如果一个流被从依赖树中移除,那么它的“从属流”们被调整为依赖被关闭流的“父亲流”(被关闭的流就是被移除的那个流)。新的依赖的权重被重新计算:基于被关闭流的“从属流”们的权重比例,将被关闭流的权重分配给它的“从属流”们。

从依赖树中移除流会导致一些优先级信息丢失。资源在具有相同“父亲流”的多个“从属流”之间共享,这意味着,如果其中一个“从属流”关闭或被阻塞,那么,分配给它的任何空闲容量都会被重新分发给它的最近“邻居流”。然而,如果公共依赖(父亲流)被从树中移除,那么,这些“从属流”就与更高一层的流共享资源。

例如,假设流A和流B共享一个父亲流,流C和流D都依赖流A。在流A被移除之前,如果流A和流D无法继续处理,那么流C就会接收所有投入给流A的资源。如果流A从树中移除,流A的权重将分配给流C和流D。如果流D仍然无法继续处理,将导致流C获取的资源比例降低。对于相同的初始权重,流C获取到三分之一,而不是二分之一的可用资源。

有可能创建依赖的优先级信息还在传输过程中,流就已经关闭了。如果在依赖中标识的一个流没有关联的优先级信息,那么依赖它的从属流将被分配一个默认的优先级。这有可能创建不理想的优先级,因为流可能被分配一个不符合预期的优先级。

为了避免这种问题,端点应该在流关闭以后的一段时间内保持流的优先级状态。状态的保存时间越长,流被分配错误或默认的优先级的机会就越少。

类似地,处于空闲状态的流也可以被分配优先级或者成为其它流的“父亲流”。这允许在依赖树中创建一组节点,从而提供更加灵活的优先级表现形式。空闲的流以一个默认的优先级开始。

为未达到SETTINGS_MAX_CONCURRENT_STREAMS参数最大限制的流们保留优先级信息可能会增加端点的负担。因此,可以限制保留的优先级状态信息的数量。

端点为优先级维护的附加状态的数量可以依赖自身的负载而设置。在高负载下,优先级状态可以被丢弃,以限制资源的承诺。在极端情况下,端点甚至可以丢弃处于激活状态或保留状态的流的优先级信息。如果应用了限制,端点应该维护的状态数量至少不能低于SETTINGS_MAX_CONCURRENT_STREAMS中设置的值。具体的实现应该也尝试保留优先级树中活跃使用的流的状态。

如果保留了足够的状态的话,当接收到修改一个关闭流的优先级的PRIORITY帧时,端点应该修改依赖树中这个流的“从属流”的优先级。

默认优先级

所有的流最初都被设置为“非排他”地依赖流0x0。“推送流”最初依赖它们的“关联流”。在这两种情况下,流被分配默认的优先级,值为16。

0 0
原创粉丝点击