【问题标题】:Why am i seeing lots of sockets in CLOSE_WAIT status when webservice stops working?当 web 服务停止工作时,为什么我看到很多处于 CLOSE_WAIT 状态的套接字?
【发布时间】:2015-03-05 10:20:50
【问题描述】:

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

我找到了这个definition

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

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

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

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

【问题讨论】:

  • 标记为重复的问题看起来很相似,但解决方案没有帮助,因为它表明问题出在客户端,但我们不能仅通过服务器控制客户端,因此我们需要为服务器提供一种方法即使客户做错了什么也要应对。
  • 问题不在于客户端,而在于服务器,或者更确切地说,无论哪一端显示 CLOSE_WAIT。 @Kayaman 不是真的重复,因为另一个是关于有这种情况的客户,并且有一个仅限客户的解决方案。
  • @EJP 哦,好吧,我不明白我该如何解决这个问题我的 servlet 中有一个 doGet(HttpServletRequest request, HttpServletResponse response) 方法,其中很多都执行 response.redirect(), response.sendError( ) 或更常见的 PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(response.getOutputStream(), CHARSET))); writer.write(out, results, responseFormat, isPretty); out.close();然后返回。我不直接处理套接字,所以我该如何解决这个问题?
  • 我认为你必须倒过来因果。 CLOSE_WAIT 中的套接字是服务器倒下的症状,而不是倒下的原因。
  • 只有 client 发送一个 FIN 才能产生 CLOSE_WAIT,如果它持续存在是服务器 failing 做某事造成的,即关闭套接字。 @DavidSchwartz 是对的,你有这一切。

标签: java tomcat tcp jetty load-balancing


【解决方案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

或者重启

【讨论】:

  • 应用这个CLOSE_WAIT会发生什么?
【解决方案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();
        }       
    }
}

infinite-variable 设置为 true,Printwriter(和底层套接字)永远不会由于无限循环而关闭。如果我运行它并使用 telnet 连接到套接字,然后退出 telnet 客户端,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   

服务器端接受的套接字卡在 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...>

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

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

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

您还可以在线程进入和退出您的方法时使用类似

的名称命名线程
        String originalName = Thread.currentThread().getName();
        Thread.currentThread().setName("myMethod");

        //Your code...

        Thread.currentThread().setName(originalName);

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

【讨论】:

  • 当它停止响应时我得到了一个 jstack,不知道有什么奇怪的地方gist.github.com/mayhem/5f53a3c55e8e110cdb9f
  • 这是一切正常的时候gist.github.com/mayhem/60be5a2134dc3daf8fd0 第一个似乎有一些处于阻塞状态的各种代码线程 - 但什么是阻塞的。当它工作时,我们似乎只有轮询线程等待新的客户端连接。
  • @PaulTaylor:我自己也找不到其他任何东西,除了第一个显示所有内容都处于 BLOCKED 状态(即使没有像 wait() 调用之类的东西)。很难说到底是什么原因造成的,也许是 stop-the-world -garbage collection 经常/不停地运行?停止响应时 CPU 使用率高吗? tomcat 日志中有什么不寻常的地方吗?如果给 JVM 的内存更少,如果给的更多,它会更快发生吗?某处可能存在内存泄漏吗?只是猜测真的......
【解决方案3】:

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

原因是大量的请求是由同步块的业务逻辑处理的。所以当客户端发送数据包丢弃连接时,绑定到这个套接字的线程正忙,等待监控。

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

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]

对于 org.eclipse.jetty.server.HttpOutput at close 方法。我认为关闭步骤中的异常是套接字的 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。正确的解决方案(同样在我们的例子中)是代码重构。

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

【讨论】:

    【解决方案4】:

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

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

    【讨论】:

      【解决方案5】:

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

      【讨论】:

      • Trailer 好吧,我认为问题出在负载均衡器上,但每个人都告诉我它必须是我的服务器,因为 CLOSE_WAITS 在我的服务器上,因此问题出在我的服务器上,但如果负载均衡器无法正常工作可以防止服务器移出 CLOSE_WAIT 连接。对更基本的 http 问题的回答可能会对我有所帮助,客户端向服务器发送 http 请求,然后服务器发送响应。接下来会发生什么,客户端是在收到服务器响应时隐式初始化连接关闭,还是服务器在发送响应后启动连接关闭?
      • 取决于服务器。如果它使用keep-alive,那么通常由客户在完成后关闭它。那可能是你的问题。但是 CLOSE_WAIT 状态可以持续几秒钟,有时甚至几分钟。等待几分钟后状态不会改变吗?您可能正在查看实际上不是问题的东西。
      • 不,它不会改变,当一切正常时,我们不会注意到任何 CLOSE_WAITS,但是当服务器停止响应时,我们会注意到 CLOSE_WAITS 的负载堆积。
      • 听起来负载均衡器正在占用套接字。只是猜测。
      • 这就是我的想法,请帮助我理解负载均衡器是否可以阻止服务器关闭套接字。
      【解决方案6】:

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

      【讨论】:

      • 是的,但是我不直接处理socket,只是HttpResponse和HttpResponse对象,关闭关联的连接是servlet容器(jetty/tomcat)的职责
      • 正如其他人指出的,这可能是 Jetty 中的一个错误。要调查的另一件事是套接字超时和保持活动:serverfault.com/questions/420205/…
      • 实际上我们转移到了 Tomcat 但仍然遇到同样的问题
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-07
      • 2012-04-08
      • 1970-01-01
      • 2014-10-02
      • 1970-01-01
      • 2013-09-08
      • 2010-12-13
      相关资源
      最近更新 更多