跨境 TCP 传输优化实录 — 使用 BBR 解决 LFN 问题

背景

公司近期开通了一条访问美国机房的 1G 专线,并基于 TCP 建立了一套数据传输服务。上线后发现一个严重的问题:应用程序发送队列中的数据大量积压,最终导致程序 OOM Kill,但观察监控发现专线带宽利用率只有 50% - 60%。

经过沟通,发现运维同事当时使用 iperf3 测试专线带宽使用的是 UDP 协议,于是在运维同事协助下使用 TCP 进行二次测试,发现了比较严重的问题:

  • 在国内机房使用 iperf3 测试单个 socket 流量,在同机房内部(1G交换机)可以达到的最大带宽约为 110Mb(对照组)
  • 在跨境专线使用 iperf3 测试单个 socket 流量,发现极不稳定
Connecting to host US_NY_VM_01, port 5001
[  4] local HK_QW_VM_01 port 58341 connected to US_NY_VM_01 port 5001
[ ID] Interval           Transfer     Bandwidth       Retr  Cwnd
[  4]   0.00-1.00   sec   109 KBytes   892 Kbits/sec    0   42.4 KBytes       
[  4]   1.00-2.00   sec  1.04 MBytes  8.75 Mbits/sec    0    324 KBytes       
[  4]   2.00-3.00   sec  5.45 MBytes  45.7 Mbits/sec    0   1.52 MBytes       
[  4]   3.00-4.00   sec  7.50 MBytes  62.9 Mbits/sec    6   1.88 MBytes       
[  4]   4.00-5.00   sec  10.0 MBytes  83.9 Mbits/sec    0   2.10 MBytes       
[  4]   5.00-6.00   sec  11.2 MBytes  94.4 Mbits/sec    0   2.27 MBytes       
[  4]   6.00-7.00   sec  8.75 MBytes  73.4 Mbits/sec    0   2.38 MBytes       
[  4]   7.00-8.00   sec  12.5 MBytes   105 Mbits/sec    0   2.48 MBytes       
[  4]   8.00-9.00   sec  12.5 MBytes   105 Mbits/sec    0   2.56 MBytes       
[  4]   9.00-10.00  sec  11.2 MBytes  94.4 Mbits/sec    0   2.62 MBytes       
[  4]  10.00-11.00  sec  11.2 MBytes  94.4 Mbits/sec    0   2.65 MBytes       
[  4]  11.00-12.00  sec  12.5 MBytes   105 Mbits/sec    3   1.93 MBytes       
[  4]  12.00-13.00  sec  7.50 MBytes  62.9 Mbits/sec   92   1.42 MBytes       
[  4]  13.00-14.00  sec  6.23 MBytes  52.3 Mbits/sec    0   1.51 MBytes       
[  4]  14.00-15.00  sec  7.50 MBytes  62.9 Mbits/sec    0   1.58 MBytes       
[  4]  15.00-16.00  sec  8.75 MBytes  73.4 Mbits/sec    0   1.63 MBytes       
[  4]  16.00-17.00  sec  6.25 MBytes  52.4 Mbits/sec    0   1.66 MBytes       
[  4]  17.00-18.00  sec  8.75 MBytes  73.4 Mbits/sec    0   1.68 MBytes       
[  4]  18.00-19.00  sec  7.50 MBytes  62.9 Mbits/sec    0   1.69 MBytes       
[  4]  19.00-20.00  sec  8.75 MBytes  73.4 Mbits/sec    0   1.69 MBytes       
[  4]  20.00-21.00  sec  6.25 MBytes  52.4 Mbits/sec    0   1.69 MBytes       
[  4]  21.00-22.00  sec  8.75 MBytes  73.4 Mbits/sec    0   1.69 MBytes       
[  4]  22.00-23.00  sec  8.75 MBytes  73.4 Mbits/sec    0   1.70 MBytes       
[  4]  23.00-24.00  sec  6.25 MBytes  52.4 Mbits/sec    0   1.71 MBytes       
[  4]  24.00-25.00  sec  8.75 MBytes  73.4 Mbits/sec    0   1.73 MBytes       
[  4]  25.00-26.00  sec  8.75 MBytes  73.4 Mbits/sec    0   1.77 MBytes       
[  4]  26.00-27.00  sec  8.75 MBytes  73.4 Mbits/sec    0   1.82 MBytes       
[  4]  27.00-28.00  sec  7.50 MBytes  62.9 Mbits/sec    0   1.88 MBytes       
[  4]  28.00-29.00  sec  10.0 MBytes  83.9 Mbits/sec    0   1.99 MBytes       
[  4]  29.00-30.00  sec  10.0 MBytes  83.9 Mbits/sec    0   2.12 MBytes       
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bandwidth       Retr
[  4]   0.00-30.00  sec   249 MBytes  69.6 Mbits/sec  101             sender
[  4]   0.00-30.00  sec   248 MBytes  69.3 Mbits/sec                  receiver

