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

Pau*_*lor 17 java tomcat tcp load-balancing jetty

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

我找到了这个定义

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

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

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

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

Eir*_*iel 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
Run Code Online (Sandbox Code Playgroud)

然后执行

sysctl -p
Run Code Online (Sandbox Code Playgroud)

或者重新启动

  • 应用此 CLOSE_WAIT 会发生什么? (3认同)
  • 这些设置不适用于CLOSE_WAIT状态。 (2认同)

esa*_*saj 5

我怀疑这可能会导致服务器代码中的长时间或无限循环/无限等待,而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();
        }       
    }
}
Run Code Online (Sandbox Code Playgroud)

随着infinite-variable设置为true,则PrintWriter的(和基础套接字)永远不会被关闭,由于无限循环。如果我运行此程序并使用telnet连接到套接字,则退出telnet-client,netstat将显示服务器端套接字仍处于CLOSE_WAIT-state(我也可以看到客户端套接字处于FIN_WAIT2-state一段时间,但它会消失):

~$ netstat -anp | grep 12345
tcp6       0      0 :::12345        :::*            LISTEN      6460/java       
tcp6       1      0 ::1:12345       ::1:34606       CLOSE_WAIT  6460/java   
Run Code Online (Sandbox Code Playgroud)

服务器端接受的套接字陷入CLOSE_WAIT状态。如果我检查进程的线程堆栈,则可以看到线程在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...>
Run Code Online (Sandbox Code Playgroud)

如果将infinite-variable 设置为false,并执行相同的操作(连接客户端并断开连接),则带有CLOSE_WAIT-state 的套接字将一直显示,直到关闭编写器(关闭基础套接字),然后消失。如果writer或socket从未关闭,则CLOSED_WAIT即使线程终止,服务器端套接字也将再次陷入(我不认为这应该在Jetty中发生,如果您的方法在某个时候返回,Jetty可能应该注意关闭插座)。

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

  • 将日志添加到您的方法中,以查看去向/正在执行的操作
  • 检查您的代码,是否有任何地方执行可能陷入无限循环或花费很长时间而导致底层套接字无法关闭?
  • 如果仍然发生,请jstack在下次发生此问题时从正在运行的Jetty进程中进行线程转储,然后尝试识别任何“卡住”的线程
  • 是否有可能会抛出某些东西(OutOfMemoryError等),而这些东西可能不会被调用您的方法的底层Jetty体系结构捕获?我从没有偷看过Jetty的内部结构,很可能会捕捉到Throwables,因此这可能不是问题,但也许值得检查是否所有其他功能都失败了

当它们进入和退出方法时,您还可以使用以下方式命名线程:

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

        //Your code...

        Thread.currentThread().setName(originalName);
Run Code Online (Sandbox Code Playgroud)

如果有很多线程在运行,则更容易发现它们。


Vit*_*nov 5

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

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

日志在write方法中显示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]
Run Code Online (Sandbox Code Playgroud)

以及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]
Run Code Online (Sandbox Code Playgroud)

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

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


小智 -1

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