Listen Drop问题解决

时间:Sept. 1, 2021 分类:

目录:

当出现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_reusetcp_tw_recycle区别:

  1. tw_reusetw_recycle必须在客户端和服务端timestamps开启时才管用(默认打开)
  2. tw_reuse只对客户端起作用,开启后客户端在1s内回收
  3. tw_recycle对客户端和服务器同时起作用,开启后在3.5*RTO内回收,RTO在200ms~120s具体时间视网络状况,内网状况比tw_reuse稍快,公网尤其移动网络大多要比tw_reuse慢,优点就是能够回收服务端的TIME_WAIT数量

由于tcp_tw_recycle坑太多,在内核4.12已经移除