iperf Done.

从上图可以观察到存在以下问题:

  1. 网络状况不稳定,低速率时也会偶尔会出现 TCP 重传
  2. 传输速率波动较大,无法长时间维持在最佳的 105M 带宽,导致带宽利用率低

长肥管道 (LFN)

跨境专线具有较长的往返时间RTT与较高带宽 Bandwidth,这类具有大管道容量 BDP = RTT * Bandwidth 的网络我们称之为长肥管道LFN (Long Fat Networks)

LFN 的 RTT 较长,因此一个包从发送到接收到 ACK 需要经历的时间比普通的网络更长。由于 TCP 滑动窗口的特性,网络会存在较长的空闲时间,导致网络的利用率不高。这篇文章中的动画很好的展示了这一点,推荐观看。因此一个简单的方案就是:增大在途未确认数据量amount inflight,使其填满整个管道

决定在途数据量的因素有以下几个方面:

  • 发送端:拥塞窗口大小 cwnd 以及 发送缓冲大小
  • 接收端:接收窗口大小 rwnd 以及 接收缓冲大小

由于 cwndrwnd 大小是系统根据网络状况进行自适应调整的,无法无法直接干预。因此决定先尝试调整 TCP 缓冲配置,观察传输效果是否有提升。

TCP 缓冲调参

首先计算管道容量:已知专线带宽为 1Gb,通过 ping 查看专线 RTT 约为 210ms,据此 计算专线 BDP 约为 25MB。

做过 TCP 开发的同学应该都熟悉 SO_RECVBUFSO_SENDBUF 这两个 socekt 选项,我们可以通过这两个参数来设置接收与发送缓冲区的大小。如果我们在建立连接 TCP 时指定了这两个参数,那么操作系统就会使用一个固定的缓冲区大小,而不再会根据网络进行动态调整,因此这两个选项要慎用。

然而当指定参数过后,会发现实际的 TCP 缓冲区大小与参数有所出入。这是什么原因造成的呢?首先来看几个重要的内核参数:

[lhop@localhost ~]$ sysctl -a  2>&1 | egrep 'core.wmem_max|core.rmem_max|ipv4.tcp_wmem|ipv4.tcp_rmem|tcp_adv_win_scale|tcp_moderate_rcvbuf'
net.core.wmem_max = 124928
net.core.rmem_max = 124928
net.ipv4.tcp_wmem = 4096	16384	4194304
net.ipv4.tcp_rmem = 4096	87380	4194304
net.ipv4.tcp_adv_win_scale = 2
net.ipv4.tcp_moderate_rcvbuf = 1
  • net.ipv4.tcp_wmemnet.ipv4.tcp_rmem 是单个 tcp socket 缓冲区的最小值min、默认值default、最大值max,单位为字节。

  • net.core.wmem_maxnet.core.rmem_max 是单个 socket 所能使用的缓冲区大小上限,单位为字节。该参数的优先级高于 tcp_wmemtcp_rmem 的最大值,调整参数时需要注意两者的对应关系。

  • net.ipv4.tcp_moderate_rcvbuf 是否允许操作系统动态调整 tcp 缓冲。开启后,系统会根据当前可用资源对接收缓冲进行动态调整,此时接收缓冲的大小会在 tcp_rmem 最大与最小值之间浮动。当 tcp socket 连接较多时,可以系统会酌情减少每个连接的缓存内存,避免资源耗尽。

  • net.ipv4.tcp_adv_win_scale 是单个 tcp socket 接收缓冲预留给应用的比例。tcp 的接收缓冲可以分为两部分,一部分是用作接收窗口保存未确认报文,另一部分则是缓存未被应用程序读取的已确认报文,因此需要预留 1/2tcp_adv_win_scale 内存空间给未读报文。这也是 tcp_rmem 的初始默认值比 tcp_wmem 大的原因。

