Linux上TCP的几个内核参数调优

linux作为一个强大的操作系统,提供了一系列内核参数供我们进行调优 。光TCP的调优参数就有50多个 。在和线上问题斗智斗勇的过程中,笔者积累了一些在内网环境应该进行调优的参数 。在此分享出来,希望对大家有所帮助 。
调优清单好了,在这里先列出调优清单 。请记住,这里只是笔者在内网进行TCP内核参数调优的经验,仅供参考 。同时,笔者还会在余下的博客里面详细解释了为什么要进行这些调优!

Linux上TCP的几个内核参数调优

文章插图
 
tcp_max_syn_backlog,somaxconn,tcp_abort_on_overflowtcp_max_syn_backlog,somaxconn,tcp_abort_on_overflow这三个参数是关于 内核TCP连接缓冲队列的设置 。如果应用层来不及将已经三次握手建立成功的TCP连接从队列中取出,溢出了这个缓冲队列(全连接队列)之后就会丢弃这个连接 。如下图所示:
Linux上TCP的几个内核参数调优

文章插图
 
从而产生一些诡异的现象,这个现象诡异之处就在于,是在TCP第三次握手的时候丢弃连接
Linux上TCP的几个内核参数调优

文章插图
 
就如图中所示,第二次握手的SYNACK发送给client端了 。所以就会出现client端认为连接成功,而Server端确已经丢弃了这个连接的现象!由于无法感知到Server已经丢弃了连接 。所以如果没有心跳的话,只有在发出第一个请求后,Server才会发送一个reset端通知这个连接已经被丢弃了,建立连接后第二天再用,也会报错!所以我们要调大Backlog队列!
echo 2048 > /proc/sys/inet/ipv4/tcp_max_syn_backlogecho 2048 > /proc/sys/net/core/somaxconn当然了,为了尽量避免第一笔调用失败问题,我们也同时要设置
echo 1 > /proc/sys/net/ipv4/tcp_abort_on_overflow设置这个值以后,Server端内核就会在这个连接被溢出之后发送一个reset包给client端 。
Linux上TCP的几个内核参数调优

文章插图
 
如果我们的client端是NIO的话,就可以收到一个socket close的事件以感知到连接被关闭!
Linux上TCP的几个内核参数调优

文章插图
 
注意JAVA默认的Backlog是50这个TCP Backlog的队列大小值是min(tcp_max_syn_backlog,somaxconn,应用层设置的backlog),而Java如果不做额外设置,Backlog默认值仅仅只有50 。C语言在使用listen调用的时候需要传进Backlog参数 。
tcp_tw_recycletcp_tw_recycle这个参数一般是用来抑制TIME_WAIT数量的,但是它有一个副作用 。即在tcp_timestamps开启(Linux默认开启),tcp_tw_recycle会经常导致下面这种现象 。
Linux上TCP的几个内核参数调优

文章插图
 
也即,如果你的Server开启了tcp_tw_recycle,那么别人如果通过NAT之类的调用你的Server的话,NAT后面的机器只有一台机器能正常工作,其它情况大概率失败 。具体原因呢由下图所示:
Linux上TCP的几个内核参数调优

文章插图
 
在tcp_tw_recycle=1同时tcp_timestamps(默认开启的情况下),对同一个IP的连接会做这样的限制,也即之前后建立的连接的时间戳必须要大于之前建立连接的最后时间戳,但是经过NAT的一个IP后面是不同的机器,时间戳相差极大,就会导致内核直接丢弃时间戳较低的连接的现象 。由于这个参数导致的问题,高版本内核已经去掉了这个参数 。如果考虑TIME_WAIT问题,可以考虑设置一下
echo 1 > /proc/sys/net/ipv4/tcp_tw_reusetcp_syn_retries这个参数值得是client发送SYN如果server端不回复的话,重传SYN的次数 。对我们的直接影响呢就是connet建立连接时的超时时间 。当然Java通过一些C原生系统调用的组合使得我们可以进行超时时间的设置 。在Linux里面默认设置是5,下面给出建议值3和默认值5之间的超时时间 。
Linux上TCP的几个内核参数调优

文章插图
 
下图给出了,重传和超时情况的对应图:
Linux上TCP的几个内核参数调优

文章插图
 
当然了,不同内核版本的超时时间可能不一样,因为初始RTO在内核小版本间都会有细微的变化 。所以,有时候在抓包时候可能会出现(3,6,12......)这样的序列 。当然Java的API有超时时间:
java: // 函数调用中携带有超时时间 public void connect(SocketAddress endpoint, int timeout) ;所以,对于Java而言,这个内核参数的设置没有那么重要 。但是,有些代码可能会有忘了设置timeout的情况,例如某个版本的Kafka就是,所以它在我们一些混沌测试的情况下,容灾恢复的时间会达到一分多钟,主要时间就是卡在connect上面-_-!,而这时我们的tcp_syn_retries设置的是5,也即超时时间63s 。减少这个恢复时间的手段就是:


推荐阅读