拥塞窗口 · 在途数据别太多

除了控制发送速率,还要限制「已发出但还没被确认」的在途数据量——这是拥塞窗口。这一章读 goog_cc 的 congestion_window_pushback_controller,讲清在途数据为什么要有上限、超限时怎么「回压」减速,以及它和 pacing、码率控制如何配合防止缓冲区膨胀。

作者 David更新于 第 49 篇(共 112 篇)

拥塞窗口 · 在途数据别太多

Pacing 控制“发多快”,但还有一个正交的约束:“已经发出去、但还没收到确认”的数据(in-flight)不能太多。 这个约束叫拥塞窗口(congestion window),由 goog_cc/congestion_window_pushback_controller.h 实现。它是 GoogCC 防止延迟失控的又一道闸。

在途数据:堆在网络里的量

“在途数据”= 你已经发出、但对端还没通过反馈确认收到的那些包的总量。 它反映“此刻有多少数据正堆在网络的缓冲区里”。哪怕发送速率看着合理,如果在途数据持续堆积(发得比确认回来的快),这些数据就在瓶颈路由器的缓冲区里排长队——这正是 bufferbloat(缓冲区膨胀) 的成因:缓冲区被塞满,每个包都要等前面一长串,延迟飙升。

拥塞窗口给在途数据设一个上限:一旦在途量逼近上限,就别再往里灌了,等确认回来、腾出空间再发。这和 TCP 的拥塞窗口 cwnd 是同一个思想——不光管“速率”,还管“总量”,双重保险地防止把网络缓冲区塞爆。

Pushback:超限就回压

WebRTC 的实现叫 CongestionWindowPushbackController——名字里的 pushback(回压) 点明了它的工作方式:

  • 计算一个目标拥塞窗口(基于估计的带宽和 RTT:带宽 × RTT ≈ “填满这条路”所需的数据量)。
  • 监控当前在途数据量。
  • 在途量超过窗口 → 回压:主动压低实际发送码率(在 pacing 之上再踩一脚刹车),让发送慢下来,直到在途数据回落到窗口内。

“回压”是速率控制之外的一道额外刹车,专治延迟失控。 正常时,延迟based / 丢包based估出的码率 + pacing 已经在管发送。但某些情况下(如网络突然变差、反馈延迟),在途数据可能来不及被限制住而堆积——拥塞窗口作为最后一道闸,一旦发现在途量超标就强行回压减速。它优先保护的是延迟:宁可暂时少发点、画质降一点,也不让缓冲区堆积导致通话延迟暴涨、卡顿。对实时通信,低延迟比高画质更重要——拥塞窗口把这个优先级落到了实处。

三道闸,层层设防

到这里,GoogCC 控制发送的手段已经有三层,各管一个维度:

机制管什么类比
码率估计(delay/loss)平均该发多快定速度上限
Pacing发包的瞬时节奏(别突发)匀速放行
拥塞窗口在途总量(别堆积)限制“路上的车”

三者协同:码率估计定“目标速度”,pacing 保证“匀速吐出”,拥塞窗口兜底“总量不超”。多重、正交的约束,让 GoogCC 在各种刁钻网络下都能稳住延迟——这种“不把鸡蛋放一个篮子”的冗余设计,是实时系统鲁棒性的来源。

小结与下一章

拥塞窗口限制在途(已发未确认)数据量,防 bufferbloat(缓冲区膨胀导致延迟飙升)。CongestionWindowPushbackController 算一个目标窗口(≈带宽 × RTT),在途超限就回压——在 pacing 之上再踩刹车,优先保低延迟。它是码率估计、pacing 之外的第三道闸,三者正交设防。

估计、探测、pacing、窗口都齐了。最后一章把它们收成一个闭环,并看估出的码率怎么变成编码器的指令。👉 码率控制与闭环。

参考资料

  • [1] libwebrtc 源码.modules/congestion_controller/goog_cc/congestion_window_pushback_controller.h。
  • [2] 关联 bufferbloat 与 TCP cwnd —— 在途数据限制的共同思想。
  • [3] IETF「GoogCC」draft。

本页目录