一、背景
TCP连接经历了三次握手,当我们开发一个netty程序,运行在线上环境后,如何对其进行监控,并适时地进行参数调优。
本文以我们生产环境的一个通道程序,看一看三次握手的过程,以及Linux系统关于创建http连接的参数设置情况。
二、流程图
网上有很多关于tcp三次握手的讲解时序图,这里结合了两个队列的出入操作,详情见下:
三、linux机器(centos)的参数
优化TCP三次握手涉及到的参数主要有以下几个,另外就是绕开三次握手的策略。
参数 | 默认值 | 建议值 | 备注 |
---|---|---|---|
/proc/sys/net/ipv4/tcp_syn_retries | 5 | SYN报文重传的次数(客户端) | |
/proc/sys/net/ipv4/tcp_abort_on_overflow | 0 | 0 | 调试的时候,设置为1的时候,accept队列满后,服务端会发送reset报文给客户端 |
/proc/sys/net/ipv4/tcp_synack_retries | 2 | SYN+ACK报文重传的次数 | |
/proc/sys/net/ipv4/tcp_max_syn_backlog | 262144 | 半连接队列的长度 | |
/proc/sys/net/core/somaxconn | 128 | 全连接队列的长度 |
1、SYN报文重传的次数
2、开关tcp_abort_on_overflow
当accept队列满的时候,如果值设为1,server会返回reset报文给client;默认值是0,服务端会抛弃TCP第三次握手,回到第二次握手----由服务端重新发送SYN+ACK报文给客户端。
这里就会有一个重试发送的次数问题,见下一段。
3、SYN+ACK报文重传的次数
4、半连接队列的长度
tcp_max_syn_backlog的长度一般都远比somaxconn大,这就跟买东西讲价钱一样,真心诚意的往往比问价格的人少之又少。
TCP SYN FLOOD恶意DOS攻击就是建立大量的半连接状态的请求,然后丢弃而不进行第三次握手,真是害人~
半连接syns队列的连接要取出来给全连接队列,而全连接accept队列中的连接,在服务端accept()的时候触发取出。
也就是说,TCP三次握手,到建立连接完成,无论是半连接队列还是全连接队列,都不保留该连接。
5、全连接队列的长度
全连接队列的大小取决于:min(backlog, somaxconn) . backlog是在socket创建的时候传入的,somaxconn是一个os级别的系统参数。
- nginx默认是511
- tomcat默认是100
- netty的默认情况,见下文的源码分析
如何查看全队列的长度
linux机器分析netty端口是8889的tcp连接情况:
# ss -lnt
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 128 *:8889 *:*
其中Send-Q 就是指全连接队列的长度为128,Recv-Q则是指在队列中和等待进队列的数量。
如果Recv-Q 大于 Send-Q 的话,则会出现客户端无法和服务端建立连接的异常情况。
调大nginx的backlog
netty listen()
public static final ChannelOption<Integer> SO_BACKLOG = valueOf("SO_BACKLOG");
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
//注册NioServerSocketChannel
.channel(NioServerSocketChannel.class)
// 设置BACKLOG队列大小为1024
.option(ChannelOption.SO_BACKLOG, 1024);
四、netty的backlog
源码类io.netty.util.NetUtil.SOMAXCONN
netty的backlog值,可以看到,windows操作系统默认是200,否则128。
程序进一步会去读取文件/proc/sys/net/core/somaxconn的内容,把它赋值给SOMAXCONN。
这也就是说,当我们没有在netty listen()中指定队列大小的时候,默认它就取somaxconn的值。
所以,我们只要修改系统参数somaxconn即可增大netty的backlog值。
public static final int SOMAXCONN;
SOMAXCONN = (Integer)AccessController.doPrivileged(new PrivilegedAction<Integer>() {
public Integer run() {
int somaxconn = PlatformDependent.isWindows() ? 200 : 128;
File file = new File("/proc/sys/net/core/somaxconn");
BufferedReader in = null;
try {
if (file.exists()) {
in = new BufferedReader(new FileReader(file));
somaxconn = Integer.parseInt(in.readLine());
if (NetUtil.logger.isDebugEnabled()) {
NetUtil.logger.debug("{}: {}", file, somaxconn);
}
} else {
Integer tmp = null;
if (SystemPropertyUtil.getBoolean("io.netty.net.somaxconn.trySysctl", false)) {
tmp = NetUtil.sysctlGetInt("kern.ipc.somaxconn");
if (tmp == null) {
tmp = NetUtil.sysctlGetInt("kern.ipc.soacceptqueue");
if (tmp != null) {
somaxconn = tmp;
}
} else {
somaxconn = tmp;
}
}
if (tmp == null) {
NetUtil.logger.debug("Failed to get SOMAXCONN from sysctl and file {}. Default: {}", file, somaxconn);
}
}
} catch (Exception var13) {
NetUtil.logger.debug("Failed to get SOMAXCONN from sysctl and file {}. Default: {}", new Object[]{file, somaxconn, var13});
} finally {
if (in != null) {
try {
in.close();
} catch (Exception var12) {
}
}
}
return somaxconn;
}
});
五、slb监控连接数
从阿里云SLB的监听可以看出,每秒的连接数以及并发连接数。
六、linux监控
[root@channel-jvm1 ~]# cat /proc/sys/net/core/somaxconn
128
[root@channel-jvm1 ~]# cat /proc/sys/net/ipv4/tcp_max_syn_backlog
262144
[root@channel-jvm1 ~]# ss -lnt
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 128 *:8889 *:*
LISTEN 0 100 *:6005 *:*
- 8889是netty的tcp端口
- 6005是tomcat的http端口
可以看到,这里的全连接队列比较小,为默认值128。因为QPS比较低,见上面的SLB监控,所以还未出现握手异常。
最后说一下,怎么看有无被丢弃的连接。
[root@channel-jvm1 ~]# netstat -s | grep overflowed
4236 times the listen queue of a socket overflowed
[root@channel-jvm1 ~]# netstat -s | grep overflowed
4236 times the listen queue of a socket overflowed
[root@channel-jvm1 ~]# netstat -s | grep overflowed
4236 times the listen queue of a socket overflowed
4236 times ,表示 accept 队列溢出的次数,注意这个是累计值。可以隔几秒钟执行下,如果这个数字一直在增加的话,说明 accept 连接队列偶尔满了。
如果持续不断地有连接因为 accept 队列溢出被丢弃,就应该调大 backlog 以及 somaxconn 参数。
或者采用如下命令,注意是每隔几秒调用一下。
[root@channel-jvm1 ~]# date;netstat -s | egrep "listen|LISTEN"
Fri May 31 23:56:46 CST 2024
4236 times the listen queue of a socket overflowed
8146 SYNs to LISTEN sockets dropped
[root@channel-jvm1 ~]# date;netstat -s | egrep "listen|LISTEN"
Fri May 31 23:57:28 CST 2024
4236 times the listen queue of a socket overflowed
8146 SYNs to LISTEN sockets dropped
[root@channel-jvm1 ~]# date;netstat -s | egrep "listen|LISTEN"
Fri May 31 23:57:48 CST 2024
4236 times the listen queue of a socket overflowed
8146 SYNs to LISTEN sockets dropped
七、参考文章
https://mp.weixin.qq.com/s/yH3PzGEFopbpA-jw4MythQ
https://learnku.com/articles/46205
https://cloud.tencent.com/developer/article/1645688