我有一个应用程序用完了文件描述符,显然是由于打开套接字造成的,但我无法确切地找出这些套接字的作用。这些在 lsof 输出中显示为
java 9689 appuser 1010u sock 0,5 263746675 can't identify protocol
java 9689 appuser 1011u sock 0,5 263746676 can't identify protocol
java 9689 appuser 1012u sock 0,5 263746677 can't identify protocol
java 9689 appuser 1014u sock 0,5 263746678 can't identify protocol
java 9689 appuser 1015u sock 0,5 263746679 can't identify protocol
java 9689 appuser 1016u sock 0,5 263746681 can't identify protocol
并在 /proc/$PID/fd 中
lrwx------ 1 appuser appuser 64 Jun 23 11:49 990 -> socket:[263732085]
lrwx------ 1 appuser appuser 64 Jun 23 11:49 991 -> socket:[263732086]
lrwx------ 1 appuser appuser 64 Jun 23 11:49 992 -> socket:[263735307]
lrwx------ 1 appuser appuser 64 Jun 23 11:49 993 -> socket:[263732088]
lrwx------ 1 appuser appuser 64 Jun 23 11:49 995 -> socket:[263735308]
lrwx------ 1 appuser appuser 64 Jun 23 11:49 996 -> socket:[263735309]
lrwx------ 1 appuser appuser 64 Jun 23 11:49 997 -> socket:[263745434]
lrwx------ 1 appuser appuser 64 Jun 23 11:49 998 -> socket:[263745435]
lrwx------ 1 appuser appuser 64 Jun 23 11:49 999 -> socket:[263745436]
但没有类似的输出netstat -a
。
这些插座是什么?我怎样才能知道它们的用途?
编辑:我尝试过跑步grep $SOCKET /proc/net
,按照 lsof 常见问题解答,其中 $SOCKET 例如为 263746679,但是也没有结果。
作为背景,该应用程序是多个任务的容器,其中包括执行网络调用。我需要找出哪个出现故障,但直到我找出这些套接字与谁通信之前,我都束手无策了。
答案1
如果您创建套接字,但从未对其执行 connect() 或 bind(),则可能会发生这种情况。最好的办法可能是对应用程序进行 strace (-fF),然后交叉引用 lsof 的输出以确定哪些套接字导致了问题。作为额外的调试方法:如果您将套接字调用与调试信息包装在一起并将它们写入 /dev/null,它将出现在 strace 中,而不会给您提供大得可笑的日志文件。
答案2
使用 Python,我在 SSL 套接字上遇到了同样的问题:
- 当我使用 socket.close() 时,套接字会无限期地保持 CLOSE_WAIT 状态
- 当我使用 socket.shutdown() 时,lsof 说“无法识别协议”
解决方案是在关闭之前解开 SSL 层:
- origsock = socket.unwrap()
- origsock.close()
这将正确关闭我的应用程序中的套接字。
答案3
我要做的第一件事就是增加文件描述符限制:
~# vi /etc/sysctl.conf
fs.file-max = 331287
接下来,我要确保您的系统是最新的,包括所有库和服务器。您的 Java 应用服务器可能已过时(如果您正在使用)。您的应用服务器也可能配置错误,您应该查看配置文件并降低您的connectionTimeout
和/或您的maxKeepAliveRequests
(我不确定您使用的是哪种应用服务器,或者您是否在使用……)。
我不确定这个应用程序是做什么的,但如果你认为它不需要数万个套接字,那么这几乎肯定是一个“文件描述符泄漏”在您的 Java 应用程序中。您可能需要向供应商发送错误报告。在此错误报告中,您应该包含有关如何重现问题的信息。
以下是调试该问题的一些方法。
Wireshark(或用于 CLI 的 twireshark)是查看这些套接字使用情况的最佳工具。Wireshark 将为您提供通过网络传输的流量类型的细分。前几个连接可能会成功,然后会达到文件描述符限制。一旦达到文件描述符限制,Wireshark 将无法检测到任何内容(就此而言,netstat 更简洁),但这将有助于缩小问题范围。可能存在发送大量传出 SYN 的情况,但是没有收到 SYN/ACK,因此许多 tcp 连接只是停留在 SYN_WAIT 状态。
如果您有权访问源代码并且知道正在创建的套接字类型(例如使用 strace 或仅搜索代码),那么您可以在 Eclipse(或其他 IDE)中打开项目并在创建这些套接字的函数处设置断点。当到达断点时,您可以查看堆栈跟踪。此文件描述符泄漏可能是一个简单的无限循环,或者套接字超时值太大。另一种可能性是 Java 应用程序没有执行socket.close()
清理连接的操作。执行关闭通常在块中完成finely
(try/catch
是的,Java 中的套接字必须始终具有 try/catch,否则它不会构建 :)。归根结底,Java 应用程序可能没有正确处理其 IOException。