4. 流量控制
4. 流量控制 (Flow Control)
receiver 需要限制其必须缓冲的数据量, 以防止快速 sender 压垮它, 或恶意 sender 消耗大量内存. 为了使 receiver 能够限制 connection 的内存占用, stream 会被单独进行 flow controlled, 也会在整个 connection 范围内进行 flow controlled. 如第 4.1 节和第 4.2 节所述, QUIC receiver 控制 sender 在某个 stream 上以及所有 stream 上任意时刻可以发送的最大数据量.
类似地, 为了限制 connection 内的并发性, QUIC endpoint 控制其 peer 能够发起的最大累计 stream 数量, 如第 4.6 节所述.
CRYPTO frame 中发送的数据不会像 stream 数据那样进行 flow controlled. QUIC 依赖加密协议实现来避免过度缓冲数据; 见 [QUIC-TLS]. 为了避免多个层次上的过度缓冲, QUIC 实现 SHOULD 为加密协议实现提供接口, 以传达其 buffering limit.
4.1 数据流量控制 (Data Flow Control)
QUIC 采用基于 limit 的 flow control 方案, 其中 receiver 会通告它准备在给定 stream 或整个 connection 上接收的总字节数 limit. 这使 QUIC 中存在两个层次的数据 flow control:
-
Stream flow control, 通过限制每个 stream 上可发送的数据量, 防止单个 stream 消耗 connection 的整个 receive buffer.
-
Connection flow control, 通过限制所有 stream 上 STREAM frame 中发送的 stream 数据总字节数, 防止 sender 超过 receiver 为该 connection 准备的 buffer capacity.
sender MUST NOT 发送超过任一 limit 的数据.
receiver 在 handshake (第 7.4 节) 期间通过 transport parameter 为所有 stream 设置初始 limit. 此后, receiver 向 sender 发送 MAX_STREAM_DATA frame (第 19.10 节) 或 MAX_DATA frame (第 19.9 节), 以通告更大的 limit.
receiver 可以通过发送带有相应 stream ID 的 MAX_STREAM_DATA frame, 为某个 stream 通告更大的 limit. MAX_STREAM_DATA frame 指示 stream 的最大绝对 byte offset. receiver 可以根据该 stream 上已消耗数据的当前 offset 来确定要通告的 flow control offset.
receiver 可以通过发送 MAX_DATA frame 为 connection 通告更大的 limit, 该 frame 指示所有 stream 的绝对 byte offset 之和的最大值. receiver 维护在所有 stream 上收到的字节累计和, 用于检查是否违反已通告的 connection 或 stream 数据 limit. receiver 可以根据所有 stream 上已消耗字节之和来确定要通告的 maximum data limit.
一旦 receiver 为 connection 或 stream 通告了 limit, 再通告更小的 limit 并不是 error, 但较小的 limit 不会产生效果.
如果 sender 违反已通告的 connection 或 stream 数据 limit, receiver MUST 以 FLOW_CONTROL_ERROR 类型的 error 关闭 connection; 有关 error handling 的细节见第 11 节.
sender MUST 忽略任何不会增加 flow control limit 的 MAX_STREAM_DATA 或 MAX_DATA frame.
如果 sender 已经发送数据到达 limit, 它将无法发送新数据, 并被视为 blocked. sender SHOULD 发送 STREAM_DATA_BLOCKED 或 DATA_BLOCKED frame, 向 receiver 表明它有数据要写入, 但受 flow control limit 阻塞. 如果 sender 被阻塞的时间超过 idle timeout (第 10.1 节), 即使 sender 有可用于传输的数据, receiver 也可能关闭 connection. 为防止 connection 关闭, 受 flow control 限制的 sender 在没有 ack-eliciting packet 在途时, SHOULD 定期发送 STREAM_DATA_BLOCKED 或 DATA_BLOCKED frame.
4.2 提高流量控制限制 (Increasing Flow Control Limits)
实现自行决定在 MAX_STREAM_DATA 和 MAX_DATA frame 中何时以及通告多少 credit, 但本节提供若干考虑因素.
为避免阻塞 sender, receiver MAY 在一个 round trip 内多次发送 MAX_STREAM_DATA 或 MAX_DATA frame, 或者足够早地发送, 以便为该 frame 丢失及后续恢复留出时间.
control frame 会增加 connection 开销. 因此, 频繁发送仅有小幅变化的 MAX_STREAM_DATA 和 MAX_DATA frame 是不可取的. 另一方面, 如果更新不那么频繁, 就需要用更大的 limit 增量来避免阻塞 sender, 从而要求 receiver 承诺更多资源. 在确定通告多大的 limit 时, 需要在资源承诺和开销之间进行权衡.
receiver 可以使用 autotuning 机制, 根据 round-trip time 估计值以及接收应用消耗数据的速率, 调整通告额外 credit 的频率和数量, 类似常见 TCP 实现. 作为优化, endpoint 可以只在有其他 frame 要发送时发送与 flow control 相关的 frame, 确保 flow control 不会导致额外 packet 被发送.
blocked sender 不需要发送 STREAM_DATA_BLOCKED 或 DATA_BLOCKED frame. 因此, receiver MUST NOT 在发送 MAX_STREAM_DATA 或 MAX_DATA frame 之前等待 STREAM_DATA_BLOCKED 或 DATA_BLOCKED frame; 这样做可能导致 sender 在 connection 的剩余生命周期内一直被阻塞. 即使 sender 发送这些 frame, 等待它们也会导致 sender 至少被阻塞整整一个 round trip.
当 sender 在被阻塞后收到 credit 时, 它可能能够响应性地发送大量数据, 从而造成短期 congestion; 有关 sender 如何避免此类 congestion 的讨论, 见 [QUIC-RECOVERY] 第 7.7 节.
4.3 流量控制性能 (Flow Control Performance)
如果 endpoint 无法确保其 peer 始终拥有大于该 connection 的 bandwidth-delay product 的可用 flow control credit, 则它的接收吞吐量将受到 flow control 限制.
packet loss 可能导致 receive buffer 中出现空洞, 从而阻止应用消耗数据并释放 receive buffer 空间.
及时发送 flow control limit 更新可以提升性能. 仅为了提供 flow control 更新而发送 packet 会增加网络负载并对性能产生不利影响. 将 flow control 更新与其他 frame 一起发送, 例如 ACK frame, 可以降低这些更新的成本.
4.4 处理流取消 (Handling Stream Cancellation)
endpoint 最终需要就每个 stream 已消耗的 flow control credit 数量达成一致, 以便能够为 connection-level flow control 统计所有字节.
收到 RESET_STREAM frame 后, endpoint 会拆除匹配 stream 的状态, 并忽略随后在该 stream 上到达的数据.
RESET_STREAM 会 abrupt termination 某个 stream 的一个方向. 对于 bidirectional stream, RESET_STREAM 对相反方向的数据流没有影响. 两个 endpoint MUST 维护该 stream 未终止方向的 flow control 状态, 直到该方向进入 terminal state.
4.5 流最终大小 (Stream Final Size)
final size 是某个 stream 消耗的 flow control credit 数量. 假定 stream 上每个连续字节都发送一次, final size 就是发送的字节数. 更一般地说, 它是该 stream 上所发送的最大 offset 字节的 offset 加一; 如果没有发送任何字节, 则为零.
无论 stream 如何终止, sender 总会可靠地向 receiver 传达 stream 的 final size. final size 是带 FIN flag 的 STREAM frame 的 Offset 和 Length 字段之和, 注意这些字段可能是隐式的. 或者, RESET_STREAM frame 的 Final Size 字段承载此值. 这保证两个 endpoint 就 sender 在该 stream 上消耗了多少 flow control credit 达成一致.
当 stream 的接收部分进入 "Size Known" 或 "Reset Recvd" 状态 (第 3 节) 时, endpoint 将知道该 stream 的 final size. receiver MUST 使用 stream 的 final size, 在其 connection-level flow controller 中统计该 stream 上发送的所有字节.
endpoint MUST NOT 在 final size 处或超过 final size 的位置向 stream 发送数据.
一旦某个 stream 的 final size 已知, 它就不能改变. 如果收到的 RESET_STREAM 或 STREAM frame 指示该 stream 的 final size 发生变化, endpoint SHOULD 以 FINAL_SIZE_ERROR 类型的 error 响应; 有关 error handling 的细节见第 11 节. receiver SHOULD 将在 final size 处或超过 final size 的位置收到数据视为 FINAL_SIZE_ERROR 类型的 error, 即使 stream 已关闭也是如此. 生成这些 error 并非强制要求, 因为要求 endpoint 生成这些 error 也意味着 endpoint 需要为已关闭 stream 维护 final size 状态, 这可能意味着显著的状态承诺.
4.6 控制并发 (Controlling Concurrency)
endpoint 会限制 peer 可打开的传入 stream 的累计数量. 只有 stream ID 小于 (max_streams * 4 + first_stream_id_of_type) 的 stream 才能被打开; 见表 1. 初始 limit 在 transport parameter 中设置; 见第 18.2 节. 后续 limit 使用 MAX_STREAMS frame 通告; 见第 19.11 节. unidirectional stream 和 bidirectional stream 分别适用不同 limit.
如果收到的 max_streams transport parameter 或 MAX_STREAMS frame 的值大于 2^60, 这将允许一个无法表示为 variable-length integer 的最大 stream ID; 见第 16 节. 如果收到其中任一值, connection MUST 立即关闭: 如果违规值是在 transport parameter 中收到的, 使用 TRANSPORT_PARAMETER_ERROR 类型的 connection error; 如果是在 frame 中收到的, 使用 FRAME_ENCODING_ERROR 类型的 connection error; 见第 10.2 节.
endpoint MUST NOT 超过其 peer 设置的 limit. endpoint 如果收到 stream ID 超过其已发送 limit 的 frame, MUST 将其视为 STREAM_LIMIT_ERROR 类型的 connection error; 有关 error handling 的细节见第 11 节.
一旦 receiver 使用 MAX_STREAMS frame 通告 stream limit, 再通告更小的 limit 不会产生效果. 不会增加 stream limit 的 MAX_STREAMS frame MUST 被忽略.
与 stream 和 connection flow control 一样, 本文档将何时以及通过 MAX_STREAMS 向 peer 通告多少 stream 的决定留给实现. 实现可以选择在 stream 关闭时提高 limit, 以使 peer 可用的 stream 数量大致保持一致.
由于 peer 的 limit 而无法打开新 stream 的 endpoint SHOULD 发送 STREAMS_BLOCKED frame (第 19.14 节). 该信号被认为有助于 debugging. endpoint MUST NOT 在通告额外 credit 前等待收到此信号, 因为这样做意味着 peer 至少会被阻塞整整一个 round trip, 如果 peer 选择不发送 STREAMS_BLOCKED frame, 则可能无限期阻塞.