第二道闸:拥塞窗口
滑动窗口篇说过,发送量受 min(rwnd, cwnd) 约束。rwnd 是接收方明示的,cwnd 则是发送方自己心里的账:网络中间的每一跳路由器都可能堵,但没人会通知你。TCP 的办法:用丢包当路况信号——丢包≈堵车,减速;一路平安≈畅通,加速。四个阶段就是四条交规。
四幕剧
cwnd
│ 快恢复后线性爬坡
│ ╱╲ ╱
│ ╱ ╲ ╱ ← 拥塞避免:每 RTT +1 MSS(线性)
│ ╱ ╲╱
│ ╱ 快恢复:cwnd 减半,接着爬
│ ╱ ↑
│ ╱ 快重传(3 个重复 ACK)
│ ╱
│ ╱ ← 慢启动:每 RTT 翻倍(指数)
│ ╱ 到 ssthresh 转入拥塞避免
│ ────╱
└────────────────────────────────────→ 时间
初期 第一次丢包 超时丢包则打回慢启动重来四幕各自的角色:慢启动——从 10 MSS 起步每 RTT 翻倍,尽快摸到网络容量;拥塞避免——过了阈值 ssthresh 后转线性增长,小心翼翼试探;快重传——3 个重复 ACK 立刻补发丢包,不等超时;快恢复——伴随快重传,cwnd 减半而不是清零重来(轻度事故,不必熄火)。若是超时级别的严重丢包,cwnd 打回原点重新慢启动。
把丢包当堵车的代价
传统算法(Reno、Cubic)假设丢包就是拥塞。但这个假设有裂缝:无线网络的丢包多半是信号波动,不是堵车——手机上 TCP 照样刹车,速度上不去。另一个问题是 bufferbloat:现代路由器缓冲区巨大,丢包来得特别晚,期间排队延迟已经飙到几百毫秒——链路满载了,延迟却烂了。经典场景:有人跑满带宽下载,全家人网页都转圈,就是缓冲区被灌满了。
BBR:不问丢包,问带宽和延迟
Google 2016 年的 BBR 换了思路:主动测量瓶颈带宽和最小 RTT,乘积就是最优发送速率,不靠丢包触发。效果是链路利用率高、排队延迟低,YouTube 平均延迟降了 4 成。代价是和传统算法同场竞争时略霸道,内核 4.9 以后一行命令开启:
$ sysctl net.ipv4.tcp_congestion_control
net.ipv4.tcp_congestion_control = cubic # 默认还是 cubic
$ sysctl -w net.ipv4.tcp_congestion_control=bbr
# 服务器对外传输为主、跨地域大文件场景,换 bbr 通常立竿见影实战启示
三条带走:第一,短连接永远在慢启动——连接复用除了省握手,还省了每次从零爬坡的过程;第二,跨地域大文件传输慢,查窗口——RTT 200ms 要跑满 1Gbps 需要 cwnd 约 25000 字节段的在途量,默认配置跑不满,需调 tcp_wmem 上限;第三,别小看每一次丢包——cwnd 减半后要爬很久,弱网下偶发丢包对吞吐的杀伤远比想象大。下一篇讲两个爱攒 batch 的老好人:Nagle 算法和延迟确认,凑在一起就是经典的 40ms 卡顿。
评论 (0)