然而当指定参数过后,会发现实际的 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_wmem 与 net.ipv4.tcp_rmem 是单个 tcp socket 缓冲区的最小值min、默认值default、最大值max,单位为字节。
net.core.wmem_max 与 net.core.rmem_max 是单个 socket 所能使用的缓冲区大小上限,单位为字节。该参数的优先级高于 tcp_wmem 与 tcp_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 系统中默认的拥塞算法 reno 与 cubic 都属于这类:
[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 过程)