2团
Published on 2024-08-15 / 13 Visits
0
0

TCP的半连接队列与全连接队列

1. 前言

近期看《理解了实现再谈⽹络性能》(作者:张彦飞,个人网站)一书,发现下图简洁明了的阐述了TCP的半连接队列与全连接队列的关系,故摘抄至本文做记录。

image-idng.png

Linux主机上半连接以及全连接队列的长度均较为保守,这导致在瞬时高并发场景下(网站门户、物联网服务器等),如果队列满了,客户端在三次握手期间发送的syn或者ack包会被服务端丢弃。客户端在等待较长时间后,会发出TCP Retransmission重传包,进一步加剧了网络压力。

服务端半连接队列满导致syn包被丢弃,如下图所示:

image-xlix.png

2. 修改半连接队列

半连接队列⻓度 Linux内核中,主要受tcp_max_syn_backlog影响 加⼤它到⼀个合适的值就可以。

sudo echo "2048" > /proc/sys/net/ipv4/tcp_max_syn_backlog
sudo sysctl -p

若要此设置永久生效, 需要将相应的设置添加到/etc/sysctl.conf文件中。

3. 修改全连接队列

全连接队列⻓度是应⽤程序调⽤listen时传⼊的backlog以及内核参数net.core.somaxconn⼆者之中较⼩的那个。因此需要同时调整你的应⽤程序和该内核参数。

3.1 内核参数修改

Linux主机默认的somaxconn参数值为128,建议根据服务端实际负载调整(现阶段阿里云以及腾讯云的默认配置为4096)。

sudo sysctl -w net.core.somaxconn=1024
sudo sysctl -p

若要此设置永久生效, 需要将相应的设置添加到/etc/sysctl.conf文件中。

3.2 应用设置全连接队列长度

TCP全连接队列(也称为accept队列)的大小可以通过SO_BACKLOG选项在Netty的ServerBootstrap中设置。这个选项指定了内核应该为正在监听的套接字排队的最大连接数。Netty设置如下所示:

ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
 .channel(NioServerSocketChannel.class)
 .option(ChannelOption.SO_BACKLOG, 2028) // 设置TCP全连接队列的大小
 .childHandler(new ChannelInitializer<SocketChannel>() {
     @Override
     public void initChannel(SocketChannel ch) throws Exception {
         ch.pipeline().addLast(new YourServerHandler());
     }
 });

4. 队列溢出错误处理

netstat -s 可查看到当前系统半连接队列满导致的丢包统 计,但该数字记录的是总丢包数。可借助 watch 命令动态监控。

4.1 半连接队列异常

如果下⾯的数字在你监控的过程中变了,那说明当前服务器有因为半连接队列满⽽产⽣的丢包。

watch 'netstat -s | grep LISTEN'

4.2 全连接队列异常

watch 'netstat -s | grep overflowed'

4.3 调整队列后依然溢出

如果加⼤队列后仍然有⾮常偶发的队列溢出的话,我们可以暂且容忍。

如果仍然有较⻓时间处理不过来怎么办?另外⼀个做法就是直接报错,不要让客户端超时等待。例如将Redis、Mysql等后端接⼝的内核参数tcp_abort_on_overflow为1。如果队列满了,直接发reset给client。告诉后端进程/线程不要痴情 地傻等。这时候client会收到错误connection reset by peer牺牲⼀个⽤户的访问请求,要⽐把整 个站都搞崩了还是要强的。


Comment