[技术求教] 如何主动杀死一个 tcp 连接?请大家帮我分析分析。

11 天前
 aizya

问题背景

我开发了一个 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 的连接。

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

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

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

请大家帮忙分析分析,为啥会出现服务器( Linux )保持了 TCP 连接,但是客户端( Windows )没有。到底是我 JAVA 应用的问题,还是网络问题抑或者是客户端关闭连接的问题?给提供点思路,拜求了!

1805 次点击
所在节点    程序员
24 条回复
dode
11 天前
CLOSE_WAIT 问题 配置相关内核参数
mango88
11 天前
60s 服务端没发送 FIN 帧, 大概率是你的代码有问题
1423
11 天前
是你 JAVA 应用的问题
palfortime
11 天前
1. 服务端用了哪个 java 的框架。
2. 客户端与服务端之间是直连吗?有用了正向或反向代理吗?
3. 服务端有发起其它 TCP 连接吗?确认 close_wait 的连接是客户端请求服务端的?
Monad
11 天前
close_wait 不是你服务端没有 close 连接嘛 又不是 time_wait 大量 close_wait 是 bug 吧
Mohanson
11 天前
拒绝废话: 服务端没有调用 accept 或者 accept 失败, 大概率后者

详细解释: http://accu.cc/content/go/socket_not_accept/
clester
11 天前
对双端发送 reset😏
tyrantZhao
11 天前
close_wait 堆积一般都是短链接集中关闭或者服务过于频繁来不及关闭,需要根据具体情况来决定怎么改。(背的八股用上了?)
Mohanson
11 天前
还有一种可能就是服务端没有调用 Close. 总之是代码写的有问题.
ysc3839
11 天前
感觉是因为你没把 write stream 关掉,对端一直等不到你的 FIN ,超时发送 RST 了。
我个人觉得“四次挥手”这说法有问题,我个人会称作“两端关闭”。TCP 连接可以看成两条方向相反的管道,其中一端发送 FIN ,代表关闭自己这端的写管道(即对端的读管道),一端关闭后,另一端还是能继续写数据的,直到对端也关闭管道,两条管道都关闭了,TCP 连接断开。
如果用“四次挥手”的话,就会给人感觉一端发送 FIN 后,另一端也会自动发送 FIN 关闭连接,但其实并不是,需要主动关闭。
同时没记错的话 FIN 是可以和其他包合并的,就会出现不是正好四个包断开连接的情况。
arloor
11 天前
被动关闭方(这里是 linux 服务端)没有调用 socket 的 close 方法,也就是没有发出 FIN 。
所以你 java 应用到底用的啥框架,如果是 spring 应该不会有这种问题。
如果是手搓的,那大概就是没有 close
cppc
11 天前
CLOSE_WAIT 一般是你自己没有关闭连接导致的,如果你是自己手搓的 Web 服务端网络框架的话,注意排查一下
oneisall8955
11 天前
这是一个好问题,mark
seWindows
10 天前
CLOSE_WAIT 是你忘了关了,对端断开是 TIME-WAIT/FIN-WAIT
kangyue9999
10 天前
杀死制造请求的人
onesuren
10 天前
现在看还是像是后端得问题 。https://blog.csdn.net/u013467442/article/details/90375361
cheng6563
10 天前
你 Web 服务是用框架启的吧,框架应该不会忘记 Close 。

翻下 std 有无输出 OOM 错误吧。我是建议直接加 -XX:+ExitOnOutOfMemoryError
aizya
10 天前
@palfortime

1. 服务端提供 REST 接口使用的是 RESTeasy ( JAX-RS )
2. 直连的,没有用代理
3. 服务端没有再调其他三方的接口,CLOSE_WAIT 对应的 ip 地址对应的就是客户端的。

系统运行一段时间后才会慢慢出现 CLOSE_WAIT 问题,如果是本身没有关闭连接应该一请求就出来,很困惑不清楚如何排查。
aizya
10 天前
@Mohanson #6 感谢,这篇文章是目前看到最匹配我遇到的问题的。但是您提到的 accept 失败,是由于对应 JAVA 进程的 open files 增多引起的?我翻看了日志,没有体现 too much open files 相关的日志,请问有其他排查的方法吗😭
aizya
10 天前
@onesuren 受教了,感觉这个跟我的问题还不太一样,文章中是由于有代码中增加了休眠(运行时间过长引起的。)应该是每次请求都会增加一条 CLOSE_WAIT? 我的情况是系统运行前几天一切正常,之后就会出现问题。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1079318

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX