在分布式系统大行其道的今天,网络往往是整个架构中最不确定的因素。我们经常遇到这样的场景:服务器带宽明明还有余量,但业务传输就是快不起来;或者延迟突然飙升,导致服务超时。

这背后,很可能就是 TCP 拥塞控制在发挥作用。本文将深入探讨这一保障互联网稳定的基石技术,分析从传统算法到现代算法的演进,并分享在Linux系统中的调优经验。

一、流量控制 vs. 拥塞控制:别再搞混了

在深入细节前,必须厘清两个极易混淆的概念:

  • 流量控制:关注点是点对点。它防止“发送方”把“接收方”的缓冲区塞爆。通过TCP头部的Window字段告知对方自己的接收能力。
  • 拥塞控制:关注点是整个网络。它防止“过多的数据”注入到网络中,导致路由器或链路过载。这是本文的主角。

如果把网络比作交通系统:

  • 流量控制是决定高速路口一次放行多少辆车,以免堵住收费站。
  • 拥塞控制是根据前方路况,决定全网范围内限制多少车上路,以免整个高速公路瘫痪。

二、拥塞控制的四大经典阶段

传统的TCP拥塞控制遵循“端到端”的设计原则,网络只负责转发,由发送端通过丢包和延迟来猜测网络状况。整个过程通常分为四个阶段:

  1. 慢启动(Slow Start)
    • 连接初期,发送方不清楚网络负载能力。为了不冲垮网络,它会以一个较小的拥塞窗口开始,每收到一个ACK,窗口就增加一个MSS。
    • 过程:指数级增长(1, 2, 4, 8…)。目的是快速探测带宽上限。
    • 当达到 慢启动阈值 时,进入拥塞避免阶段。
  2. 拥塞避免(Congestion Avoidance)
    • 进入线性增长阶段。每个RTT只增加1个MSS,缓慢试探网络的临界点。
    • 这个过程持续到发生丢包(通常是路由器缓冲区溢出)。
  3. 拥塞发生(Congestion Occurrence)
    • 传统反应:检测到丢包(超时重传或收到重复ACK),TCP会认为网络已拥塞。
    • 它会将慢启动阈值设为当前窗口的一半,并将拥塞窗口重置,重新开始慢启动。这就是经典的“倍减加增”原则。
  4. 快速恢复(Fast Recovery)
    • 当收到3个重复ACK时,TCP认为只是个别包丢失,而非网络彻底中断。它会将阈值设为当前窗口的一半,窗口减半,然后进入拥塞避免,而不是回到慢启动。

三、传统算法的局限:丢包不一定是拥塞

经典算法(如Reno、NewReno)的核心逻辑是:丢包 = 拥塞。这在传统的有线网络(比特误码率极低)中是成立的。

但在现代网络环境下,这一假设开始失灵:

  • 无线网络/移动网络:信号波动导致的丢包(比特误码),并非网络拥塞。但TCP会误判,无脑降低发送速度,导致带宽利用率极低。
  • 深队列问题:为了容忍突发流量,路由器的缓冲区通常很大。当网络即将拥塞时,数据包在缓冲区排队,延迟飙升(Bufferbloat),但并未丢包。传统算法对此视而不见,继续发送,导致延迟高得无法接受。
  • 高速长肥网络:在延迟很高、带宽很大的链路(如跨国传输)上,传统算法需要极长时间才能将窗口线性增长到理想值。

四、现代拥塞控制算法的革新

为了解决上述问题,新一代拥塞控制算法不再单纯依赖丢包,而是引入了延迟(RTT)带宽探测作为决策依据。

1. BBR (Bottleneck Bandwidth and RTT) - Google的颠覆者

