本文主要根据火丁笔记http://huoding.com/2013/11/21/299整理而成,详细信息请点击原文;
1 原理
流量控制rwnd
在每次交互时告知对端自己的接收窗口rwnd大小,避免接收缓冲溢出导致数据丢失;
慢启动cwnd
在得到发送方确认前,最大允许传输的未经确认的数据,主要为了避免耗尽网络流量。
cwnd无需通知给接收方,其初始值往往比较小,在达到阈值前呈指数级增长:假定当前cwnd=1,收到对端的ACK后变为2,若接下来发送的两个数据包又收到ack,则cwnd=4;
慢启动阈值ssthresh:随着cwnd的增加,可能会导致网络过载即出现丢包,此时cwnd的大小会迅速衰减至当前的一半;
一旦触发了慢启动阈值,cwnd趋于线性增长,以避免再次迅速引发网络阻塞,直至下次丢包(如此反复);
两者关联
网络中实际传输的未经确认的数据大小 = min(rwnd, cwnd);
查看初始值
可通过perl脚本查看对端的初始设置https://github.com/redhands/initcwnd_check
# ./initcwnd_check.pl eth0 http://kr.yahoo.com/ 111.67.226.84
+ connected from x.x.x.x:2652 to 111.67.226.84:80
* kr.yahoo.com (111.67.226.84) - init_cwnd: 2 (2875 byte), init_rwnd: 4 (5840 byte)
调用ip route show会显示的本机各网卡接口的initcwnd和initrwnd;
2 调优
2.1 调优rwnd
注:TCP使用16位来记录窗口大小,也就是说最大值是64KB,如果超过它,就需要使用tcp_window_scaling机制。
如果网络实际传输率远低于理论值,则可能是rwnd设置不合理造成的;
rwnd的合理值取决于BDP的大小,也就是带宽和延迟的乘积。假设带宽是 100Mbps,延迟是 100ms,那么计算过程如下:
BDP = 100Mbps * 100ms = (100 / 8) * (100 / 1000) = 1.25MB
此问题下如果想最大限度提升吞度量,接收窗口rwnd的大小不应小于 1.25MB。
Linux有一个缓冲大小自动调优的机制,接收窗口的实际大小会自动在最小值和最大值之间浮动,以期找到性能和资源的平衡点。
shell> sysctl -a | grep mem
net.ipv4.tcp_rmem =
shell> sysctl -a | grep tcp_moderate_rcvbuf
--1表示开启,tcp_rmem最大值设置为BDP;0为关闭,tcp_rmem缺省值设置为BDP;
除此之外,缓冲区还要保存TCP连接的元数据信息,相应开销为Buffer / 2^tcp_adv_win_scale;
因此,rwnd合理值为BDP / (1 – 1 / 2^tcp_adv_win_scale)
2.2 调优cwnd
计算公式:cwnd = min(4 * MSS, max(2 * MSS, 4380))
以太网标准的MSS大小通常是1460,初始值为4380即3MSS;
对于小的网络请求,譬如浏览器打开文本网页,cwnd越大则请求完成的越快,;
网络连接请求的内容越少,cwnd的影响就越明显,因为到达慢启动阀值前为指数增长,即便初始值很小也可迅速达到阀值;
至于initcwnd,Google给出的建议是10MSS,那么可以通过如下方法来调整initrwnd和initcwnd:
调整initrwnd & initcwnd
sajal@sajal-desktop:~$ sudo ip route change default via 192.168.1.1 dev eth0 proto static initrwnd 10
ajal@sajal-desktop:~$ ip route show
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.100 metric 1
169.254.0.0/16 dev eth0 scope link metric 1000
default via 192.168.1.1 dev eth0 proto static initrwnd 10
sajal@sajal-desktop:~$ sudo ip route change default via 192.168.1.1 dev eth0 proto static initcwnd 10
sajal@sajal-desktop:~$ ip route show
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.100 metric 1
169.254.0.0/16 dev eth0 scope link metric 1000
default via 192.168.1.1 dev eth0 proto static initcwnd 10
http://www.cdnplanet.com/blog/tune-tcp-initcwnd-for-optimum-performance/
注:这种方法在系统重启后会复原,可采用褚霸的stap脚本
$ cat > tcp_init_cwnd.stp
probe kernel.function("tcp_init_cwnd").return
{
$return = $1
}
#设成7个mss
$ sudo stap -p4 -g -m initcwnd tcp_init_cwnd.stp 7
initcwnd.ko
#在系统的启动脚本里面运行以下命令:
$ sudo staprun -o initcwnd.out -D initcwnd.ko
#当然如果你的模块没有输出的话也可以直接, 这样更简单
$ sudo insmod initcwnd.ko
http://blog.yufeng.info/archives/1173
代理服务器的设置
以CDN为例,如下图所示,其慢启动initcwnd决定了和客户端通信的下限,而接收窗口initrwnd决定了和服务器通信的下限;
因此对于类似CND之类的代理服务器,initcwnd和initrwnd都应该设置的尽量大一些;