### 问题背景

我开发了一个 Web 系统,有一个功能需要提供一个 HTTP REST 接口供**设备端**调用,我的 Web 用 Java 开发,设备的用的是 Labview ,他的代码具体怎么实现我不清楚。

系统在运行一段时间后,客户反馈 HTTP 接口偶发调用失败,但是我通过查询日志,发现在上报接口无法调用的时刻,后台日志中接口都没有被触发。这个时候想到了看一下 TCP 连接数,监控发现 Web 系统有很多 CLOSE_WAIT 的 TCP 连接。

        netstat -an | grep "8080" | grep 'CLOSE_WAIT'
   
后面发现一个规律,随着 CLOSE_WAIT 连接越来越多,各个设备反馈接口无法调用的频率也随之增多。

### 排查过程

1. 先分析服务端的 CLOSE_WAIT 连接,发现一个特别怪异的事,服务端存在某个客户端(设备)的连接,但是在设备端,同样执行 netstat ,居然没有与服务端对应的 tcp 连接。从表象上来说,客户端不知道用什么方式杀死了连接,服务端还保留了 socket 。很奇怪啊,按照正常的逻辑,客户端应该存在很多 FIN-WAIT-2 的连接。

![四次挥手]( https://i.imgur.com/QH2C9T7.png  )

2. 于是分别在出问题的情况下,使用 tcpdump 和 wirehark 抓了服务器和某几个存在 CLOSE_WAIT 连接的设备的网络包,发现在设备端反馈 FIN 关闭连接时,会出现一个 RST 的帧,并且 8080 不会回 FIN,只会 ACK ,导致 CLOSE_WAIT 的出现。但是,这种问题具体出现的原因,我一直想不明白。

![抓包]( https://i.imgur.com/5iyEQ3j.png )

![抓包]( https://i.imgur.com/FRzI4uK.png )

3. 经过一番折腾,我考虑能不能通过服务器配置或者主动杀死 tcp 连接的方式减少 CLOSE_WAIT 连接,来尽可能减少 CLOSE_WAIT 堆积引起的高频率 CLOSE_WAIT 问题(解决不了问题,就解决出现问题的地方)。 我采用的是 killcx https://killcx.sourceforge.net/ ,来模拟 SYNC ,主动关闭连接。但是,不出意外,还是除了意外,连接关不掉!!!其他的连接都可以,但是就是 CLOSE_WAIT 的无法关闭。

4. 最后,只能定时重启 JAVA 程序,主动释放连接才能恢复,在重启的三四天时间不会有问题,过了一个时间点,这个问题又出问题了。。。周而复始。。。

请大家帮忙分析分析,为啥会出现服务器( Linux )保持了 TCP 连接,但是客户端( Windows )没有。到底是我 JAVA 应用的问题,还是网络问题抑或者是客户端关闭连接的问题?给提供点思路,拜求了!
举报· 357 次点击
登录 注册 站外分享
24 条回复  
dode 小成 2024-10-11 18:58:29
CLOSE_WAIT 问题 配置相关内核参数
mango88 小成 2024-10-11 19:00:21
60s 服务端没发送 FIN 帧, 大概率是你的代码有问题
1423 小成 2024-10-11 19:11:25
是你 JAVA 应用的问题
palfortime 小成 2024-10-11 19:21:20
1. 服务端用了哪个 java 的框架。
2. 客户端与服务端之间是直连吗?有用了正向或反向代理吗?
3. 服务端有发起其它 TCP 连接吗?确认 close_wait 的连接是客户端请求服务端的?
Monad 小成 2024-10-11 19:22:34
close_wait 不是你服务端没有 close 连接嘛 又不是 time_wait 大量 close_wait 是 bug 吧
Mohanson 初学 2024-10-11 19:22:49
拒绝废话: 服务端没有调用 accept 或者 accept 失败, 大概率后者

详细解释: http://accu.cc/content/go/socket_not_accept/
clester 初学 2024-10-11 19:25:26
对双端发送 reset😏
tyrantZhao 小成 2024-10-11 19:26:19
close_wait 堆积一般都是短链接集中关闭或者服务过于频繁来不及关闭,需要根据具体情况来决定怎么改。(背的八股用上了?)
ysc3839 小成 2024-10-11 19:30:38
感觉是因为你没把 write stream 关掉,对端一直等不到你的 FIN ,超时发送 RST 了。
我个人觉得“四次挥手”这说法有问题,我个人会称作“两端关闭”。TCP 连接可以看成两条方向相反的管道,其中一端发送 FIN ,代表关闭自己这端的写管道(即对端的读管道),一端关闭后,另一端还是能继续写数据的,直到对端也关闭管道,两条管道都关闭了,TCP 连接断开。
如果用“四次挥手”的话,就会给人感觉一端发送 FIN 后,另一端也会自动发送 FIN 关闭连接,但其实并不是,需要主动关闭。
同时没记错的话 FIN 是可以和其他包合并的,就会出现不是正好四个包断开连接的情况。
123下一页
返回顶部