互联网工程任务组(IETF) 奥. 一穂
请求注释: 9218 Fastly
类别: 标准轨道 L. Pardue
ISSN: 2070-1721 Cloudflare
June 2022

HTTP 可扩展优先级方案


摘要

本文档描述了一种方案,允许 HTTP 客户端传达其关于上游服务器如何对其请求的响应进行优先级排序的偏好,同时也允许服务器向下游中介提示其响应在转发时应该如何被优先排序。本文档定义了用于以与 HTTP 版本无关的方式传达初始优先级的 Priority 头字段,以及用于重新优先级设置响应的 HTTP/2 和 HTTP/3 帧。它们共享一种通用的格式结构,旨在提供未来的可扩展性。

本备忘录状态

这是一个互联网标准轨道文档。

本文档是互联网工程任务组 (IETF) 的产物。它代表了 IETF 社区的共识。该文档已接受公开评审并已获得互联网工程指导组 (IESG) 批准发布。有关互联网标准的更多信息,请参阅 RFC 7841 第 2 节

有关本文件当前状态、任何勘误以及如何提供反馈的信息,可在 https://www.rfc-editor.org/info/rfc9218 获得。

Copyright Notice

Copyright (c) 2022 IETF Trust and the persons identified as the document authors. All rights reserved.

This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.


1. 引言

HTTP [HTTP] 资源的表示通常与一个或多个其他资源存在关系。客户端经常在处理已检索的表示时发现这些关系,这可能导致进一步的检索请求。同时,这些关系的性质决定了客户端是否会被阻塞而无法继续处理本地可用资源。一个示例是 HTML 文档的可视化呈现,文档引用的级联样式表(CSS)文件的检索可能会阻塞渲染。相比之下,内联图像不会阻塞渲染,会在图像数据块到达时增量绘制。

HTTP/2 [HTTP/2] 和 HTTP/3 [HTTP/3] 支持在单个连接上复用请求和响应。任何提供复用的协议实现的一个重要功能是能够对发送的信息进行优先级排序。例如,为了尽早为客户端提供有意义的 HTML 文档呈现,HTTP 服务器应当优先发送那些对呈现最重要的 HTTP 响应,或这些响应的分块。

HTTP/2 和 HTTP/3 服务器可以通过任意方式对并发响应数据的传输进行调度。服务器可以忽略客户端的优先级信号仍然成功提供 HTTP 响应。然而,不了解客户端如何发出请求和消费响应的服务器可能会导致客户端应用性能不佳。优先级信号允许客户端传达它们对请求优先级的看法。服务器有其自身的需求,这些需求独立于客户端,因此服务器通常将优先级信号与其他可用信息结合起来,以指导响应数据的调度。

RFC 7540 [RFC7540] 的流优先级允许客户端发送一系列优先级信号来向服务器传达一个“优先级树”;该树的结构表示客户端偏好的相对排序和带权带宽分配。服务器可以将这些优先级信号用作优先级决策的输入。

RFC 7540 流优先级的设计与实现被观察到存在不足,具体说明见 第 2 节。因此,HTTP/2 [HTTP/2] 在其修订中弃用使用这些流优先级信号。为了保持线上的兼容性,HTTP/2 保留了这些协议元素(参见 HTTP/2 第 5.3.2 节),这意味着即使存在替代信号(例如本文档描述的方案),这些元素仍可能被使用。

本文档描述了一种使用绝对值的可扩展 HTTP 响应优先级方案。第 4 节 定义了优先级参数,这是一种标准化且可扩展的优先级信息格式。第 5 节 定义了 Priority HTTP 头字段,用于以与 HTTP 版本无关的方式传达初始优先级。客户端可以发送该头字段以指示它们对响应优先级的看法。同样,位于中介之后的服务器可以使用该字段向中介提示优先级。在发送请求之后,客户端可以通过发送对应于 HTTP 版本的帧来改变它们对响应优先级的看法(参见 第 6 节),这些帧如 第 7.1 节第 7.2 节 所定义。

头字段和帧的优先级信号是服务器响应优先级处理的输入。它们仅为建议,并不保证某一响应相对于其他响应在处理或传输顺序上的具体行为。第 10 节第 12 节 提供了关于服务器可能如何基于这些信号采取行动的考虑和指导。

1.1. 符号约定

本文件中出现的关键字 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", 和 "OPTIONAL" 应按 BCP 14 [RFC2119][RFC8174] 中的定义来解释,且仅当这些词以全部大写形式出现时适用。

本文件使用 [STRUCTURED-FIELDS] 第 3 节 中的术语来指定语法和解析:"Boolean"、"Dictionary" 和 "Integer"。

示例 HTTP 请求和响应采用来自 [HTTP/2] 的 HTTP/2 风格格式。

本文件使用来自 [QUIC] 的可变长度整数编码。

术语 "control stream" 用于描述 HTTP/2 标识符为 0x0 的流和 HTTP/3 控制流;参见 HTTP/3 第 6.2.1 节

术语 "HTTP/2 priority signal" 用于描述客户端在 HTTP/2 帧中发送给服务器的优先级信息;参见 HTTP/2 第 5.3.2 节


2. 替代 RFC 7540 流优先级 的动机

RFC 7540 流优先级(参见 RFC7540 第 5.3 节)是一个复杂的系统,客户端通过信号流的依赖关系和权重来描述一个不平衡树。它在部署和互操作性方面存在限制,并已在 HTTP/2 的修订中被弃用。为了保持线上的兼容性,HTTP/2 保留了这些元素(见 HTTP/2 第 5.3.2 节),这意味着即使存在替代信号(例如本文档描述的方案),这些元素仍可能被使用。