根据 sysctl 给出的结果,我们需要调整有:

  • net.core.wmem_max = BDP = 26214400
  • net.core.rmem_max = BDP/(1-1/2tcp_adv_win_scale) = 34952000
[lhop@localhost ~]$ cat <<EOF>> /etc/sysctl.conf
net.core.wmem_max = 26214400
net.core.rmem_max = 34952000
net.ipv4.tcp_wmem = 4096 87380 26214400
net.ipv4.tcp_rmem = 4096 87380 34952000
EOF
sysctl -p

调整后重新使用 ipref3 进行测试,发现测试结果基本上没有变化。基本可以断定:缓存不足不是造成 TCP 流量瓶颈的主因。

从前面的 iperf3 结果中可以看出,当出现重新传时,拥塞窗口急剧缩小,最终导致了传输速度的下降。决定拥塞窗口大小的就是拥塞控制算法,因此我们将目光转移到拥塞算法上。

拥塞控制

TCP拥塞控制算法的目的可以简单概括为:公平效率

  • 当网络拥塞时,TCP 连接降低传输速率,减少由于竞争导致的网络资源浪费。
  • 当网络空闲时,TCP 连接提升传输速率,提高通信效率。

根据实现方式不同,拥塞算法可以分为两类:基于丢包反馈基于传输延时
其最终目的是找到一个适合当前网络状况的最佳拥塞窗口 Wbest

基于丢包反馈

基于丢包反馈的拥塞算法是目前应用最广泛且较为成熟的算法。

Linux 系统中默认的拥塞算法 renocubic 都属于这类:

[lhop@localhost ~]$ sysctl -a  2>&1 | egrep 'tcp_available_congestion_control|tcp_congestion_control'
net.ipv4.tcp_congestion_control = cubic
net.ipv4.tcp_available_congestion_control = cubic reno

Reno 算法

reno 是最经典的拥塞算法,其核心是基于 加性增窗/乘性减窗 AIMD 的反馈控制:当检测到信道拥塞时,拥塞窗口会呈指数级快速减小(减少网络丢包),然后窗口缓慢地线性增长(避免再次拥塞)。算法流程如下图所示:

然而加性增窗的特性决定了 reno 存在一个明显缺点:如果算法进入拥塞避免快速恢复状这两个阶段时,每经过一个 RTT 才会将窗口大小加1,假设我们链路状况好,但如果RTT很长的话,reno 需要很长时间才能达到 Wbest。实际上受限于丢包率,reno 的另一个典型问题就是还没等 cwnd 增长到 Wbest,就已经发生丢包并削减cwnd了。

BIC 算法

为了提高 TCP 在 LFN 上的传输效率,后续提出了 bic 拥塞算法:

bic 也采用乘法减小的方式减小窗口,并引入一个参数 β 作为减窗因子。若发生丢包时的 cwnd 大小为 Wmax,则减小后的窗口大小为 W = β * Wmax

bic 假定 W < Wbest < Wmax,因此在恢复阶段 bic 是一个变速过程:

  • 在远离 Wmax 时,快速增大窗口,使 cwnd 尽快恢复至 Wbest(对应图中的 Addtitive Increase 过程)
  • 在靠近 Wmax 时,缓慢增大窗口,使 cwnd 尽可能长期保持在 Wbest 附近(对应图中的 Binary Search 过程)
  • 当到达 Wmax 时,如果仍未发生丢包,那么网络是空闲的,应该积极地去抢占网络资源。此时 TCP 连接会尝试增加拥塞窗口大小,并且增加速度越来越快,直到发现新的 Wmax 为止(对应图中的 Max Probing 过程)

