如何调试未关闭的CLOSE_WAIT连接的原因?(tcpdump 等)

Mur*_*xim 6 networking tcp nginx tcpdump wireshark

我们将 Java 应用程序和 Nginx 作为反向代理安装在同一主机上。定期地,我们会发现CLOSE_WAIT连接长时间挂起:

$ ss -n4t | head
State      Recv-Q Send-Q  Local Address:Port    Peer Address:Port
CLOSE-WAIT 1      0           127.0.0.1:8180       127.0.0.1:36599
CLOSE-WAIT 1      0           127.0.0.1:8180       127.0.0.1:36467
CLOSE-WAIT 1      0           127.0.0.1:8180       127.0.0.1:36154
Run Code Online (Sandbox Code Playgroud)

而且数量不断增加,那么应用程序就会出现问题。互联网上说:

Your server is failing to detect client disconnects, or ignoring them, and not closing the socket.
Run Code Online (Sandbox Code Playgroud)

Nginx 说:

2020/06/28 04:59:15 [error] 65506#0: *31719640 recv() failed (104: Connection reset by peer) while reading response header from upstream, client: 55.55.55.55, server: app.mycompany.com, request: "POST /url/url HTTP/1.0", upstream: "http://127.0.0.1:8180/url/url/provider", host: "app.mycompany.com"
Run Code Online (Sandbox Code Playgroud)

好的,tcpdump在正常行为期间显示:

2020-06-08 06:58:23.073139 IP 127.0.0.1.8180 > 127.0.0.1.57786: Flags [P.], seq 1:738, ack 1211, win 1365, options [nop,nop,TS val 2780380992 ecr 2780380974], length 737
2020-06-08 06:58:23.073233 IP 127.0.0.1.8180 > 127.0.0.1.57786: Flags [F.], seq 738, ack 1211, win 1365, options [nop,nop,TS val 2780380992 ecr 2780380992], length 0
2020-06-08 06:58:23.073302 IP 127.0.0.1.57786 > 127.0.0.1.8180: Flags [F.], seq 1211, ack 739, win 353, options [nop,nop,TS val 2780380992 ecr 2780380992], length 0
Run Code Online (Sandbox Code Playgroud)

[F.]双方都表示连接已正确关闭(基于图表

接下来,tcpdump在异常行为期间(CLOSE_WAIT出生时)显示:

2020-06-08 06:55:30.282015 IP 127.0.0.1.8180 > 127.0.0.1.57160: Flags [.], ack 1380, win 1365, options [nop,nop,TS val 2780208201 ecr 2780208201], length 0
2020-06-08 06:57:10.279006 IP 127.0.0.1.57160 > 127.0.0.1.8180: Flags [F.], seq 1380, ack 1, win 342, options [nop,nop,TS val 2780308198 ecr 2780208201], length 0
2020-06-08 06:57:10.318432 IP 127.0.0.1.8180 > 127.0.0.1.57160: Flags [.], ack 1381, win 1365, options [nop,nop,TS val 2780308238 ecr 2780308198], length 0
Run Code Online (Sandbox Code Playgroud)

这里我们只看到一个[F.]

我已经阅读了数千篇文章,但仍然无法回答以下问题:

tcpdump[F.]标志的实际含义是什么?文档说:Placeholder, usually used for ACK.,但是第一个初始 FIN (没有 ACK)在哪里,例如:

this one? --> 2020-06-08 06:58:23.073233 IP 127.0.0.1.57786 > 127.0.0.1.8180: Flags [F], seq 738, ack 1211, win 1365, options [nop,nop,TS val 2780380992 ecr 2780380992], length 0
2020-06-08 06:58:23.073233 IP 127.0.0.1.8180 > 127.0.0.1.57786: Flags [F.], seq 738, ack 1211, win 1365, options [nop,nop,TS val 2780380992 ecr 2780380992], length 0
Run Code Online (Sandbox Code Playgroud)

我的问题是,tcpdump将FIN和FIN/ACK合并成[F.]flag吗?如果是这样,在正常行为期间我们会看到以下操作顺序:

  1. 客户端向服务器发送FIN:omitted by tcpdump
  2. 服务器向客户端发送FIN/ACK:127.0.0.1.8180 > 127.0.0.1.57786: Flags [F.]
  3. 服务器向客户端发送FIN:omitted by tcpdump
  4. 客户端向服务器发送FIN/ACK:127.0.0.1.57786 > 127.0.0.1.8180: Flags [F.]

如果是这样,在异常行为期间,客户端不会向我们发送 FIN,因此,我们的服务器不会发送 FIN/ACK,即 FIN/ACK [F.]正确的?

第二个问题是 Nginx。据我了解netstat

CLOSE-WAIT 1      0           127.0.0.1:8180       127.0.0.1:36467
Run Code Online (Sandbox Code Playgroud)

连接是从本地主机到本地主机。是因为 Nginx 反向代理吗?这就是为什么我看不到真实客户端 IP 的原因?如果是这样,是否是 Java 应用程序和 Nginx 之间的问题?我这样问是因为我想调试/监视 HTTP 流量,包括从本地主机到本地主机的请求和响应标头以及消息正文:

tcpdump -A -s 0 'tcp port 8180 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)' -i lo
Run Code Online (Sandbox Code Playgroud)

因为我们在 Nginx 上有 SSL 终止,所以我相信我会看到服务器/客户端流程内部发生了什么。第三个问题是,它是否正确/是否有更多方法可以清楚地理解我们得到“CLOSE_WAIT”的根本原因?