许多 RFC 7540 的服务器实现并不根据 HTTP/2 优先级信号采取行动。

优先级决策可以使用服务器关于资源的信息或请求生成顺序的信息。例如,了解 HTML 文档结构的服务器可能希望优先传送对用户体验至关重要的图像而不是其他图像。在 RFC 7540 中,服务器难以解释客户端发送的优先级信号,因为相同的条件可能导致不同客户端发出非常不同的信号。本文档描述的信号更简单且更受限,需要更少的解释并减少差异。

RFC 7540 未定义服务器可用于向中介提供优先级信号的方法。

RFC 7540 流优先级是相对于在同一连接上同时存在的其他请求表达的。在不清楚其他请求如何共享连接的应用中,或在像 HTTP/3 这样跨流没有强顺序保证的协议中,难以将此设计融入应用。

独立研究的实验 [MARX] 表明,对于 Web 使用场景,较简单的方案在性能特性上至少可以达到与实践中复杂的 RFC 7540 方案相当的表现。

2.1. 禁用 RFC 7540 流优先级

上述问题和洞见促使提出 RFC 7540 流优先级 的替代方案(参见 HTTP/2 第 5.3 节)。

本文档定义了 SETTINGS_NO_RFC7540_PRIORITIES HTTP/2 设置,以允许端点省略或忽略 HTTP/2 优先级信号(参见 HTTP/2 第 5.3.2 节)。SETTINGS_NO_RFC7540_PRIORITIES 的值 MUST 为 0 或 1。任何非 0 或 1 的值 MUST 被视为类型为 PROTOCOL_ERROR 的连接错误(参见 HTTP/2 第 5.4.1 节)。初始值为 0。

如果端点使用 SETTINGS_NO_RFC7540_PRIORITIES,它们 MUST 在第一个 SETTINGS 帧中发送该设置。发送者 MUST NOT 在第一个 SETTINGS 帧之后更改 SETTINGS_NO_RFC7540_PRIORITIES 的值。检测到更改的接收者 MAY 将其视为类型为 PROTOCOL_ERROR 的连接错误。

客户端可以通过将 SETTINGS_NO_RFC7540_PRIORITIES 的值设置为 1 来指示它们不使用 HTTP/2 优先级信号。SETTINGS 帧先于客户端发送的任何 HTTP/2 优先级信号到达,因此服务器可以在信号到达之前确定是否需要为信号处理分配资源。收到 SETTINGS_NO_RFC7540_PRIORITIES 值为 1 的服务器 MUST 忽略 HTTP/2 优先级信号。

服务器可以通过发送 SETTINGS_NO_RFC7540_PRIORITIES 值为 1 来指示它们将忽略客户端发送的 HTTP/2 优先级信号。

发送 SETTINGS_NO_RFC7540_PRIORITIES 的端点被鼓励使用替代的优先级信号(例如参见 第 5 节第 7.1 节),但并不要求使用特定的信号类型。

2.1.1. 在将可扩展优先级用作替代方案时的建议

在从服务器接收到 SETTINGS 帧之前,客户端不知道服务器是否在忽略 HTTP/2 优先级信号。因此,在客户端接收到来自服务器的 SETTINGS 帧之前,客户端 SHOULD 同时发送 HTTP/2 优先级信号和本文优先级方案的信号(参见 第 5 节第 7.1 节)。

一旦客户端收到包含 SETTINGS_NO_RFC7540_PRIORITIES 参数且其值为 1 的第一个 SETTINGS 帧,它 SHOULD 停止发送 HTTP/2 优先级信号。这可以避免发送已知会被忽略的冗余信号。

类似地,如果客户端收到 SETTINGS_NO_RFC7540_PRIORITIES 值为 0,或该设置参数不存在,则客户端 SHOULD 停止发送 PRIORITY_UPDATE 帧(第 7.1 节),因为这些帧很可能会被忽略。然而,客户端 MAY 继续发送 Priority 头字段(第 5 节),因为它是一个端到端信号,可能对位于服务器后方且与客户端直接连接的节点有用。


3. 可扩展优先级方案的适用性

本文档定义的优先级方案主要关注 HTTP 响应消息的优先级(参见 HTTP Semantics 第 3.4 节)。它定义了新的优先级参数(第 4 节)以及传达这些参数的方法(第 5 节第 7 节),用于将响应的优先级传达给负责对它们进行优先级排序的服务器。第 10 节 为服务器在结合其他输入和因素时如何依据这些信号采取行动提供了考虑。

CONNECT 方法(参见 HTTP Semantics 第 9.3.6 节)可用于建立隧道。对隧道的信号传递也类似适用;关于服务器端优先级的额外考虑见 第 11 节

第 9 节 描述了客户端如何可选地将本方案的元素应用于它们生成的请求消息的本地处理。

某些形式的 HTTP 扩展可能会改变 HTTP/2 或 HTTP/3 的流行为或定义新的数据承载机制。这类扩展可以自行定义如何应用本优先级方案。


4. 优先级参数

优先级信息是一系列键值对,为将来的扩展留出空间。每个键值对表示一个优先级参数。