CUBIC 算法

然而在 RTT 较小的非 LFN 环境中,bic 的增长策略显得过于激进,会抢占其他其他拥塞算法的 TCP 连接资源。后来,该算法的作者提出了普适性更好的 cubic 算法。
为了减少 RTT(受物理设备影响)对算法的影响,cubic 会记录最近一次发生网络拥塞的时间 treduce,距离最近一次拥塞发生的时间可以表示为 t = tnow - treduce

cubic 的减窗与增窗过程可以简化为一个的与时间 t 为自变量的窗口增长函数 W(t):

\[W(t) = C(t-K)^3 + W_{max} \\ K=\sqrt[3]{\frac{W_{max}\beta}{C}}\\ \beta = 0.2 \quad C = 0.4 \]

cubic 的窗口增长函数仅仅取决于与拥塞事件的时间间隔,从而将窗口增长独立于网络的时延RTT,因而能够在多条共享瓶颈链路的 TCP 连接之间保持良好的RTT公平性。

cubicreno 的拥塞窗口行为比较:

cubic 保证了 TCP 尽可能的长时间保持在 Wbest,从而避免了 reno 算法漫长增窗过程导致传输效率低下的问题。

Bufferbloat 现象

随着内存越来越便宜,TCP 链路上的网络设备的 Buffer 倾向于配置的特别大。但这一做法对于基于丢包反馈的 TCP 拥塞算法相当不友好:

  • 发送方无法及时感知到拥塞:当数据开始在队列排队时,链路已经出现拥塞,但因为 Buffer 很大,数据包不会被丢弃,发送端根本无法感知到拥塞的发生。
  • 无效的数据包占用网络资源:等真的出现丢包时候,重传的包放在链路上还得等之前积压在 Buffer 的数据包都送达接收端后才能被处理,对网络网络资源造成了浪费。
  • 接收方队头阻塞并丢弃数据:接收端在等待重传的过程中,如果 Buffer 不足够大,大量数据送达接收端后都会被丢弃,无形中增加了重传率,极大增加传输延迟。

Bufferbloat 造成的高重传率,无形中增加了网络传输的延迟,并且还会导致网络传输不稳定,有时候延迟很小,有的时候延迟又很大。这一表现正好符合我们之前的 iperf3 的测试结果:

  • 较长的 RTT 导致网络非拥塞时频繁丢包,基于丢包反馈的拥塞算法的窗口会比较小,对带宽的利用率很低,吞吐量下降很明显,但是实际上网络负载并不高。
  • 在网络拥塞但无丢包情况下算法一直加窗,丢包事件很快就发生了,乘性减窗使发送速率迅速降低,造成整个网络的瞬时抖动性,总体呈现较大的锯齿状波动。

基于传输时延

下图是 TCP 传输链路上某个缓存队列,根据网络状况变化,队列可能处于以下 3 种状态之一:

  • State 1: 网络空闲,没有排队的数据。网络延迟最低
  • State 2: 网络占满,数据开始排队,网络延迟开始增大
  • State 3: 队列溢出,网络出现丢包

cubic 的拥塞避免策略是让拥塞窗口尽可能保持在上一个 Wmax 附近,即 State 2 与 State 3 之间的状态。相当于尽可能把链路资源(线路带宽+中继Buffer)占满,也因此造成了较高的网络延迟。

而理想状态是维持在 State 1 和 State 2 之间,即没有出现排队导致延迟升高,又能完全占满链路带宽发送数据,高效且低延迟。为了实现这一点,需要使用 基于传输时延 的拥塞避免算法:

监控每个数据包的 RTT,先尽力增大 CWND 提高发送率:
  • 速率提高后 RTT 不变(无需排队),则此时链路处于 State 1,可以继续提高发送速率
  • 速率提高后 RTT 升高(开始排队),说明此时链路从 State 1 切换到了 State 2,需要降低发送效率,使其恢复到 State 1

