V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
aizya
V2EX  ›  程序员

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

  •  1
     
  •   aizya · 24 天前 · 1936 次点击

    问题背景

    我开发了一个 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 应用的问题,还是网络问题抑或者是客户端关闭连接的问题?给提供点思路,拜求了!

    24 条回复    2024-10-13 02:00:44 +08:00
    dode
        1
    dode  
       24 天前
    CLOSE_WAIT 问题 配置相关内核参数
    mango88
        2
    mango88  
       24 天前
    60s 服务端没发送 FIN 帧, 大概率是你的代码有问题
    1423
        3
    1423  
       24 天前
    是你 JAVA 应用的问题
    palfortime
        4
    palfortime  
       24 天前 via Android
    1. 服务端用了哪个 java 的框架。
    2. 客户端与服务端之间是直连吗?有用了正向或反向代理吗?
    3. 服务端有发起其它 TCP 连接吗?确认 close_wait 的连接是客户端请求服务端的?
    Monad
        5
    Monad  
       24 天前 via iPhone   ❤️ 1
    close_wait 不是你服务端没有 close 连接嘛 又不是 time_wait 大量 close_wait 是 bug 吧
    Mohanson
        6
    Mohanson  
       24 天前   ❤️ 1
    拒绝废话: 服务端没有调用 accept 或者 accept 失败, 大概率后者

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

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

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

    系统运行一段时间后才会慢慢出现 CLOSE_WAIT 问题,如果是本身没有关闭连接应该一请求就出来,很困惑不清楚如何排查。
    aizya
        19
    aizya  
    OP
       23 天前
    @Mohanson #6 感谢,这篇文章是目前看到最匹配我遇到的问题的。但是您提到的 accept 失败,是由于对应 JAVA 进程的 open files 增多引起的?我翻看了日志,没有体现 too much open files 相关的日志,请问有其他排查的方法吗😭
    aizya
        20
    aizya  
    OP
       23 天前
    @onesuren 受教了,感觉这个跟我的问题还不太一样,文章中是由于有代码中增加了休眠(运行时间过长引起的。)应该是每次请求都会增加一条 CLOSE_WAIT? 我的情况是系统运行前几天一切正常,之后就会出现问题。
    seedhk
        21
    seedhk  
       23 天前
    CLOSE_WAIT 很明显就是服务端问题
    导出线程栈信息,找到所有 CLOSE_WAIT 的线程,看堆栈找对应代码
    iceheart
        22
    iceheart  
       23 天前 via Android
    客户端没连接是因为已经 close 了,
    客户端 close 了服务端才有 CLOSE_WAIT
    服务端收到断开通知就要 close
    CLOSE_WAIT 的字面意思就是 等待(应用程序) 来调用 close
    palfortime
        23
    palfortime  
       23 天前 via Android
    @aizya 不了解这个框架,你可以试一下跑个测试的服务,让客户端的进程跑一个请求就退出,看看会不会产生。
    可以在服务端 jstack 一下,看看有没有工作线程是卡住了。可能由于工作线程卡住,导致框架无法处理 close 请求。
    同时可以在服务端加载一下 opentelemetry 的 javaagent 收集一下 trace 和 metric ,看看是否有服务端的异常。
    xxxbin
        24
    xxxbin  
       23 天前
    有意思。那么各位大佬,CLOSE_WAIT 的 tcp 该咋关?
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   979 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 20:53 · PVG 04:53 · LAX 12:53 · JFK 15:53
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.