Priority HTTP 头字段(第 5 节)是在发出请求或响应时以端到端方式传输这组优先级参数的方法。发送请求后,客户端可以通过发送对应 HTTP 版本的 PRIORITY_UPDATE 帧来改变其对响应优先级的看法(第 6 节),这些帧如 第 7.1 节第 7.2 节 所定义。帧仅在单跳上传输优先级参数。

中介可以在 PRIORITY_UPDATE 帧或 Priority 头字段中消费并产生优先级信号。仅将 Priority 请求头字段传递到下一跳的中介会保留来自客户端的原始端到端信号;见 第 14 节。中介可以既转发 Priority 头字段,又额外发送 PRIORITY_UPDATE 帧。这将既保留客户端的端到端信号,又按 第 7 节 的指导指示下一跳使用不同的优先级。替换或添加 Priority 请求头字段的中介会覆盖客户端的原始端到端信号,这可能影响请求随后所有接收者的优先级处理。

对于 Priority 头字段和 PRIORITY_UPDATE 帧,优先级参数集合被编码为 Dictionary(参见 [STRUCTURED-FIELDS] 第 3.2 节)。

本文档定义了紧急度(u)和是否可增量处理(i)优先级参数。当接收到不携带这些优先级参数的 HTTP 请求时,服务器 SHOULD 视为指定了它们的默认值。

中介可以组合其转发的请求和响应中的信号。注意,响应中省略优先级参数的处理方式与请求中省略时不同;见 第 8 节

接收者按 [STRUCTURED-FIELDS] 第 4.2 节 中描述的方式解析 Dictionary。当 Dictionary 成功解析时,本文档额外要求:未知的优先级参数、取值超出范围的优先级参数,或类型不符的值 MUST 被忽略。

4.1. 紧急度

紧急度(u)参数值为 Integer(参见 [STRUCTURED-FIELDS] 第 3.3.1 节),取值范围为 0 到 7(含),按优先级递减排序。默认值为 3。

端点使用此参数来传达它们对 HTTP 响应先后顺序的看法。选择的紧急度值可以基于对服务器可能使用该信息按紧急度顺序传输 HTTP 响应的预期。值越小,优先级越高。

下面的示例展示了一个将紧急度设为 0 的 CSS 文件请求:

:method = GET
:scheme = https
:authority = example.net
:path = /style.css
priority = u=0

获取可能由多个 HTTP 资源组成的文档(例如 HTML)的客户端 SHOULD 将主资源分配为默认紧急度级别。该约定允许服务器使用特定于网站的知识来细化紧急度(见 第 8 节)。

最低紧急度级别(7)保留用于后台任务,例如软件更新的传送。此紧急度级别 SHOULD NOT 用于获取对用户交互有任何影响的响应。

4.2. 是否可增量处理

是否可增量处理(i)参数值为 Boolean(参见 [STRUCTURED-FIELDS] 第 3.3.6 节)。它表明 HTTP 响应是否可以被增量处理,即当响应的分块到达时是否能提供有意义的部分输出。

是否可增量处理参数的默认值为 false0)。

如果客户端对多个并发请求将是否可增量处理参数设为 false,则同时为这些响应提供相同紧急度的并发服务没有好处,因为客户端不会对这些响应进行增量处理。按请求生成的顺序逐一提供非增量响应被认为是最佳策略。

如果客户端对多个并发请求将是否可增量处理参数设为 true,则并发提供具有相同紧急度的请求可能是有益的。这样做会分配连接带宽,这意味着每个响应完成的时间会更长。增量交付在多个部分响应在完整响应可用之前可以为客户端提供一些价值时最为有用。

下面的示例展示了一个对 JPEG 文件的请求,其紧急度参数设为 5,且是否可增量处理参数设为 true

:method = GET
:scheme = https
:authority = example.net
:path = /image.jpg
priority = u=5, i

4.3. 定义新的优先级参数

在试图定义新的优先级参数时,必须注意不要对不理解这些新参数的现有端点或中介的优先级行为造成不利干扰。由于未知的优先级参数会被忽略,新的优先级参数不应以不向后兼容或不可回退安全的方式更改或修改紧急度(见 第 4.1 节)或是否可增量处理(见 第 4.2 节)。

例如,如果需要比八个紧急度级别提供更细粒度,可以使用额外的优先级参数对范围进行细分。不识别该参数的实现仍可以安全地继续使用较粗粒度的八个级别。

或者,可以增强紧急度。例如,图形用户代理可以发送一个 visible 优先级参数来指示所请求的资源是否在视口内。

通用优先级参数优于供应商特定、应用特定或部署特定的值。如果社区无法就通用值达成一致,则参数名称应相应地更具体(例如,使用标识供应商、应用或部署的前缀)。

4.3.1. 注册

新的优先级参数可以通过在 “HTTP Priority” 注册表中注册来定义。该注册表管理用于 Dictionary 中的键(短文本字符串)。由于每个 HTTP 请求都可能具有相关的优先级信号,因此短键长度(尤其是单字符字符串)是有价值的。为鼓励扩展同时避免对有吸引力的键值发生意外冲突,“HTTP Priority” 注册表依据键长度采用两种注册策略。

  • 键长度为单个字符的优先级参数的注册请求使用 Specification Required 策略,依据 RFC8126 第 4.6 节
  • 键长度大于一个字符的优先级参数的注册请求使用 Expert Review 策略,依据 RFC8126 第 4.5 节。规范性文档是受欢迎的但非必需。

在审查注册请求时,指定的专家可以考虑 第 4.3 节 中提供的额外指导,但不得以此作为拒绝的依据。

注册请求应使用以下模板:

