问题1 三次握手产生的连接池满
【现象】:QPS远低于预期值,压测机请求产生大量网络异常:Connection time out/Read time out,后端未观测到很多请求
【思路】:连接超时,看起来多半是网络问题,如果是后端主动拒绝连接(tomcat线程池满等),报错不应该是连接超时
【排查流程】:
在节点上查看网络情况
netstat -nat | awk '/tcp/{print $6}' | sort | uniq -c
>>
700 SYN_RECV
200 ESTABLISHED
……
看到有700的SYN_RECV半连接产生,即想到与三次握手相关的配置
# 半连接队列长度
$ sysctl net.ipv4.tcp_max_syn_backlog
>> 128
# 全连接队列长度
$ sysctl net.core.somaxconn
>> 1024
三次握手的流程中,客户端实际上向服务器发送了两个连接请求,一次请求syn,一次应答ack,这两个请求分别存放在半连接队列net.ipv4.tcp_max_syn_backlog
中和全连接队列net.core.somaxconn
中
当net.ipv4.tcp_max_syn_backlog
设置过小,服务器就会丢弃过量的半连接,客户端长期无法建立连接,就会产生Connetion time out异常
当net.core.somaxconn
过小,服务器就会丢弃过量的全连接,客户端认为连接建立了,但是长期收不到服务器数据,就会产生Read time out异常
问题2 tcp重发导致的socket无法断连
为了节省tcp连接产生的开销,socket被设计成一个长连接,一旦连接建立,socket不会在发送一轮数据后就关闭
但是有些主备倒换场景下(比如redis、sftp),原本连接在主节点上的socket由于倒换变成了从节点,但是本质上addr还没有变,只是从节点不给访问了,这时候客户端不会立刻收到RST包,而是基于tcp重连机制进行重传,重传次数参考配置内核参数net.ipv4.tcp_retries2
,默认值15次,重传时间从200ms递增,最大到120s,超过最大重传次数后客户端才会收到RST包,得知socket已断开
使用socket作为底层连接并且有池化逻辑的服务都可能存在此问题,例如:Jedis/Lettuce、Jsch
Jedis对此做了优化,见jedis部分
lettuce没有优化,可以通过修改socket配置解决:
.tcpUserTimeout(Duration.ofSeconds(TCP_USER_TIMEOUT))
评论区