我在JAVA(1.8版)上编写了Web服务,该服务连接HSM并通过套接字发送/接收数据。我的应用程序部署在Apache Tomcat/8.5.14
的linux
上。
虽然我正确关闭了套接字连接
这是我的课
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);
}
}
}
您关闭顺序错误。您应该先关闭
in
和out
,然后再关闭socket
。特别是,如果out
已缓冲数据,则关闭out
将尝试刷新...如果已经关闭socket
,则失败。同样,如果
socket.close()
或in.close()
出于IOException
之外的其他原因而失败,则随后的关闭将被跳过。因此,您应该在此处使用finally
。同样,
isClosed()
调用是多余的。在已经关闭的资源上调用close()
应该什么都不做。这是close()
契约(Contract)的一部分。最后,在套接字上调用
close()
应该会自动关闭in
和out
下的低级文件描述符。因此,最好是这样做:public void close() {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
lgg.info("Closing of socket failed", e);
}
}
}
如果这不能解决泄漏问题,建议您使用
netstat
和lsof
尝试查找泄漏是否是打开的文件或打开的套接字。是的...如果您可以找到一个满足您要求的现有(经过良好设计和测试)的库。从头开始实现可靠的池并非易事。
但请注意:
1-是否应该在
out
之前关闭in
尚有争议。一方面,关闭out
将未完成的数据刷新到服务器。另一方面,在调用myClass.close()
时,将没有任何内容可以读取服务器的响应。此外,sendCommandToHsm
方法会刷新...因此不应有任何未完成的数据。2-
Socket.close()
的Javadoc说:“关闭此套接字还将关闭套接字的InputStream
和OutputStream
。”关于java - 修复了太多打开的文件异常(我最终使用try-catch),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/47635910/