当webservice停止工作时,为什么我看到很多CLOSE_WAIT状态的套接字?

时间:2015-03-05 10:20:51

标签: java tomcat tcp jetty load-balancing

我在Jetty上运行的java webservice在几个小时之后就会崩溃,调查显示许多套接字处于CLOSE_WAIT状态。虽然它工作正常但似乎没有CLOSE_WAIT状态的套接字,但是当它出错时会有负载。

我发现了definition

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

在我的服务器上使用netstat,我看到一个CLOSE_WAIT状态的tcp套接字列表,本地地址是我的服务器,外部地址是我的负载均衡器机器。所以我认为这意味着客户端(负载均衡器)刚刚以某种不正当的方式终止了连接,并且我的服务器没有正确地关闭它的连接。

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

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

6 个答案:

答案 0 :(得分:6)

听起来像是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

或重新启动

答案 1 :(得分:5)

我们的项目存在同样的问题。我不确定这是你的情况,但也许会有所帮助。

原因是业务逻辑使用synchronized块处理了大量请求。因此,当客户端发送数据包到drop连接时,绑定到此套接字的线程正忙,等待监视器。

日志在写入方法中显示org.eclipse.jetty.io.WriteFlusher的异常:

DEBUG org.eclipse.jetty.io.WriteFlusher - write - write exception
org.eclipse.jetty.io.EofException: null
    at org.eclipse.jetty.io.ChannelEndPoint.flush
(ChannelEndPoint.java:192) ~[jetty-io-9.2.10.v20150310.jar:9.2.10.v20150310]

和close方法中的org.eclipse.jetty.server.HttpOutput。我认为近距离的例外是套接字的原因。 CLOSE_WAIT状态:

DEBUG org.eclipse.jetty.server.HttpOutput - close -
org.eclipse.jetty.io.EofException: null
    at org.eclipse.jetty.server.HttpConnection$SendCallback.reset
(HttpConnection.java:622) ~[jetty-server-9.2.10.v20150310.jar:9.2.10.v20150310]

我们案例中的快速解决方案是增加idleTimeout。正确的解决方案(在我们的例子中)是代码重构。

所以我的建议是仔细阅读带有DEBUG级别的Jetty日志,以查找异常并使用VisualVM分析应用程序性能。也许原因是性能瓶颈(同步块?)。

答案 2 :(得分:4)

我怀疑这可能是导致服务器代码长时间或无限循环/无限等待的原因,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();
        }       
    }
}

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

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

服务器端接受的套接字卡在CLOSE_WAIT -state中。如果我检查进程的线程堆栈,我可以看到线程在do ... while -loop中等待:

~$ 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...>

如果我将infinite - 变量设置为false,并执行相同操作(连接客户端和断开连接),则会显示带有CLOSE_WAIT -state的套接字,直到编写器关闭(关闭底层) socket),然后消失。如果编写器或套接字永远不会关闭,服务器端套接字将再次陷入CLOSED_WAIT,即使线程终止(我不认为这应该发生在Jetty中,如果你的方法在某些方面返回点,Jetty可能应该关闭插座)。

所以,我建议你尝试找到罪魁祸首的步骤

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

您还可以在使用类似

之类的方式输入和退出方法时命名线程
        String originalName = Thread.currentThread().getName();
        Thread.currentThread().setName("myMethod");

        //Your code...

        Thread.currentThread().setName(originalName);
如果有很多线程正在运行,

更容易发现它们。

答案 3 :(得分:0)

我遇到了类似的问题,而罪魁祸首代码可能有所不同,症状是 1)服务器(Jetty)正在运行但尚未处理请求 2)没有额外的普通负载/例外 3)有太多的CLOSE_WAIT连接。

这些表明服务器中的所有工作线程都被卡在某处。 Jstack Thread转储显示我们所有的工作线程都被困在apache HttpClient对象中。 (由于未关闭的响应对象),并且由于所有线程都在无限期等待,因此没有可用于处理传入请求的线程。

答案 4 :(得分:-1)

负载均衡器是否还在运行?尝试停止负载均衡器,看看这是不是服务器的问题。

答案 5 :(得分:-2)

这可能意味着您没有清理传入的连接。确保每次交易结束时套接字都关闭。 (最好在服务器代码开头附近的finally block内完成,这样即使发生服务器端异常,连接也会关闭。)