Skip to main content

7. 拥塞控制 (Congestion Control)

本文档为 QUIC 指定了一个类似于 TCP NewReno [RFC6582] 的发送方拥塞控制器。

QUIC 为拥塞控制提供的信号是通用的,旨在支持不同的发送方算法。发送方可以单方面选择使用不同的算法,例如 CUBIC [RFC8312]。

如果发送方使用与本文档中指定的控制器不同的控制器,则所选控制器必须 (MUST) 符合 [RFC8085] 的第 3.1 节中指定的拥塞控制准则。

与 TCP 类似,仅包含 ACK 帧的包不计入在途字节 (bytes in flight) 并且不受拥塞控制。与 TCP 不同,QUIC 可以检测这些包的丢失,并可以 (MAY) 使用该信息来调整拥塞控制器或仅 ACK 包的发送速率,但本文档不描述这样做的机制。

拥塞控制器是按路径 (per path) 的,因此在其他路径上发送的包不会改变当前路径的拥塞控制器,如 [QUIC-TRANSPORT] 的第 9.4 节所述。

本文档中的算法以字节为单位指定并使用控制器的拥塞窗口。

端点不得 (MUST NOT) 发送会导致 bytes_in_flight(参见附录 B.2)大于拥塞窗口的包,除非该包是在 PTO 计时器到期时发送的(参见第 6.2 节)或在进入恢复期时发送的(参见第 7.3.2 节)。


7.1. 显式拥塞通知 (Explicit Congestion Notification)

如果路径已验证支持显式拥塞通知 (ECN) [RFC3168] [RFC8311],QUIC 将 IP 头中的拥塞经历 (Congestion Experienced, CE) 码点视为拥塞信号。本文档指定了当对等方报告的 ECN-CE 计数增加时端点的响应;参见 [QUIC-TRANSPORT] 的第 13.4.2 节。


7.2. 初始和最小拥塞窗口 (Initial and Minimum Congestion Window)

QUIC 在慢启动 (slow start) 中开始每个连接,并将拥塞窗口设置为初始值。端点应 (SHOULD) 使用最大数据报大小 (max_datagram_size) 的十倍作为初始拥塞窗口,同时将窗口限制为 14,720 字节或最大数据报大小的两倍中的较大值。这遵循 [RFC6928] 中的分析和建议,增加了字节限制以考虑 UDP 的 8 字节开销小于 TCP 的 20 字节开销。

如果在连接期间最大数据报大小发生变化,则应 (SHOULD) 使用新大小重新计算初始拥塞窗口。如果为了完成握手而减小最大数据报大小,则拥塞窗口应 (SHOULD) 设置为新的初始拥塞窗口。

在验证客户端地址之前,服务器可能会受到 [QUIC-TRANSPORT] 的第 8.1 节中指定的反放大限制的进一步限制。尽管反放大限制可以阻止拥塞窗口被完全利用,从而减慢拥塞窗口的增长,但它不会直接影响拥塞窗口。

最小拥塞窗口是拥塞窗口响应丢包、对等方报告的 ECN-CE 计数增加或持续拥塞 (persistent congestion) 而可以达到的最小值。推荐 (RECOMMENDED) 值为 2 * max_datagram_size


7.3. 拥塞控制状态 (Congestion Control States)

本文档中描述的 NewReno 拥塞控制器具有三个不同的状态,如图 1 所示。

       新路径或                +------------+
持续拥塞 | 慢启动 |
(O)---------------------->| |
+------------+
|
丢包或
ECN-CE 增加
|
v
+------------+ 丢包或 +------------+
| 拥塞避免 | ECN-CE 增加 | 恢复期 |
| |------------------>| |
+------------+ +------------+
^ |
| |
+----------------------------+
确认恢复期内发送的包

图 1:拥塞控制状态和转换

这些状态及其之间的转换在后续章节中描述。

7.3.1. 慢启动 (Slow Start)

当拥塞窗口低于慢启动阈值时,NewReno 发送方处于慢启动状态。发送方从慢启动开始,因为慢启动阈值被初始化为无限值。

当发送方处于慢启动状态时,拥塞窗口在处理每个确认时增加已确认字节数。这导致拥塞窗口呈指数增长。

当包丢失或其对等方报告的 ECN-CE 计数增加时,发送方必须 (MUST) 退出慢启动并进入恢复期。

当拥塞窗口小于慢启动阈值时,发送方重新进入慢启动,这仅在声明持续拥塞后才会发生。

7.3.2. 恢复 (Recovery)

当 NewReno 发送方检测到包丢失或其对等方报告的 ECN-CE 计数增加时,它进入恢复期。已经处于恢复期的发送方保持在其中并且不会重新进入。

