为什么在 Web 服务停止工作时,我看到很多套接字处于CLOSE_WAIT状态?

2022-09-02 22:08:42

我在Jetty上运行的java Web服务在几个小时后就掉了下来,调查显示许多套接字处于CLOSE_WAIT状态。虽然它工作正常,但似乎没有插槽处于CLOSE_WAIT状态,但是当它出错时,会有负载。

我发现了这个定义

CLOSE-WAIT:本地端点已收到连接终止请求并确认,例如,已执行被动关闭,本地端点需要执行主动关闭才能离开此状态。

在我的服务器上使用netstat,我看到CLOSE_WAIT状态的tcp套接字列表,本地地址是我的服务器,外部地址是我的负载平衡器计算机。因此,我假设这意味着客户端(负载平衡器)刚刚以某种不正确的方式在其末端终止了连接,并且我的服务器尚未在其末端正确关闭连接。

但是我该怎么做,我的Java代码不处理低级套接字?

或者,负载均衡器终止连接是因为服务器在代码中执行错误而导致的早期问题。


答案 1

听起来像是Jetty或JVM中的一个错误,也许这个解决方法会对你有用:http://www.tux.hk/index.php?entry=entry090521-111844

将以下行添加到 /etc/sysctl.conf

net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_keepalive_intvl = 2
net.ipv4.tcp_keepalive_probes = 2
net.ipv4.tcp_keepalive_time = 1800

然后执行

sysctl -p

或重新启动


答案 2

我怀疑这可能是导致服务器代码中长时间或无限循环/无限等待的原因,而Jetty根本没有机会关闭连接(除非有某种超时在一段时间后强制关闭套接字)。请考虑以下示例:

public class TestSocketClosedWaitState
{
    private static class SocketResponder implements Runnable
    {
        private final Socket socket;

        //Using static variable to control the infinite/waiting loop for testing purposes, with while(true) Eclipse would complain of dead code in writer.close() -line
        private static boolean infinite = true;

        public SocketResponder(Socket socket)
        {
            this.socket = socket;
        }       

        @Override
        public void run()
        {
            try
            {               
                PrintWriter writer = new PrintWriter(socket.getOutputStream()); 
                writer.write("Hello");              

                //Simulating slow response/getting stuck in an infinite loop/waiting something that never happens etc.
                do
                {
                    Thread.sleep(5000);
                }
                while(infinite);

                writer.close(); //The socket will stay in CLOSE_WAIT from server side until this line is reached
            }
            catch(Exception e)
            {
                e.printStackTrace();
            }           

            System.out.println("DONE");
        }
    }

    public static void main(String[] args) throws IOException
    {
        ServerSocket serverSocket = new ServerSocket(12345);

        while(true)
        {
            Socket socket = serverSocket.accept();
            Thread t = new Thread(new SocketResponder(socket));
            t.start();
        }       
    }
}

将 -变量设置为 true 时,Printwriter(和底层套接字)永远不会由于无限循环而关闭。如果我运行这个并使用telnet连接到套接字,然后退出telnet客户端,将显示服务器端套接字仍处于-状态(我也可以看到客户端套接字处于FIN_WAIT2状态一段时间,但它会消失):infinitenetstatCLOSE_WAIT

~$ netstat -anp | grep 12345
tcp6       0      0 :::12345        :::*            LISTEN      6460/java       
tcp6       1      0 ::1:12345       ::1:34606       CLOSE_WAIT  6460/java   

服务器端接受的套接字卡在 CLOSE_WAIT -状态。如果我检查进程的线程堆栈,我可以看到线程在do...而 -循环:

~$ jstack 6460

<OTHER THREADS>

"Thread-0" prio=10 tid=0x00007f424013d800 nid=0x194f waiting on condition [0x00007f423c50e000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
    at java.lang.Thread.sleep(Native Method)
    at TestSocketClosedWaitState$SocketResponder.run(TestSocketClosedWaitState.java:32)
    at java.lang.Thread.run(Thread.java:701)

<OTHER THREADS...>

如果我将 -变量设置为 false,并执行相同的操作(连接客户端和断开连接),则具有 -state 的套接字将显示,直到编写器关闭(关闭底层套接字),然后消失。如果编写器或套接字从未关闭,即使线程终止,服务器端套接字也会再次卡住(我不认为这应该发生在Jetty中,如果你的方法在某个时候返回,Jetty可能应该负责关闭套接字)。infiniteCLOSE_WAITCLOSED_WAIT

因此,我建议您尝试找到罪魁祸首的步骤是

  • 将日志记录添加到您的方法中,以查看它们正在做什么
  • 检查你的代码,是否有任何地方的执行可能会陷入无限循环或需要很长时间,从而阻止底层套接字被关闭?
  • 如果仍然发生,请在下次出现此问题时从正在运行的 Jetty 进程中获取线程转储,并尝试识别任何“卡住”的线程jstack
  • 是否有可能抛出一些东西(OutOfMemoryError等),这些东西可能不会被调用你的方法的底层Jetty架构捕获?我从来没有偷看过Jetty的内部,它很可能是捕捉到的,所以这可能不是问题,但如果所有其他方法都失败了,也许值得检查一下。Throwable

当线程进入和退出您的方法时,您还可以使用类似如下方式命名线程

        String originalName = Thread.currentThread().getName();
        Thread.currentThread().setName("myMethod");

        //Your code...

        Thread.currentThread().setName(originalName);

如果有很多线程正在运行,可以更容易地发现它们。


推荐