修复太多打开的文件异常(我正在使用 try-catch-finally)

Fixing too many open files Exception(I am using try-catch-finally)

我在 JAVA(1.8 版)上编写了 Web 服务,它通过套接字连接 HSM 和 sends/receives 数据。我的应用程序部署在 linuxApache Tomcat/8.5.14

虽然我正在正确关闭套接字连接,但我有

java.net.SocketException: Too many open files

这是我的class

public class myClass implements AutoCloseable {
    Socket socket;
    DataInputStream in;
    DataOutputStream out;

    public myClass(String ip, int port) throws Exception {
        try {
            socket = new Socket(ip, port);
            in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
            out = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
        } catch (IOException e) {
            throw new Exception("Connecting to HSM failed" + e);
        }
    }       

   public String sendCommandToHsm(String command) throws IOException {
        out.writeUTF(command);
        out.flush();
        return in.readUTF();
    }

    @Override
    public void close() {
        if (socket != null && !socket.isClosed()) {
            try {
                socket.close();
            } catch (IOException e) {
                lgg.info("Closing of socket failed", e);
            }
        }

        if (in != null) {
            try {
                in.close();
            } catch (IOException e) {
                lgg.info("Closing of inputStream failed", e);
            }
        }

        if (out != null) {
            try {
                out.close();
            } catch (IOException e) {
                lgg.info("Closing of outputStream failed", e);
            }
        }
    }

}

这里是使用我的 class

try (MyClass myClass = new MyClass(ip, port);) {
      myClass.sendCommandToHsm("my command");
 }

我将服务器上的最大打开文件限制从默认值 (1024) 增加到 8192,几次后同样的 Exception 再次发生。

我正在考虑创建 Socket Connection Pool,这是个好主意吗?

你能提出任何其他解决方案吗?

Although I'm closing socket connection properly ...

看起来你是,但我认为有几个问题。 (我不知道这些是你泄漏的原因,但第一个是一个合理的解释。)

问题 1.

public myClass(String ip, int port) throws Exception {
    try {
        socket = new Socket(ip, port);
        in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
        out = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
    } catch (IOException e) {
        throw new Exception("Connecting to HSM failed" + e);
    }
}  

如果在设置流期间抛出异常,则套接字将泄漏。

问题 2.

public void close() {
    if (socket != null && !socket.isClosed()) {
        try {
            socket.close();
        } catch (IOException e) {
            lgg.info("Closing of socket failed", e);
        }
    }

    if (in != null) {
        try {
            in.close();
        } catch (IOException e) {
            lgg.info("Closing of inputStream failed", e);
        }
    }

    if (out != null) {
        try {
            out.close();
        } catch (IOException e) {
            lgg.info("Closing of outputStream failed", e);
        }
    }
}

您关闭的顺序错误。在关闭 socket 之前,您应该1 关闭 inout。特别是,如果 out 有缓冲数据,则关闭 out 将尝试刷新...如果您已经关闭 socket,这将失败。

此外,如果 socket.close()in.close() 由于 IOException 以外的其他原因失败,则将跳过后续关闭。所以你应该在这里使用finally

此外,isClosed() 调用是多余的。在已经关闭的资源上调用 close() 应该什么都不做。这是 close() 合同的一部分。

最后,在套接字上调用 close() 应该2 自动关闭 inout 下的低级文件描述符。所以可以说最好只这样做:

public void close() {
    if (socket != null) {
        try {
            socket.close();
        } catch (IOException e) {
            lgg.info("Closing of socket failed", e);
        }
    }
}

如果这不能解决您的泄漏,我建议您使用 netstatlsof 来尝试找出泄漏是打开的文件还是打开的套接字。


I'm thinking about creating Socket Connection Pool, is it good idea?

是的...如果您能找到满足您要求的现有(设计和测试良好的)库。从头开始实施可靠的矿池并非易事。

但注意:

  1. 未正确实施(或使用)的池可能会泄漏文件描述符。
  2. 服务器端需要能够在同一个连接上处理一系列请求/回复。
  3. 如果您有太多同时打开的连接到不同的 地方,那么池需要一种方法来关闭其中的一些...

1 - 是否应该在 in 之前关闭 out 是有争议的。一方面,关闭 out 会将未完成的数据刷新到服务器。另一方面,在 myClass.close() 被调用时,不会有任何内容读取服务器的响应。此外,sendCommandToHsm 方法会刷新...因此不应有任何未完成的数据。

2 - Socket.close() 的 javadoc 说:"Closing this socket will also close the socket's InputStream and OutputStream."