最优控制点

下面通过一张图直观地感受一下网络拥塞的变化过程:

  • RTprop round-trip propagation time:链路固有的传播时延
  • BtlBw bottleneck bandwidth:链路的带宽上限
  • Amount Inflight:在途数据量
  • Delivery Rate:数据送达速率

横向将网络状态分为 3 个阶段:

  • app limited:性能瓶颈是应用本身(网络空闲)
  • bandwidth limited:性能瓶颈是网络带宽(网络拥塞)
  • buffer limit:性能瓶颈是中继 buffer 容量(网络耗尽)

纵向两幅图分别描绘了网络延迟与吞吐量的变化:

  • 上图展示了 RTT在途数据量 的之间的关系:

    • app limited:网络空闲时,RTT 大小仅取决于 线路 RTprop
    • bandwidth limited:网络拥塞时,RTT 大小取决于 网络带宽 BtlBwbuffer 数据量(BtlBw限制了队列的消费速率)
    • buffer limit:网络资源耗尽,开始丢包
  • 下图展示了 送达率在途数据量 的之间的关系:

    • app limited:网络空闲时,送达速率取决于 应用发送速率线路 RTprop(RTprop限制了ACK响应速率)
    • bandwidth limited:网络拥塞时,送达速率仅取决于 网络带宽 BtlBw
    • buffer limit:网络资源耗尽,开始丢包

注意图中的两个点:

  • 最优点optimum operating point is here:理想的拥塞算法应该将在途数据量控制在 BDP 附近。
  • 丢包点loss-based congestion control opreates here:基于丢包的拥塞算法将在途数据量控制在 BDP + BtlneckBufSize 附近。

在最优点附近,发送速率达到 BltBw,从而占满链路带宽,最高效的利用带宽;在途数据量不超过链路的 BDP = BtlBw * RTprop,从而有最优的延迟。

当链路上 Buffer 较小时,最优点与丢包点十分接近,此时基于丢包的算法延迟就不会很高。当链路上 Buffer 很大时,基于丢包的拥塞算法就容易导致将 Buffer 占满,就容易出现 BufferBloat。

反过来说,如果链路的 BtlBw 与 RTprop 已知,那么拥塞控制算法就可以根据这两个值,计算得出最优的发送速率 BDP。这也为拥塞算法的开发提供了新的思路。

BBR 算法

bbr 是 Google 开源的拥塞算法,其目的就是为了解决基于丢包的拥塞控制算法存在的弊端:

  • 在 Buffer 较大的链路中,容易造成 BufferBloat,增加了传输延迟
  • 在 Buffer 较小的长距离链路中,会对突发流量造成的丢包反应过度,导致吞吐量极低

其核心理念就是建立一套探测模型,动态评估链路上 BtlBw 与 RTprop 的变化情况:

  • 将最近一个时间窗口 WR(一般是在几十秒到几分钟之间)内监控到的 RTT 最小值作为近似的 RTprop
  • 将最近一个时间窗口 WB(一般是 10 个 RRT)内监控到的 送达率 最大值作为近似的 BtlBw

\[\hat{RT_{prop}} = min(RTT_t) \quad \forall t \in [T-W_R,T] \\ \hat{BltBw} = max(deliveryRate_t) \quad \forall t \in [T-W_B,T] \]

bbr 算法可以分为 4 个阶段:

StartUp

刚启动时,bbr以指数形式增大发送速率,每经过一个 RTT 发送速率会增大 $2/ln(2)$ 倍,从而保证能够在 O(log(BDP)) 个 RRT 内找到 BtlBw。 当bbr发现增大速率后,送达率没有明显上升时,会认为链路上的 Buffer 已经开始积压,于是认为已经探测到 BtlBw。

Drain

完成 BtlBw 探测后,会将发送速率增速调整为 $ln(2)/2$,等待链路上的队列积压数据清空,避免发生丢包。 bbr会监控在途数据量 Inflight,当 Inflight 与估计的 BDP 接近时,会进入稳定状态。

ProbeBW

