高流量大并发Linux TCP性能调优( 二 )


一开始怀疑是网卡驱动有问题 , 
通过修改网卡驱动参数 , 关闭NAPI功能 , 同机房的传输延时有所提升 , 具体的操作:Disable掉NAPI功能  , 即更改 ethtool -C ethx rx-usecs 0  , 但这个方案有缺点:使得cpu中断请求变多 。
另外一个方案:修改tcp的初始化拥塞窗口 , 强制将初始化拥塞窗口设置为3 , 即: ip route | while read p; do ip route change $p initcwnd 3;done
这两种方案可以将同机房的读延时至于理论计算水平 。
但这两种方案 , 都无法解决跨机房的长延时问题 。进一步追踪如下:
我们测试的延时高 , 是因为没有享受Tcp高速通道阶段甚至一直处于Tcp慢启动阶段 。
 
我做了下面5步尝试 , 具体过程如下:
 
STEP1】 最开始的测试代码:
每次请求建立一个Tcp连接 , 读完4MB数据后关闭连接 , 测试的结果:平均延时174毫秒:每次都新建连接 , 都要经历慢启动阶段甚至还没享受高速阶段就结束了 , 所以延时高 。
 
STEP2】 改进后的测试代码:
只建立一个Tcp连接 , Client每隔10秒钟从Server读4MB数据 , 测试结果:平均延时102毫秒 。
改进后延时还非常高 , 经过观察拥塞窗口发现每次读的时候拥塞窗口被重置 , 从一个较小值增加 , tcp又从慢启动阶段开始了 。
 
STEP3】改进后的测试代码+设置net.ipv4.tcp_slow_start_after_idle=0:
只建立一个Tcp连接 , Client每隔10秒钟从Server读4MB数据 , 测试结果:平均延时43毫秒 。
net.ipv4.tcp_slow_start_after_idle设置为0 , 一个tcp连接在空闲后不进入slow start阶段 , 即每次收发数据都直接使用高速通道 , 平均延时43毫秒 , 跟计算的理论时间一致 。
 
STEP4】我们线上的业务使用了Sofa-Rpc网络框架 , 这个网络框架复用了Socket连接 , 每个EndPoint只打开一个Tcp连接 。
我使用Sofa-Rpc写了一个简单的测试代码 , Client每隔10秒钟Rpc调用从Server读4MB数据 , 
即:Sofa-Rpc只建立一个Tcp连接+未设置net.ipv4.tcp_slow_start_after_idle(默认为1) , 测试结果:延时高 , 跟理论耗时差距较大:transbuf配置为32KB时 , 平均延时93毫秒 。
 
STEP5】
Sofa-Rpc只建立一个Tcp连接+设置net.ipv4.tcp_slow_start_after_idle为0 , 测试结果: transbuf配置为1KB时 , 平均延时124毫秒;transbuf配置为32KB时 , 平均延时61毫秒;transbuf配置为4MB时 , 平均延时55毫秒
使用Sofa-Rpc网络框架 , 在默认1KB的transbuf时延时124毫秒 , 不符合预期;
使用Sofa-Rpc网络框架 , 配置为32KB的transbuf达到较理想的延时61毫秒 。32KB跟Sofa-Rpc官方最新版本推荐的transbuf值一致 。
 
结论:
延时高是由于Tcp传输没享受高速通道阶段造成的 , 
1】需要禁止Tcp空闲后慢启动 :设置net.ipv4.tcp_slow_start_after_idle = 0
2】尽量复用Tcp socket连接 , 保持一直处于高速通道阶段
3】我们使用的Sofa-Rpc网络框架 , 需要把Transbuf设置为32KB以上
 
另附linux-2.6.32.71内核对tcp idle的定义:
 
从内核代码153行可见在idle时间icsk_rto后需要执行tcp_cwnd_restart()进入慢启动阶段 , 
Icsk_rto赋值为TCP_TIMEOUT_INIT , 其定义为
#define TCP_TIMEOUT_INIT ((unsigned)(3*HZ)) /* RFC 1122 initial RTO value */
linux内核Tcp性能调优1. fs.file-max
最大可以打开的文件描述符数量 , 注意是整个系统 。
在服务器中 , 我们知道每创建一个连接 , 系统就会打开一个文件描述符 , 所以 , 文件描述符打开的最大数量也决定了我们的最大连接数
select在高并发情况下被取代的原因也是文件描述符打开的最大值 , 虽然它可以修改但一般不建议这么做 , 详情可见unp select部分 。
2.net.ipv4.tcp_max_syn_backlog
Tcp syn队列的最大长度 , 在进行系统调用connect时会发生Tcp的三次握手 , server内核会为Tcp维护两个队列 , Syn队列和Accept队列 , Syn队列是指存放完成第一次握手的连接 , Accept队列是存放完成整个Tcp三次握手的连接 , 修改net.ipv4.tcp_max_syn_backlog使之增大可以接受更多的网络连接 。


推荐阅读