Listen Drop问题解决
目录:
当出现listen drop的时候
有两种情况
- syn queue(半连接队列)或accept queue(全连接队列)溢出丢包
- 因为时间戳校验问题丢包
队列溢出问题导致丢包
可以在服务器上看到存在丢包
$ netstat -s | grep LISTEN
337560270 SYNs to LISTEN sockets dropped
$ netstat -s | grep LISTEN
337569111 SYNs to LISTEN sockets dropped
也可以看netstat -s | grep -E 'overflow|drop'
或者
$ nstat -az | grep -E 'TcpExtListenOverflows|TcpExtListenDrops'
TcpExtListenOverflows 12178939 0.0
TcpExtListenDrops 12247395 0.0
如果ListenOverflows和ListenDrops差值不大,就是accept queue满了,如果大的话应该是syn queue满了
在listen的时候,backlog参数指定了socket的syn queue和accept queue大小
- syn queue,对于启动了
net.ipv4.tcp_syncookies=1
,sync queue满了之后还能继续接收SYN包,syn queue的逻辑大小是没有限制,如果没有启用,限制为min(backlog, net.core.somaxconn, net.ipv4.tcp_max_syn_backlog)
,如果syn queue满了并且没有开启syncookies就丢包,并将ListenDrops计数器+1。 - accept queue,限制为min(backlog, net.core.somaxconn),如果accept queue满了也会丢包,并将ListenOverflows和ListenDrops计数器+1。
启动了net.ipv4.tcp_syncookies=1
,sync queue满了之后还能继续接收SYN包并且回复SYN+ACK的client,只不过不存入syn queue,而是syncookies算法机制生成隐藏信息写入响应的SYN+ACK包中,等client回复ACK时,server利用syncookie算法校验,校验通过后三次握手就完成了
先看半连接队列是否开启了cookies
$ cat /proc/sys/net/ipv4/tcp_syncookies
1
如果开启的时候tcp_syncookies,那么就看一下有没有accept queue队列满的情况
$ ss -nlpt
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 8193 8192 :::8180 :::* users:(("push-lcs",pid=31356,fd=10))
LISTEN 8193 8192 :::8181 :::* users:(("push-lcs",pid=31356,fd=8))
LISTEN 8193 8192 :::8182 :::* users:(("push-lcs",pid=31356,fd=11))
- 对于LISTEN状态,Recv-Q为实际大小,Send-Q为限制大小,如果存在Recv-Q超过Send-Q,就是存在丢包的服务了
- 对于ESTABELISHED状态,Recv-Q为接收数据包buffer,Send-Q为发送数据包buffer
可以调整这个的大小
$ echo 16384 > /proc/sys/net/core/somaxconn
对于kubernetes服务,可以单独给pod设置
apiVersion: v1
kind: Pod
metadata:
name: sysctl-example
spec:
securityContext:
sysctls:
- name: net.core.somaxconn
value: "8096"
或
apiVersion: v1
kind: Pod
metadata:
name: sysctl-example-init
spec:
initContainers:
- image: busybox
command:
- sh
- -c
- echo 1024 > /proc/sys/net/core/somaxconn
imagePullPolicy: Always
name: setsysctl
securityContext:
privileged: true
Containers:
...
也可以用cni的插件tuning plugin
CNI配置示例
{
"name": "mytuning",
"type": "tuning",
"sysctl": {
"net.core.somaxconn": "1024"
}
}
对于nginx需要配合调整backlog
listen 80 default backlog=1024;
主要还是要解决因为进程处理慢的问题,可能是进程的问题或者是IOWAIT、CPU负载过高等
时间戳问题导致丢包
如果存在passive connections rejected because of time stamp
$ netstat -s | grep 'passive connections rejected because of time stamp'
在2.6内核以上中tcp_timestamps
默认是打开的,所以当打开tcp_tw_recycle
时会导致部分通过NAT上网client无法正确连接服务器,故障表现为client发出SYN后无法收到server返回 的SYN+ACK,推荐的解决方法是关闭tcp_tw_recycle
,打开tcp_tw_reuse
解决TIME-WAIT过多的问题。
echo 0 > /proc/sys/net/ipv4/tcp_tw_recycle
echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse
然后查看tw个数会下降
netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
tcp_tw_recycle
设置为1会开启系统对TIME_WAIT状态的socket的快速回收。开启这个功能,系统就会存下TCP连接的时间戳,当同一个IP地址过来的包的时间戳小于缓存的时间戳,系统就直接丢包,并回收这个 socket,需要需要开启tcp_timestamp才生效。
开启这个功能是有很大风险的,服务器端会根据同一个IP发送过来的包的时间戳来判断是否丢包,而时间戳是根据发包的客户端的系统时间得来的,如果服务端收到的包是同一出口IP而系统时间不一样的两个客户端的包,就有可能会丢包,可能出现的情况就是一个局域网内有的客户端能连接服务端,有的不能。具体原因是客户端处于NAT模式下,出口ip可能是同一个ip,不同客户端的发送的时间戳可能乱序,服务器会检查相同ip地址发送来过的包的时间戳是不是小于缓存的时间戳,如果不是,直接丢掉;
在服务器上最好打开tcp_tw_reuse
,并且关闭tcp_tw_recycle
tcp_tw_reuse
和tcp_tw_recycle
区别:
tw_reuse
,tw_recycle
必须在客户端和服务端timestamps开启时才管用(默认打开)tw_reuse
只对客户端起作用,开启后客户端在1s内回收tw_recycle
对客户端和服务器同时起作用,开启后在3.5*RTO内回收,RTO在200ms~120s具体时间视网络状况,内网状况比tw_reuse
稍快,公网尤其移动网络大多要比tw_reuse
慢,优点就是能够回收服务端的TIME_WAIT
数量
由于tcp_tw_recycle
坑太多,在内核4.12已经移除