在进入恢复期时,发送方必须 (MUST) 将慢启动阈值设置为检测到丢失时拥塞窗口值的一半。在退出恢复期之前,拥塞窗口必须 (MUST) 设置为慢启动阈值的减小值。

实现可以 (MAY) 在进入恢复期时立即减小拥塞窗口,或使用其他机制(例如比例速率减少 (Proportional Rate Reduction) [PRR])来更渐进地减小拥塞窗口。如果拥塞窗口立即减小,则可以在减小之前发送单个包。这加快了丢包恢复速度,如果丢失包中的数据被重传,并且类似于 [RFC6675] 的第 5 节中描述的 TCP。

恢复期旨在将拥塞窗口减小限制为每往返时间一次。因此,在恢复期间,拥塞窗口不会响应新的丢包或 ECN-CE 计数的增加而改变。

当在恢复期间发送的包被确认时,恢复期结束,发送方进入拥塞避免。这与 TCP 的恢复定义略有不同,TCP 的恢复在启动恢复的丢失段被确认时结束 [RFC5681]。

7.3.3. 拥塞避免 (Congestion Avoidance)

当拥塞窗口等于或大于慢启动阈值并且不处于恢复期时,NewReno 发送方处于拥塞避免状态。

拥塞避免中的发送方使用加性增加乘性减少 (Additive Increase Multiplicative Decrease, AIMD) 方法,该方法必须 (MUST) 将拥塞窗口的增加限制为每确认一个拥塞窗口最多增加一个最大数据报大小。

当包丢失或其对等方报告的 ECN-CE 计数增加时,发送方退出拥塞避免并进入恢复期。


7.4. 忽略不可解密包的丢失 (Ignoring Loss of Undecryptable Packets)

在握手期间,当包到达时某些包保护密钥可能不可用,接收方可以选择丢弃该包。特别是,在 Initial 包到达之前无法处理 Handshake 和 0-RTT 包,并且在握手完成之前无法处理 1-RTT 包。端点可以 (MAY) 忽略可能在对等方拥有包保护密钥来处理这些包之前到达的 Handshake、0-RTT 和 1-RTT 包的丢失。端点不得 (MUST NOT) 忽略在给定包编号空间中最早确认包之后发送的包的丢失。


7.5. 探测超时 (Probe Timeout)

探测包不得 (MUST NOT) 被拥塞控制器阻塞。然而,发送方必须 (MUST) 将这些包计为额外在途,因为这些包在不建立包丢失的情况下增加了网络负载。请注意,发送探测包可能会导致发送方的在途字节超过拥塞窗口,直到收到确认建立丢失或交付包的确认。


7.6. 持续拥塞 (Persistent Congestion)

当发送方确定在足够长的持续时间内发送的所有包都丢失时,网络被认为正在经历持续拥塞 (persistent congestion)。

7.6.1. 持续时间 (Duration)

持续拥塞持续时间计算如下:

(smoothed_rtt + max(4*rttvar, kGranularity) + max_ack_delay) * kPersistentCongestionThreshold

与第 6.2 节中的 PTO 计算不同,此持续时间包括 max_ack_delay,无论在哪些包编号空间中确定丢包。

此持续时间允许发送方在确定持续拥塞之前发送尽可能多的包,包括一些响应 PTO 到期的包,就像 TCP 使用尾部丢包探测 (Tail Loss Probes) [RFC8985] 和 RTO [RFC5681] 一样。

kPersistentCongestionThreshold 的较大值会导致发送方对网络中的持续拥塞响应较慢,这可能导致向拥塞网络激进发送。过小的值可能导致发送方不必要地声明持续拥塞,从而降低发送方的吞吐量。

kPersistentCongestionThreshold 的推荐 (RECOMMENDED) 值为 3,这导致的行为大致相当于 TCP 发送方在两次 TLP 后声明 RTO。

此设计不使用连续的 PTO 事件来确定持续拥塞,因为应用程序模式会影响 PTO 到期。例如,在它们之间有静默期发送少量数据的发送方每次发送时都会重新启动 PTO 计时器,即使没有收到确认,也可能在很长一段时间内阻止 PTO 计时器到期。使用持续时间使发送方能够在不依赖 PTO 到期的情况下确定持续拥塞。

7.6.2. 确定持续拥塞 (Establishing Persistent Congestion)

如果在收到确认后,两个确认触发 (ack-eliciting) 的包被声明丢失,并且满足以下条件,则发送方确定持续拥塞:

  • 在所有包编号空间中,在这两个包的发送时间之间发送的包都没有被确认;

  • 这两个包的发送时间之间的持续时间超过持续拥塞持续时间(第 7.6.1 节);并且

  • 在发送这两个包时存在先前的 RTT 样本。

这两个包必须 (MUST) 是确认触发的,因为接收方需要在其最大确认延迟内确认仅确认触发包;参见 [QUIC-TRANSPORT] 的第 13.2 节。

