TCP(Transmision Control Protocol
)即传输控制协议, 位于TCP/IP协议栈的第三层(L3), 是一种提供了可靠连接的字节流协议; TCP是目前使用最为广泛的协议之一, HTTP/MQTT/FTP等诸多应用层协议都是基于TCP实现的, 更多关于TCP协议相关的具体内容可以参考标准文档RFC793以及早前写的一篇聊一聊TCP协议.
在上一篇文章中讲到了高速以太网如1Gbps/10Gpbs中Linux网络L2(链路层)的一些优化方法, 包括了offload(卸荷)
以及scaling(缩放)
两种技术. 随着高速网络的不断普及, 1Gbps/10Gpbs以太网已经被广泛使用, 40Gbps/100Gbps也已经制定标准, TCP也在随着网络带宽的提升而不断进化.这篇文章我们就来看下如何在高速以太网下对TCP相关的参数的进行调优.
TCP窗口大小
TCP为了协调客户端与服务端数据的发送/接收, 使用窗口机制来确保两者保持一致的速率, 确保快速的发送方不会超过慢速的接收方. 接收方需要通过窗口更新(Window Update
)消息来告知发送方当前可以发送多少数据. 当接收方应用处理数据时, 可向发送方发送窗口更新消息
; 窗口更新消息最快需要一个返回时间(RTT-Round Trip Time
), 因此窗口越大, 同一个RTT可以接收的数据就越多; 类似地, RTT越大, 则同样的时间可以接收的数据会越少. 为此, 我们可以得到如下公式:
1 |
|
TCP原始设计中, 窗口大小为65535
字节(64KiB - 1), 这是发送方在收到窗口更新之前允许发送的最大数据量. 但随着网络带宽越来越高, 1Gbps/10Gbps的网络已经得到广泛使用, 这个窗口值无法让高速网络的带宽得到有效利用. 为了让高速网络的带宽得到充分利用, 一般需要通过带宽时延成乘积BDP(Bandwidth-delay product
)公式来计算真实的TCP窗口大小.
1 |
|
假如, 我们有一个1Gbps
的网络, RTT为10ms
, 通过BDP公式可以大致计算出网络实际的带宽:
1 |
|
也就是说, 如果按照TCP原始的64KiB
的窗口设定, 一个千兆网络的实际吞吐量才不到50Mbps, 这远没有达到1000Mbps的理论值. 那么, 要如何解决这个问题了? 标准协议通过增加一个窗口缩放选项来扩张TCP窗口大小.
TCP窗口缩放
为了改变TCP原有设计对窗口大小的限定, RFC7323引入了扩展协议, 在TCP头中增加一个Window Scaling Factor
的参数, 可以使窗口大小扩展到1,073,725,440
字节(接近1GiB).具体可以参考RFC7323中的说明.
要在Linux中确认是否开启了窗口缩放功能, 可以通过命令:
1 |
|
如果这个值为1
, 则表示开启了该选项(看4.4的内核版本已经默认打开了);如果没有打开, 要开启该选项, 只需要通过下述命令打开即可:
1 |
|
调整TCP窗口大小
现在开启了窗口缩放选项, 我们就可以根据BDP
来计算给定理论带宽与延迟情况下, 对应的TCP窗口大小了. 还是以1Gbps
网络为例, 假设延迟(可以通过ping来大致判断)为3ms
, 那么对应的窗口大小应
1 |
|
就是说至少需要将TCP窗口的大小设置为3.6Mb
大小才能确保实际吞吐量达到网络的理论带宽. 在Linux中, 一般需要设置如下几个参数来设定TCP窗口大小:
1 |
|
其中前两个参数rmem_max/wmem_max
表示应用最大可用的TCP窗口大小, tcp_rmem
/tcp_wmem
表示单个应用允许分配的最大TCP窗口大小(对应有三个值, 分别表示最小/默认/最大的TCP窗口大小). 为了保证TCP的性能与理论比较接近, 我们可以将上述四个参数都按照上述计算得到的TCP窗口进行设定:
1 |
|
或者可以通过直接写对应/proc
的文件节点来设定上述参数, 如下:
1 |
|
除了上述根据BDP
公式来优化TCP窗口大小之外, 对于高速以太网, 通常还可以通过开启TCP时间戳(增加一次往返RTT时间的估算准确度), 对TCP头进行压缩(减少TCP传输数据的大小)等方式来优化, 具体可以参考RFC7323.
总结
本文首先介绍了TCP在高速网络中需要解决的窗口不足的问题, 然后阐述了TCP标准协议是如何解决该问题的。接着着重说明了如何在Linux中配置TCP参数,确保TCP性能达到最优。