BBR 彻底抛弃了“丢包即拥塞”的旧观念。它认为,网络的最佳工作点是当链路既充满数据(高带宽)又不产生排队(低延迟)的时候。

  • 核心思想:交替测量链路的最大带宽最小RTT
  • 工作方式
    1. 启动:快速探测带宽。
    2. 排空:降低发送速率,排空队列,测量真实的最小RTT。
    3. 稳定运行:以估计的带宽和RTT构建数据包发送模型,将整条链路填满,但又不让数据在中间路由器堆积。
  • 优势:在高延迟、高丢包率的链路上,BBR能跑满带宽,且保持极低延迟。

2. CUBIC - Linux 内核的默认选项

CUBIC 是为了解决“高速网络下窗口增长缓慢”而生的。它将窗口增长函数从线性改为立方函数

  • 核心思想:窗口增长只取决于两次拥塞事件的时间差。
  • 优势:无论RTT多大,CUBIC的窗口恢复速度都很快,非常适合高带宽高延迟网络。它是目前Linux的默认算法(截至内核2.6.19及以后)。

3. Vegas / Westwood

  • Vegas:通过比较实际吞吐量和预期吞吐量的差值来判断拥塞,在丢包发生前就主动降低速度,避免拥塞。
  • Westwood:在丢包发生后,不是盲目减半,而是通过监控ACK的到达速率来评估当前的可用带宽,并据此设置新的阈值。

五、Linux 系统调优实战

了解理论后,我们来看看如何在Linux系统中查看和调整拥塞控制算法。

1. 查看当前可用的算法

# 查看当前使用的拥塞控制算法
sysctl net.ipv4.tcp_congestion_control

# 或者
cat /proc/sys/net/ipv4/tcp_congestion_control

# 查看内核支持的算法
sysctl net.ipv4.tcp_available_congestion_control

2. 修改拥塞控制算法

如果内核编译了相关模块(如BBR),可以临时修改:

# 临时修改为 bbr
echo "bbr" > /proc/sys/net/ipv4/tcp_congestion_control

# 或者使用 sysctl
sysctl -w net.ipv4.tcp_congestion_control=bbr

如果需要永久生效,在 /etc/sysctl.conf 中添加:

net.ipv4.tcp_congestion_control = bbr

3. 启用BBR(以CentOS 7/8 或 Ubuntu 18.04+为例)

现代内核(4.9+)通常已经包含了BBR模块。

# 检查内核版本
uname -r

# 如果内核支持,加载BBR模块
modprobe tcp_bbr

# 确保模块开机自动加载
echo "tcp_bbr" >> /etc/modules-load.d/modules.conf

# 设置拥塞控制算法
echo "net.ipv4.tcp_congestion_control = bbr" >> /etc/sysctl.conf
sysctl -p

4. 监控和调优指标

配置完成后,可以通过ssnstat观察TCP重传率:

# 查看TCP重传统计
nstat -az | grep -i retrans

# 使用 ss 查看当前连接状态
ss -i

ss -i 的输出中,可以看到每个连接的拥塞窗口大小、RTT等信息。

六、如何选择算法?

  • 通用服务器(IDC机房,低丢包率)CUBIC 是成熟稳定的选择,兼容性最好。
  • 跨国传输、卫星链路(高带宽高延迟)BBR 能显著提升吞吐量,降低延迟。
  • 无线网络(移动APP服务端):可以考虑 WestwoodBBR,它们对随机丢包不敏感。
  • 低延迟内部网络(数据中心):有些场景会使用 DCTCP(数据中心TCP),它对延迟极其敏感,需要交换机的配合。

结语

网络的拥塞控制是一个博大精深的领域,从基于丢包的“蛮力控制”演进到基于模型的“精准控制”,体现了我们对网络传输本质理解的深化。

对于开发和运维人员来说,理解这些算法不仅能帮助我们诊断“带宽没跑满”、“延迟忽高忽低”的诡异问题,更是我们在关键时刻调整系统参数、榨干硬件性能的理论基础。你的服务器用的是什么拥塞控制算法?不妨现在就去检查一下。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注