持续拥塞期不应 (SHOULD NOT) 在至少有一个 RTT 样本之前开始。在第一个 RTT 样本之前,发送方根据初始 RTT(第 6.2.2 节)设置其 PTO 计时器,该 RTT 可能大大大于实际 RTT。要求先前的 RTT 样本可以防止发送方使用可能太少的探测来确定持续拥塞。

由于网络拥塞不受包编号空间的影响,持续拥塞应 (SHOULD) 考虑跨包编号空间发送的包。没有所有包编号空间状态的发送方或无法跨包编号空间比较发送时间的实现可以 (MAY) 仅使用刚刚被确认的包编号空间的状态。这可能导致错误地声明持续拥塞,但不会导致无法检测持续拥塞。

当声明持续拥塞时,发送方的拥塞窗口必须 (MUST) 减少到最小拥塞窗口 (kMinimumWindow),类似于 TCP 发送方在 RTO 上的响应 [RFC5681]。

7.6.3. 示例 (Example)

以下示例说明了发送方如何确定持续拥塞。假设:

smoothed_rtt + max(4*rttvar, kGranularity) + max_ack_delay = 2
kPersistentCongestionThreshold = 3

考虑以下事件序列:

时间操作
t=0发送包 #1(应用数据)
t=1发送包 #2(应用数据)
t=1.2收到 #1 的确认
t=2发送包 #3(应用数据)
t=3发送包 #4(应用数据)
t=4发送包 #5(应用数据)
t=5发送包 #6(应用数据)
t=6发送包 #7(应用数据)
t=8发送包 #8(PTO 1)
t=12发送包 #9(PTO 2)
t=12.2收到 #9 的确认

当在 t = 12.2 收到包 9 的确认时,包 2 到 8 被声明丢失。

拥塞期计算为最旧和最新丢失包之间的时间:8 - 1 = 7。持续拥塞持续时间为 2 * 3 = 6。因为达到了阈值,并且在最旧和最新丢失包之间没有包被确认,所以网络被认为经历了持续拥塞。

虽然此示例显示了 PTO 到期,但它们不是确定持续拥塞所必需的。


7.7. 步调控制 (Pacing)

发送方应 (SHOULD) 基于拥塞控制器的输入来控制所有在途包的发送步调。

在不延迟的情况下将多个包发送到网络中会创建包突发,可能导致短期拥塞和丢包。发送方必须 (MUST) 使用步调控制或限制此类突发。发送方应 (SHOULD) 将突发限制为初始拥塞窗口;参见第 7.2 节。知道到接收方的网络路径可以吸收更大突发的发送方可以 (MAY) 使用更高的限制。

实现应该注意架构其拥塞控制器以与步调器配合良好。例如,步调器可能包装拥塞控制器并控制拥塞窗口的可用性,或者步调器可能控制由拥塞控制器交给它的包。

及时交付 ACK 帧对于高效的丢包恢复很重要。为了避免延迟它们向对等方的交付,因此不应 (SHOULD NOT) 对仅包含 ACK 帧的包进行步调控制。

端点可以按其选择实现步调控制。完美步调的发送方在时间上完全均匀地分布包。对于基于窗口的拥塞控制器(例如本文档中的控制器),该速率可以通过在 RTT 上平均拥塞窗口来计算。以每时间字节数为单位表示速率,其中 congestion_window 以字节为单位:

rate = N * congestion_window / smoothed_rtt

或表示为以时间为单位的包间间隔:

interval = (smoothed_rtt * packet_size / congestion_window) / N

对 N 使用一个小但至少为 1 的值(例如 1.25)可确保 RTT 的变化不会导致拥塞窗口利用不足。

实际考虑因素(例如分组化、调度延迟和计算效率)可能导致发送方在远短于 RTT 的时间段内偏离此速率。

步调控制的一种可能实现策略使用漏桶算法,其中"桶"的容量限制为最大突发大小,"桶"填充的速率由上述函数确定。


7.8. 拥塞窗口利用不足 (Underutilizing the Congestion Window)

当在途字节小于拥塞窗口并且发送不受步调限制时,拥塞窗口未被充分利用。这可能是由于应用程序数据不足或流量控制限制造成的。发生这种情况时,在慢启动或拥塞避免中,拥塞窗口不应 (SHOULD NOT) 增加。

对包进行步调控制的发送方(参见第 7.7 节)可能会延迟发送包,而不会由于此延迟而完全利用拥塞窗口。如果发送方没有步调延迟就会完全利用拥塞窗口,则发送方不应 (SHOULD NOT) 认为自己受应用程序限制。

发送方可以 (MAY) 在利用不足期后实现替代机制来更新其拥塞窗口,例如为 TCP 提出的那些机制 [RFC7661]。