Name:
[与参数键匹配的优先级参数名称]
Description:
[关于优先级参数语义和值的描述]
Reference:
[定义此优先级参数的规范文档]

有关将注册请求发送到何处的详细信息,请参阅注册表 <https://www.iana.org/assignments/http-priority>。


5. Priority HTTP 头字段

Priority HTTP 头字段是一个承载优先级参数的 Dictionary(参见 第 4 节)。它可以出现在请求和响应中。它是一个端到端的信号,用来表示端点关于 HTTP 响应应如何被优先处理的观点。第 8 节 描述了中介如何合并来自客户端和服务器的优先级信息。客户端不能将 Priority 响应头字段的出现或省略解释为任何优先级处理已发生的确认。关于端点如何基于 Priority 头字段值采取动作的指导见 9 节10 节

带有 Priority 头字段的 HTTP 请求可能会被缓存并在后续请求中重用;参见 [CACHING]。当源服务器基于收到的 HTTP 请求的属性生成 Priority 响应头字段时,服务器应通过使用控制缓存行为的头字段(例如 Cache-Control、Vary)来控制缓存响应的可缓存性或适用性。


6. 重新优先级

在客户端发送请求之后,改变响应的优先级可能是有益的。例如,浏览器可能会为一个 JavaScript 文件发出预取请求,并在 Priority 请求头字段中将紧急度参数设置为 u=7(后台)。然后,当用户导航到引用该 JavaScript 文件的页面时,在预取进行中,浏览器可以发送一个 reprioritization 信号,将 Priority 字段值设置为 u=0。PRIORITY_UPDATE 帧(第 7 节)可以用于此类重新优先级设置。


7. PRIORITY_UPDATE 帧

本文档为 HTTP/2 [HTTP/2] 和 HTTP/3 [HTTP/3] 指定了一种新的 PRIORITY_UPDATE 帧。该帧携带优先级参数并基于版本特定的标识符引用被优先化的目标。在 HTTP/2 中,此标识符为 stream ID;在 HTTP/3 中,标识符可以是 stream ID 或 push ID。与 Priority 头字段不同,PRIORITY_UPDATE 帧是按跳(hop-by-hop)的信号。

客户端在控制流上发送 PRIORITY_UPDATE 帧,使其可以独立于承载响应的流而发送。这意味着它们可用于对响应或推送流重新优先级,或在替代 Priority 头字段的情况下指示响应的初始优先级。

PRIORITY_UPDATE 帧在 Priority Field Value 字段中传达完整的一组优先级参数。省略某个优先级参数表示使用其默认值。无法解析 Priority Field Value MAY 被视为连接错误。在 HTTP/2 中,该错误类型为 PROTOCOL_ERROR;在 HTTP/3 中,该错误类型为 H3_GENERAL_PROTOCOL_ERROR。

客户端 MAY 在其引用的流打开之前发送 PRIORITY_UPDATE 帧(HTTP/2 推送流除外;参见 第 7.1 节)。此外,HTTP/3 在流之间不提供保证的 ordering,这可能导致帧被比预期更早接收。无论哪种情况都会导致竞争条件,即服务器接收到引用尚未打开的请求流的 PRIORITY_UPDATE 帧。为了解决此类情况,就调度而言,最近接收到的 PRIORITY_UPDATE 帧可以被视为最新信息并覆盖任何其他信号。服务器 SHOULD 缓冲最近接收到的 PRIORITY_UPDATE 帧,并在所引用的流打开后应用它。为每个流保存 PRIORITY_UPDATE 帧会消耗服务器资源,服务器可以通过本地实现策略对其进行限制。尽管可以发送任意数量的 PRIORITY_UPDATE 帧,但仅存储最近接收到的帧可以限制资源占用。

7.1. HTTP/2 PRIORITY_UPDATE 帧

HTTP/2 PRIORITY_UPDATE 帧(type=0x10)由客户端用于指示响应的初始优先级,或对响应或推送流重新优先级。它携带响应的 stream ID 以及以 ASCII 文本表示的优先级,使用与 Priority 头字段值相同的表示方法。