经过 StartUp 和 Drain 之后,发送方进入稳定状态进行数据的发送。由于网络带宽的变化要比RTT更为频繁,因此 ProbeBW 阶段也是bbr的主要阶段。

ProbeBW 状态分为两个周期:

  • 增周期:提高发送率到稳定期的 1.25 倍,直到出现丢包或 Inflight 达到 1.25 倍 BDP 为止。如果观察到延迟升高且送达率不变,说明链路上带宽没有变化且产生了队列堆积。
  • 减周期:降低发送率到稳定期的 0.75 倍,等待一个 RTprop 或 Inflight低于 BDP 为止。让链路上在增周期出现堆积的队列清空。

ProbeRTT

前面 3 个阶段在运行过程中,都可能进入 ProbeRTT 阶段。 如果bbr运行了很长时间一直没有更新 RTprop 时,会进入 ProbeRTT 状态并试图探测到更小 RTprop。探测完成之后再根据最新数据来确定进入 StartUp 还是 ProbeBW 阶段。

cubicrenobbr 的拥塞窗口行为比较:

bbr 保证了 TCP 尽可能的长时间保持对网络的 100% 的利用率,规避了 cubic 过分占用网络资源引起 Bufferbloat 现象。

此外,由于 bbr 算法本身不依赖于丢包反馈,因此在高丢包率的网络下的表现明显优于 cubic

优化效果

经过前面的分析,发现 bbr 算法对于正好能够解决之前 ipref3 测试中发现的问题。于是跟运维同时协商将 CentOS6 升级为高内核版本的 CentOS7,并开启 bbr 算法:

[lhop@localhost ~]$ sysctl -a  2>&1 | egrep 'tcp_available_congestion_control|tcp_congestion_control|default_qdisc'
net.core.default_qdisc = fq
net.ipv4.tcp_available_congestion_control = reno cubic bbr
net.ipv4.tcp_congestion_control = bbr

再次使用 iperf3 进行测试,结果如下:

Connecting to host US_NY_VM_01, port 5001
[  4] local HK_QW_VM_01 port 58341 connected to US_NY_VM_01 port 5001
[ ID] Interval           Transfer     Bandwidth       Retr  Cwnd
[  4]   0.00-1.00   sec   740 KBytes  6.06 Mbits/sec    0   97.6 KBytes       
[  4]   1.00-2.00   sec  4.17 MBytes  35.0 Mbits/sec    0   1.89 MBytes       
[  4]   2.00-3.00   sec  12.5 MBytes   105 Mbits/sec    0   8.76 MBytes       
[  4]   3.00-4.00   sec  13.8 MBytes   115 Mbits/sec    0   8.79 MBytes       
[  4]   4.00-5.00   sec  11.2 MBytes  94.4 Mbits/sec    3   8.79 MBytes       
[  4]   5.00-6.00   sec  13.8 MBytes   115 Mbits/sec    0   8.79 MBytes       
[  4]   6.00-7.00   sec  15.0 MBytes   126 Mbits/sec    0   8.79 MBytes       
[  4]   7.00-8.00   sec  12.5 MBytes   105 Mbits/sec    0   8.79 MBytes       
[  4]   8.00-9.00   sec  13.8 MBytes   115 Mbits/sec    0   8.79 MBytes       
[  4]   9.00-10.00  sec  13.8 MBytes   115 Mbits/sec    0   8.79 MBytes       
[  4]  10.00-11.00  sec  13.8 MBytes   115 Mbits/sec    0   8.79 MBytes       
[  4]  11.00-12.00  sec  12.5 MBytes   105 Mbits/sec    0   8.79 MBytes       
[  4]  12.00-13.00  sec  15.0 MBytes   126 Mbits/sec    0   8.79 MBytes       
[  4]  13.00-14.00  sec  8.75 MBytes  73.4 Mbits/sec    0   8.79 MBytes       
[  4]  14.00-15.00  sec  15.0 MBytes   126 Mbits/sec    0   8.79 MBytes       
[  4]  15.00-16.00  sec  12.5 MBytes   105 Mbits/sec    0   8.79 MBytes       
[  4]  16.00-17.00  sec  13.8 MBytes   115 Mbits/sec    0   8.79 MBytes       
[  4]  17.00-18.00  sec  13.8 MBytes   115 Mbits/sec    0   8.79 MBytes       
[  4]  18.00-19.00  sec  13.8 MBytes   115 Mbits/sec    0   8.79 MBytes       
[  4]  19.00-20.00  sec  12.5 MBytes   105 Mbits/sec    0   8.79 MBytes       
[  4]  20.00-21.00  sec  15.0 MBytes   126 Mbits/sec    0   8.79 MBytes       
[  4]  21.00-22.00  sec  13.8 MBytes   115 Mbits/sec    0   8.79 MBytes       
[  4]  22.00-23.00  sec  12.5 MBytes   105 Mbits/sec    0   8.79 MBytes       
[  4]  23.00-24.00  sec  11.2 MBytes  94.4 Mbits/sec    3   8.79 MBytes       
[  4]  24.00-25.00  sec  11.2 MBytes  94.4 Mbits/sec    0   7.44 MBytes       
[  4]  25.00-26.00  sec  11.2 MBytes  94.4 Mbits/sec    0   6.30 MBytes       
[  4]  26.00-27.00  sec  12.5 MBytes   105 Mbits/sec    0   6.30 MBytes       
[  4]  27.00-28.00  sec  11.2 MBytes  94.4 Mbits/sec    0   6.30 MBytes       
[  4]  28.00-29.00  sec  12.5 MBytes   105 Mbits/sec    0   6.30 MBytes       
[  4]  29.00-30.00  sec  11.2 MBytes  94.4 Mbits/sec    0   6.30 MBytes       
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bandwidth       Retr
[  4]   0.00-30.00  sec   365 MBytes   102 Mbits/sec    6             sender
[  4]   0.00-30.00  sec   364 MBytes   102 Mbits/sec                  receiver

