我在JAVA(1.8版)上编写了Web服务,该服务连接HSM并通过套接字发送/接收数据。我的应用程序部署在Apache Tomcat/8.5.14linux上。

虽然我正确关闭了套接字连接



这是我的课

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);
            }
        }
    }

}

这是我类的使用
try (MyClass myClass = new MyClass(ip, port);) {
      myClass.sendCommandToHsm("my command");
 }

我将服务器上的最大打开文件限制从默认值(1024)增加到8192,几次之后,再次出现了相同的Exception

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

您能提出其他解决方案吗?

最佳答案



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

问题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);
        }
    }
}

您关闭顺序错误。您应该先关闭inout,然后再关闭socket。特别是,如果out已缓冲数据,则关闭out将尝试刷新...如果已经关闭socket,则失败。

同样,如果socket.close()in.close()出于IOException之外的其他原因而失败,则随后的关闭将被跳过。因此,您应该在此处使用finally

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

最后,在套接字上调用close()应该会自动关闭inout下的低级文件描述符。因此,最好是这样做:
public void close() {
    if (socket != null) {
        try {
            socket.close();
        } catch (IOException e) {
            lgg.info("Closing of socket failed", e);
        }
    }
}

如果这不能解决泄漏问题,建议您使用netstatlsof尝试查找泄漏是否是打开的文件或打开的套接字。



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

但请注意:
  • 错误实现(或使用)的池可能会泄漏文件描述符。
  • 服务器端需要能够处理在同一连接上的一系列请求/回复。
  • 如果同时打开到不同位置的连接过多,则该池需要一种关闭其中一些连接的方法...


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

    2-Socket.close()的Javadoc说:“关闭此套接字还将关闭套接字的InputStreamOutputStream。”

    关于java - 修复了太多打开的文件异常(我最终使用try-catch),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/47635910/

    10-13 04:55