PRIORITY_UPDATE 帧头部中的 Stream Identifier 字段(参见 HTTP/2 第 5.1.1 节MUST 为零(0x0)。接收到具有其他值的 PRIORITY_UPDATE 帧 MUST 被视为类型为 PROTOCOL_ERROR 的连接错误。

HTTP/2 PRIORITY_UPDATE Frame {
  Length (24),
  Type (8) = 0x10,

  Unused Flags (8),

  Reserved (1),
  Stream Identifier (31),

  Reserved (1),
  Prioritized Stream ID (31),
  Priority Field Value (..),
}

图 1:HTTP/2 PRIORITY_UPDATE 帧格式

Length、Type、Unused Flag(s)、Reserved 和 Stream Identifier 字段在 HTTP/2 第 4 节 中有描述。PRIORITY_UPDATE 帧的负载包含以下附加字段:

Prioritized Stream ID:
用于指示被优先更新目标的 31 位流标识符。
Priority Field Value:
以 ASCII 文本表示、使用 Structured Fields 编码的优先级更新值。此表示方法与 Priority 头字段值相同。

当 PRIORITY_UPDATE 帧应用于请求流时,客户端 SHOULD 提供一个指向处于 "open"、"half-closed (local)" 或 "idle" 状态的流的 prioritized stream ID(即可能仍会接收数据的流)。服务器可以丢弃那些其 prioritized stream ID 指向 "half-closed (local)" 或 "closed" 状态的帧(即不会再发送更多数据的流)。已被优先但仍处于 "idle" 状态的流的数量,加上处于活动状态的流的数量(参见 HTTP/2 第 5.1.2 节),MUST NOT 超过 SETTINGS_MAX_CONCURRENT_STREAMS 参数的值。收到此类 PRIORITY_UPDATE 的服务器 MUST 使用类型为 PROTOCOL_ERROR 的连接错误进行响应。

当 PRIORITY_UPDATE 帧应用于推送流时,客户端 SHOULD 提供一个指向处于 "reserved (remote)" 或 "half-closed (local)" 状态的流的 prioritized stream ID。服务器可以丢弃那些其 prioritized stream ID 指向 "closed" 状态的帧。客户端 MUST NOT 提供指向处于 "idle" 状态的推送流的 prioritized stream ID。收到针对处于 "idle" 状态的推送流的 PRIORITY_UPDATE 的服务器 MUST 使用类型为 PROTOCOL_ERROR 的连接错误进行响应。

如果接收到的 PRIORITY_UPDATE 帧的 prioritized stream ID 为 0x0,接收方 MUST 使用类型为 PROTOCOL_ERROR 的连接错误进行响应。

服务器 MUST NOT 发送 PRIORITY_UPDATE 帧。如果客户端接收到 PRIORITY_UPDATE 帧,则其 MUST 使用类型为 PROTOCOL_ERROR 的连接错误进行响应。

7.2. HTTP/3 PRIORITY_UPDATE 帧

HTTP/3 PRIORITY_UPDATE 帧(type=0xF0700 或 0xF0701)由客户端用于指示响应的初始优先级,或对响应或推送流重新优先级。它携带被优先化元素的标识符以及以 ASCII 文本表示的更新优先级,使用与 Priority 头字段值相同的表示方法。type=0xF0700 的 PRIORITY_UPDATE 用于请求流,而 type=0xF0701 的 PRIORITY_UPDATE 用于推送流。

PRIORITY_UPDATE 帧 MUST 在客户端控制流上发送(参见 HTTP/3 第 6.2.1 节)。在客户端控制流以外的流上接收到 PRIORITY_UPDATE 帧 MUST 被视为类型为 H3_FRAME_UNEXPECTED 的连接错误。

HTTP/3 PRIORITY_UPDATE Frame {
  Type (i) = 0xF0700..0xF0701,
  Length (i),
  Prioritized Element ID (i),
  Priority Field Value (..),
}

图 2:HTTP/3 PRIORITY_UPDATE 帧

PRIORITY_UPDATE 帧的负载具有以下字段:

Prioritized Element ID:
作为优先级更新目标的 stream ID 或 push ID。
Priority Field Value:
以 ASCII 文本表示、使用 Structured Fields 编码的优先级更新值。此表示方法与 Priority 头字段值相同。

请求流变体的 PRIORITY_UPDATE(type=0xF0700)MUST 引用一个请求流。如果服务器收到对非请求流的 PRIORITY_UPDATE(type=0xF0700),则 MUST 将其视为类型为 H3_ID_ERROR 的连接错误。该 stream ID MUST 在客户端发起的双向流限制内。如果服务器收到对超出流限制的 stream ID 的 PRIORITY_UPDATE(type=0xF0700),则 SHOULD 将其视为类型为 H3_ID_ERROR 的连接错误。生成错误并非强制性要求,因为 HTTP/3 实现可能在确定 QUIC 层施加的活动流并发限制时存在实际障碍。

推送流变体的 PRIORITY_UPDATE(type=0xF0701)MUST 引用一个已承诺的推送流。如果服务器收到带有大于最大 push ID 或尚未被承诺的 push ID 的 PRIORITY_UPDATE(type=0xF0701),则 MUST 将其视为类型为 H3_ID_ERROR 的连接错误。

服务器 MUST NOT 发送任一类型的 PRIORITY_UPDATE 帧。如果客户端接收到 PRIORITY_UPDATE 帧,则 MUST 将其视为类型为 H3_FRAME_UNEXPECTED 的连接错误。


8. 合并客户端和服务器驱动的优先级参数

客户端并不总是最清楚哪些 HTTP 响应应被优先处理。服务器可能掌握额外的信息,可以与客户端表明的优先级相结合,以改进响应的优先级排序。例如,HTML 文档的使用可能非常依赖某个内联图像;此类依赖关系通常由服务器最清楚。或者,当服务器收到对字体 [RFC8081] 和图像具有相同紧急度的请求时,服务器可能会优先传送字体,以便可视客户端能尽早呈现文本信息。

源可以使用 Priority 响应头字段来指示其关于 HTTP 响应应如何被优先处理的观点。转发 HTTP 响应的中介可以将 Priority 响应头字段中发现的优先级参数与客户端的 Priority 请求头字段结合,作为其优先级处理过程的输入。对于如何合并优先级不提供具体指导;这由实现决定。

HTTP 响应中缺少优先级参数表示服务器不希望更改客户端提供的值。这与请求头字段不同:在请求头字段中省略优先级参数表示使用其默认值(参见 第 4 节)。

作为一个非规范性示例,当客户端发送一个紧急度参数为 5、是否可增量处理参数为 true 的 HTTP 请求时:

:method = GET
:scheme = https
:authority = example.net
:path = /menu.png
priority = u=5, i

并且源响应为

:status = 200
content-type = image/png
priority = u=1

中介可能会将其对紧急度的理解从 5 更改为 1,因为它更倾向于使用服务器提供的值。增量值继续为 true,即客户端指定的值,因为服务器未指定增量参数(i)。


9. 客户端调度

客户端 MAY 使用优先级值对其发起的请求在本地进行处理或调度选择。


10. 服务器调度

通常,HTTP 服务器尽可能提前发送所有响应是有益的。然而,当在单个连接上为多个请求提供服务时,请求之间可能会为诸如连接带宽等资源发生竞争。本节描述了在存在此类竞争时,服务器可如何调度竞争响应的发送顺序的考虑事项。

服务器调度是一个基于多种输入的优先级过程,优先级信号只是输入的一种形式。实现选择或部署环境等因素也会起作用。任何给定连接很可能具有许多动态变化。出于这些原因,不可能描述通用的调度算法。本文档为服务器可能如何基于优先级参数采取行动提供了一些基本的、非详尽的建议,但并未详细描述服务器如何将优先级信号与其他因素结合。端点不能依赖于基于优先级信号的特定处理。表达优先级仅为建议。

在可行时,建议服务器尊重紧急度参数(第 4.1 节),在可能时先发送更高紧急度的响应。

增量参数指示客户端如何在字节到达时处理响应。建议在可行时,服务器尊重增量参数(第 4.2 节)。

具有相同紧急度的非增量响应 SHOULD 通过按照流 ID 的升序优先分配带宽来提供,这对应于客户端发出请求的顺序。这样做可确保客户端可以使用请求顺序来影响响应顺序。

具有相同紧急度的增量响应 SHOULD 通过在它们之间共享带宽来提供。增量响应的消息内容会随着部分或分块的接收被使用。客户端可能更受益于接收所有这些资源的一部分,而不是单个资源的全部。对于提高性能所需的资源部分大小因资源类型而异:有些资源类型在较早位置包含关键元素;有些资源可以渐进使用。该方案未对服务器应如何使用大小、类型或任何其他输入来决定优先级进行明确要求。

在某些情形下,服务器需要在相同紧急度级别下调度多个增量和非增量响应。严格遵守基于紧急度和请求生成顺序的调度指导可能会导致客户端的次优结果,因为早期的非增量响应可能阻止随后发出的增量响应被提供。以下为此类挑战的示例:

  1. 在相同紧急度级别下,先有一个对大型资源的非增量请求,随后是对小资源的增量请求。
  2. 在相同紧急度级别下,先有一个长度不确定的增量请求,随后是对大型资源的非增量请求。

建议服务器在可能的情况下避免此类饥饿。实现避免的方法由实现者决定。例如,服务器可以基于内容大小等信息预先发送特定增量类型的响应。

服务器推送的最佳调度很难确定,尤其是在被推送的资源与活动并发请求发生竞争时。服务器在调度时可以考虑多种因素,例如被推送资源的类型或大小、触发推送的请求的优先级、活动并发响应的计数、其他活动并发响应的优先级等。对于如何最好地应用这些因素没有通用指导。过于简单的服务器很容易以过高的优先级推送并阻塞客户端请求,或以过低的优先级推送并延迟响应,从而抵消服务器推送的预期目标。

优先级信号是服务器推送调度的一个因素。参数默认值的概念在此略有不同,因为没有明确的客户端信号初始优先级。服务器可以应用源响应中提供的优先级信号;见 第 8 节 中关于合并的指导。在缺少源信号的情况下,应用默认参数值可能并非最佳。无论服务器以何种方式决定调度被推送的响应,它都可以通过在 PUSH_PROMISE 或 HEADERS 帧中包含 Priority 字段向客户端指示预期优先级。

10.1. 具有多个后端连接的中介

为一个 HTTP 连接提供服务的中介可能会将请求分散到多个后端连接。当其严格应用优先级规则时,低优先级请求在高优先级请求在飞行中的情况下无法取得进展。此类阻塞可能会传播到后端连接,对端可能将其解释为连接停滞。端点通常会实施防止停滞的保护措施,例如在一定时间后突然关闭连接。为减少发生此类情况的可能性,中介可以避免严格遵循优先级,而是为所有正在转发的请求分配少量带宽,以便每个请求都能随时间取得一些进展。

同样,服务器 SHOULD 为作为隧道的流分配一定量的带宽。


11. CONNECT 方法的调度

当流承载 CONNECT 请求时,本文档中的调度指导适用于该流上的帧。发出多个 CONNECT 请求的客户端可以将增量参数设置为 true。实现了增量参数处理建议的服务器(第 10 节)很可能会公平地调度这些请求,防止一个 CONNECT 流阻塞其他流。


12. 重传调度

诸如 TCP 和 QUIC 的传输协议通过检测分组丢失并重传丢失的信息来提供可靠性。除了 第 10 节 中的考虑外,重传数据的调度可能会与新数据竞争。本节余下部分讨论在使用 QUIC 时的有关考虑。

QUIC 第 13.3 节 说明:“端点 SHOULD 优先重传数据而不是发送新数据,除非应用指定的优先级另有指示”。当 HTTP/3 应用使用本文档定义的优先级方案并且 QUIC 传输实现支持应用指示的流优先级时,一个在调度新数据和重传数据时考虑流的相对优先级的传输实现可能更符合应用的预期。然而,对于传输如何基于这些信息选择调度并没有要求,因为该决定取决于若干因素和权衡。传输可能会优先为高紧急度流发送新数据而不是为低优先级流重传数据,或者无论紧急度如何都优先重传数据。

QUIC-RECOVERY 第 6.2.4 节 还强调在探测超时(Probe Timeout)计时器到期后发送探测分组时关于应用优先级的考虑。支持应用指示优先级的 QUIC 实现可能会在选择探测数据时使用流的相对优先级。


13. 公平性

通常,HTTP 实现依赖底层传输在竞争带宽的连接之间维持公平性。当中介在客户端连接上接收 HTTP 请求时,会将它们转发到后端连接。根据中介如何将请求合并或拆分到不同的后端连接,不同的客户端可能会体验到不同的性能。如果中介在转发请求时也使用优先级信号,这种差异可能会扩大。13.1 节13.2 节 讨论了减缓此类不公平扩大的措施。

相反,第 13.3 节 讨论了服务器如何可能根据优先级信号有意地对某些连接分配不等的带宽。

13.1. 合并中介

当中介将来自多个客户端的 HTTP 请求合并为到后端服务器的一条 HTTP/2 或 HTTP/3 连接时,来自某一客户端的请求可能携带表明比其他客户端更高优先级的信号。

在某些情况下,位于中介后的服务器遵从 Priority 头字段值是有益的。例如,资源受限的服务器可能会延迟传输具有后台紧急度级别(7)的软件更新文件。然而,在最坏情况下,多个客户端声明的优先级不对称可能导致发往一个用户代理的所有响应都被延迟,直到发往另一个用户代理的所有响应发送完毕。

为缓解这种公平性问题,服务器可以将关于中介的知识作为其优先级决策的另一个输入。例如,如果服务器知道中介正在合并请求,则它可以避免一次性完整传送响应,而是分配带宽(例如以轮询方式)进行分发。如果受限资源是中介与用户代理之间的网络容量,这种方法可行,因为中介会缓冲响应并基于其实现的优先级方案转发分块。

服务器可以通过配置确定请求是否来自中介,或检查请求是否包含以下某个头字段:

13.2. HTTP/1.x 后端

内容分发网络(CDN)基础设施通常在前端和后端支持不同的 HTTP 版本。例如,面向客户端的边缘可能支持 HTTP/2 和 HTTP/3,而与后端服务器的通信则使用 HTTP/1.1。与连接合并不同,CDN 会将请求“拆分”为到后端的独立连接。HTTP/1.1(或更早版本)不支持在单一连接内的响应复用,因此不存在公平性问题。然而,后端服务器 MAY 仍然可能使用客户端头字段进行请求调度。后端服务器 SHOULD 仅在该信息可以限定到单个终端客户端时,基于客户端优先级信息进行调度。认证和其他会话信息可能提供这种可关联性。

13.3. 有意引入不公平性

有时将某个连接的传输优先级降低以优先处理其他连接是有利的,但需要知道这样做会在连接之间以及这些连接上提供的请求之间引入一定程度的不公平性。

例如,服务器可能会在仅传输后台优先级响应(例如软件更新镜像)的连接上使用回收式拥塞控制器。这样可以提高其他连接的响应性,但代价是延迟更新的交付。


14. 为什么使用端到端头字段?

与使用按跳(hop-by-hop)帧的 HTTP/2 优先级方案相反,Priority 头字段被定义为“端到端”。

客户端如何处理响应是与生成该请求的客户端相关的属性,而不是中介的属性。因此,这是一个端到端属性。由 Priority 头字段携带的这些端到端属性如何影响共享连接的响应之间的优先级,是一个按跳处理的问题。

将 Priority 头字段定义为端到端对缓存中介很重要。此类中介可以将 Priority 头字段的值与响应一同缓存,并在提供缓存响应时使用缓存的头字段值,之所以可以这样做,正是因为该头字段被定义为端到端而非按跳。


15. 安全性考虑

第 7 节 介绍了服务器对 PRIORITY_UPDATE 帧进行缓冲时的考虑事项。

第 10 节 给出了示例,说明以某种方式优先处理响应的服务器可能会陷入无法传输响应的饥饿状态。

来自 [STRUCTURED-FIELDS] 的安全性考虑适用于对 第 4 节 中定义的优先级参数的处理。


16. IANA 考量

本规范在 [HTTP/2] 定义的“超文本传输协议(HTTP)字段名注册表”中登记了以下条目:

字段名:
Priority
状态:
permanent
参考:
本文件

本规范在 [HTTP/2] 定义的“HTTP/2 设置”注册表中登记了以下条目:

代码:
0x9
名称:
SETTINGS_NO_RFC7540_PRIORITIES
初始值:
0
参考:
本文件

本规范在 [HTTP/2] 定义的“HTTP/2 帧类型”注册表中登记了以下条目:

代码:
0x10
帧类型:
PRIORITY_UPDATE
参考:
本文件

本规范在由 [HTTP/3] 建立的“HTTP/3 帧类型”注册表中登记了以下条目:

值:
0xF0700-0xF0701
帧类型:
PRIORITY_UPDATE
状态:
permanent
参考:
本文件
变更控制者:
IETF
联系人:
ietf-http-wg@w3.org

IANA 已在 <https://www.iana.org/assignments/http-priority> 创建了“超文本传输协议(HTTP)优先级”注册表,并已使用 表 1 中的条目对其进行了填充;有关其相关程序,请参见 第 4.3.1 节

表 1:初始优先级参数
名称 描述 参考
u HTTP 响应的紧急度。 第 4.1 节
i HTTP 响应是否可以被增量处理。 第 4.2 节

17. 参考文献

17.1. 规范性参考文献

[HTTP]
Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke, Ed., “HTTP Semantics”, STD 97, RFC 9110, DOI 10.17487/RFC9110, 2022 年 6 月, <https://www.rfc-editor.org/info/rfc9110>。
[HTTP/2]
Thomson, M., Ed. and C. Benfield, Ed., “HTTP/2”, RFC 9113, DOI 10.17487/RFC9113, 2022 年 6 月, <https://www.rfc-editor.org/info/rfc9113>。
[HTTP/3]
Bishop, M., Ed., “HTTP/3”, RFC 9114, DOI 10.17487/RFC9114, 2022 年 6 月, <https://www.rfc-editor.org/info/rfc9114>。
[QUIC]
Iyengar, J., Ed. and M. Thomson, Ed., “QUIC: A UDP-Based Multiplexed and Secure Transport”, RFC 9000, DOI 10.17487/RFC9000, 2021 年 5 月, <https://www.rfc-editor.org/info/rfc9000>。
[RFC2119]
Bradner, S., “Key words for use in RFCs to Indicate Requirement Levels”, BCP 14, RFC 2119, DOI 10.17487/RFC2119, 1997 年 3 月, <https://www.rfc-editor.org/info/rfc2119>。
[RFC8126]
Cotton, M., Leiba, B., and T. Narten, “Guidelines for Writing an IANA Considerations Section in RFCs”, BCP 26, RFC 8126, DOI 10.17487/RFC8126, 2017 年 6 月, <https://www.rfc-editor.org/info/rfc8126>。
[RFC8174]
Leiba, B., “Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words”, BCP 14, RFC 8174, DOI 10.17487/RFC8174, 2017 年 5 月, <https://www.rfc-editor.org/info/rfc8174>。
[STRUCTURED-FIELDS]
Nottingham, M. and P-H. Kamp, “Structured Field Values for HTTP”, RFC 8941, DOI 10.17487/RFC8941, 2021 年 2 月, <https://www.rfc-editor.org/info/rfc8941>。

17.2. 信息性参考文献

[CACHING]
Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke, Ed., “HTTP Caching”, STD 98, RFC 9111, DOI 10.17487/RFC9111, 2022 年 6 月, <https://www.rfc-editor.org/info/rfc9111>。
[FORWARDED]
Petersson, A. and M. Nilsson, “Forwarded HTTP Extension”, RFC 7239, DOI 10.17487/RFC7239, 2014 年 6 月, <https://www.rfc-editor.org/info/rfc7239>。
[MARX]
Marx, R., De Decker, T., Quax, P., and W. Lamotte, “Of the Utmost Importance: Resource Prioritization in HTTP/3 over QUIC”, DOI 10.5220/0008191701300143, SCITEPRESS 第 15 届国际 Web 信息系统与技术会议论文集(第 130-143 页), 2019 年 9 月, <https://www.doi.org/10.5220/0008191701300143>。
[PRIORITY-SETTING]
Lassey, B. and L. , “Declaring Support for HTTP/2 Priorities”, 进行中的工作, draft-lassey-priority-setting-00, 2019 年 7 月, <https://datatracker.ietf.org/doc/html/draft-lassey-priority-setting-00>。
[QUIC-RECOVERY]
Iyengar, J., Ed. and I. Swett, Ed., “QUIC Loss Detection and Congestion Control”, RFC 9002, DOI 10.17487/RFC9002, 2021 年 5 月, <https://www.rfc-editor.org/info/rfc9002>。
[RFC7540]
Belshe, M., Peon, R., and M. Thomson, Ed., “Hypertext Transfer Protocol Version 2 (HTTP/2)”, RFC 7540, DOI 10.17487/RFC7540, 2015 年 5 月, <https://www.rfc-editor.org/info/rfc7540>。
[RFC8081]
Lilley, C., “The "font" Top-Level Media Type”, RFC 8081, DOI 10.17487/RFC8081, 2017 年 2 月, <https://www.rfc-editor.org/info/rfc8081>。

致谢

Roy Fielding 在 <https://www.ietf.org/proceedings/83/slides/slides-83-httpbis-5.pdf> 提出了使用头字段表示优先级的想法。在 <https://github.com/pmeenan/http3-prioritization-proposal> 中, Patrick Meenan 主张使用紧急度和并发性的元组来表示优先级。禁用 HTTP/2 优先级的能力受 [PRIORITY-SETTING] 的启发,该草案由 Brad Lassey 和 Lucas Pardue 撰写,本文根据未并入该文档更新的反馈对其进行了修改。

定义替代 HTTP/2 优先级方案的动机来自广泛的 HTTP 社区讨论。特别感谢 Roberto Peon、Martin Thomson 和 Netflix 提供的已明确并入本文的文本。

除上述人员外,本文档还受益于 HTTP 优先级设计团队的广泛讨论,团队成员包括 Alan Frindell、Andrew Galloni、Craig Taylor、Ian Swett、Matthew Cox、Mike Bishop、Roberto Peon、Robin Marx、Roy Fielding 以及本文档的作者。

Yang Chi 为重传调度部分做出了贡献。


作者地址

Kazuho Oku
Fastly
EMail: kazuhooku@gmail.com

附加联系方式:
奥 一穂
Fastly
EMail: kazuhooku@gmail.com
Lucas Pardue
Cloudflare
EMail: lucaspardue.24.7@gmail.com