iperf Done.

观察测试结果发现网络的吞吐量明显上升,并且重传率保持在一个较低的水平。

并且拥塞窗口变化也更稳定,不再出现之前剧烈波动的情况。

使用 10 个 socket 基本上可以跑满整个专线,优化目标达成。

附录

在丢包率较高的网络中,调整 TCP 参数并不能起到很好的效果。但着这并不意味着 TCP 调参没有意义。

某个服务在同机房内,单个 TCP 连接流量能达到 80M,但在 RTT 1.5ms 且无丢包的跨机房网络环境上,突发流量峰值最多只能达到 60M 且不稳定。我们尝试通过调参,将 TCP 传输峰值较好地稳定在 70M 左右:

调整前:

[lhop@localhost ~]$ sysctl -a  2>&1 | egrep 'net.core.[r|w]mem_|net.ipv4.tcp_[r|w]*mem|tcp_adv_win_scale'
net.core.rmem_default = 212992
net.core.wmem_default = 212992
net.core.rmem_max = 212992
net.core.wmem_max = 212992
net.ipv4.tcp_mem = 765918	1021224	1531836
net.ipv4.tcp_rmem = 4096	87380	6291456
net.ipv4.tcp_wmem = 4096	16384	4194304
net.ipv4.tcp_adv_win_scale = 1

调整后:

[lhop@localhost ~]$ sysctl -a  2>&1 | egrep 'net.core.[r|w]mem_|net.ipv4.tcp_[r|w]*mem|tcp_adv_win_scale'
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.core.rmem_default = 65536
net.core.wmem_default = 65536
net.ipv4.tcp_mem = 4096	87380	4194304
net.ipv4.tcp_rmem = 4096	87380	16777216
net.ipv4.tcp_wmem = 4096	87380	16777216
net.ipv4.tcp_adv_win_scale = 2

可以看到,对于大流量的 TCP 传输通道,调整参数仍然能显著提升跨机房传输质量。

但想要获得一组适合的参数,需要依赖大量的测试才能获得。很多时候还是得靠业务设计来规避这类问题。


参考资料与图片来源

posted @ 2020-10-14 20:55  buttercup  阅读(3236)  评论(2编辑  收藏  举报