El Psy Congroo

TCP backlog引起的Tomcat客户端connect timeout

生产环境的Tomcat在Full GC时,部分客户端请求会有connect timeout报错,然而我们客户端设置的连接超时是10s,FullGC最长不过2s,一番搜索后发现是backlog的锅,在此记录一下backlog的工作原理

How TCP backlog works in Linux

How TCP backlog works in Linux
TCP(7)

  • 两个队列管理backlog

    • SYN queue
      管理三次握手过程中的连接,使用如下参数控制长度,系统级别

      /proc/sys/net/ipv4/tcp_max_syn_backlog

    • accept queue
      管理ESTABLISHED状态的连接,这个Q中的连接可以被accpet(2)处理,Q长度在listen(2)调用时传入,同时最大值受如下参数限制,应用级别

      net.core.somaxconn

  • 查看accept queue大小

    ss -lt (查询结果的Send-Q列)

  • 如果accept queue空间耗尽
    服务器默认会忽略三次握手最后的ACK包,可以通过将如下参数设置为1将忽略变为发送RST

    net.ipv4.tcp_abort_on_overflow

    此时服务器仍然处于SYN RECEIVED状态,而客户端已经是ESTABLISHED状态
    服务器会继续向客户端发送SYN/ACK包,重试间隔从3s开始,重试次数由如下参数控制

    net.ipv4.tcp_synack_retries = 5

    当客户端收到重发的SYN/ACK包,会重发ACK包,如果queue有空间,即进入ESTABLISHED状态,反之服务端继续忽略ACK
    当重试次数耗尽,queue仍然没有空间,服务器会发送RST包,终止连接

    从客户端角度来看,发送ACK后连接已经建立,此时客户端如果发送数据,会导致数据重传,由于TCP Slow-start,重传的数据量会受到限制,如果客户端等待服务器发送数据,即会出现half-open connection问题

    SYN queue限流
    accept queue空间耗尽时,系统会对SYN queue进行限流,即使SYN queue没满,部分SYN包仍然会被丢弃,由客户端负责重试

    net.ipv4.tcp_syn_retries = 5

    可以通过netstat -s查看丢弃的包

    960 times the listen queue of a socket overflowed
    960 SYNs to LISTEN sockets ignored

    或者nstat -a

    TcpExtListenOverflows 960
    TcpExtListenDrops 960

  • 分为两个队列的优势
    应用根据处理能力管理accept queue的大小
    系统管理员根据流量特征管理SYN queue的大小

  • 合理设置accept queue大小
    设置过大会导致请求堆积,使请求响应变慢
    设置过小会导致并发高时部分请求被丢弃,引发不合理的connect timeout错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# accept queue空间耗尽情况下的抓包记录
0.000 127.0.0.1 -> 127.0.0.1 TCP 74 53302 > 9999 [SYN] Seq=0 Len=0
0.000 127.0.0.1 -> 127.0.0.1 TCP 74 9999 > 53302 [SYN, ACK] Seq=0 Ack=1 Len=0
0.000 127.0.0.1 -> 127.0.0.1 TCP 66 53302 > 9999 [ACK] Seq=1 Ack=1 Len=0
0.000 127.0.0.1 -> 127.0.0.1 TCP 71 53302 > 9999 [PSH, ACK] Seq=1 Ack=1 Len=5
0.207 127.0.0.1 -> 127.0.0.1 TCP 71 [TCP Retransmission] 53302 > 9999 [PSH, ACK] Seq=1 Ack=1 Len=5
0.623 127.0.0.1 -> 127.0.0.1 TCP 71 [TCP Retransmission] 53302 > 9999 [PSH, ACK] Seq=1 Ack=1 Len=5
1.199 127.0.0.1 -> 127.0.0.1 TCP 74 9999 > 53302 [SYN, ACK] Seq=0 Ack=1 Len=0
1.199 127.0.0.1 -> 127.0.0.1 TCP 66 [TCP Dup ACK 6#1] 53302 > 9999 [ACK] Seq=6 Ack=1 Len=0
1.455 127.0.0.1 -> 127.0.0.1 TCP 71 [TCP Retransmission] 53302 > 9999 [PSH, ACK] Seq=1 Ack=1 Len=5
3.123 127.0.0.1 -> 127.0.0.1 TCP 71 [TCP Retransmission] 53302 > 9999 [PSH, ACK] Seq=1 Ack=1 Len=5
3.399 127.0.0.1 -> 127.0.0.1 TCP 74 9999 > 53302 [SYN, ACK] Seq=0 Ack=1 Len=0
3.399 127.0.0.1 -> 127.0.0.1 TCP 66 [TCP Dup ACK 10#1] 53302 > 9999 [ACK] Seq=6 Ack=1 Len=0
6.459 127.0.0.1 -> 127.0.0.1 TCP 71 [TCP Retransmission] 53302 > 9999 [PSH, ACK] Seq=1 Ack=1 Len=5
7.599 127.0.0.1 -> 127.0.0.1 TCP 74 9999 > 53302 [SYN, ACK] Seq=0 Ack=1 Len=0
7.599 127.0.0.1 -> 127.0.0.1 TCP 66 [TCP Dup ACK 13#1] 53302 > 9999 [ACK] Seq=6 Ack=1 Len=0
13.131 127.0.0.1 -> 127.0.0.1 TCP 71 [TCP Retransmission] 53302 > 9999 [PSH, ACK] Seq=1 Ack=1 Len=5
15.599 127.0.0.1 -> 127.0.0.1 TCP 74 9999 > 53302 [SYN, ACK] Seq=0 Ack=1 Len=0
15.599 127.0.0.1 -> 127.0.0.1 TCP 66 [TCP Dup ACK 16#1] 53302 > 9999 [ACK] Seq=6 Ack=1 Len=0
26.491 127.0.0.1 -> 127.0.0.1 TCP 71 [TCP Retransmission] 53302 > 9999 [PSH, ACK] Seq=1 Ack=1 Len=5
31.599 127.0.0.1 -> 127.0.0.1 TCP 74 9999 > 53302 [SYN, ACK] Seq=0 Ack=1 Len=0
31.599 127.0.0.1 -> 127.0.0.1 TCP 66 [TCP Dup ACK 19#1] 53302 > 9999 [ACK] Seq=6 Ack=1 Len=0
53.179 127.0.0.1 -> 127.0.0.1 TCP 71 [TCP Retransmission] 53302 > 9999 [PSH, ACK] Seq=1 Ack=1 Len=5
106.491 127.0.0.1 -> 127.0.0.1 TCP 71 [TCP Retransmission] 53302 > 9999 [PSH, ACK] Seq=1 Ack=1 Len=5
106.491 127.0.0.1 -> 127.0.0.1 TCP 54 9999 > 53302 [RST] Seq=1 Len=0

Tomcat

The HTTP Connector
acceptCount 控制accept queue长度,默认值为100
maxThreads 最大处理线程数,默认值为200
maxConnections 最大连接数,NIO下默认值为10000,BIO(8.5版本废弃)下同maxThreads

Tuning Tomcat For A High Throughput, Fail Fast System
计算实际处理能力,当超出处理能力时尽量fail fast,防止影响全局性能