Java.net.SocketException: 打开的文件太多

23

我有一个Java应用程序,在Ubuntu 10.04上运行良好,但在几个小时后会出现 "java.net.SocketException: Too many open files" 错误。 Sender.java 的代码可以在这里找到。

这是因为每个线程都创建了 HttpPutHttpPost 的新实例吗? 我正在使用apache-commons HTTPClient 4。

以下是异常日志:

java.net.SocketException: Too many open files
    at java.net.Socket.createImpl(Socket.java:414)
    at java.net.Socket.connect(Socket.java:544)
    at org.apache.http.conn.scheme.PlainSocketFactory.connectSocket(PlainSocketFactory.java:123)
    at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:133)
    at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:149)
    at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:108)
    at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:415)
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:641)
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:576)
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:554)
    at com.marketplace.io.Sender.doBasicHttpPost(Sender.java:434)
    at com.marketplace.io.Sender.appVisualExists(Sender.java:223)
    at com.marketplace.io.Sender.addVisualToCollection(Sender.java:350)
    at com.marketplace.service.ImageThread.run(ImageThread.java:136)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
    at java.util.concurrent.FutureTask.run(FutureTask.java:166)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
    at java.lang.Thread.run(Thread.java:636)

你是否曾经关闭过你的httpclient? - Buhake Sindi
1
抱歉,我没有时间查看所有的代码,但听起来好像你一直没有关闭你打开的套接字。 - Matt Ball
@The Elite Gentleman- 不,我不知道怎么做。HTTPClient的一些文档是不存在的。 - Raunak
4
我将尝试使用httpClient.getConnectionManager().shutdown();,看看它是否有效。 - Raunak
太好了!这就是如何关闭您的httpclient连接管理器。 - Buhake Sindi
5个回答

27

“java.net.SocketException: Too many files open” 会在任何Java服务器应用程序中看到,例如Tomcat、Weblogic、WebSphere等,客户端频繁连接和断开连接。

请注意,套接字连接与文件相同,并使用文件描述符,这是一种有限的资源。

不同的操作系统对它们可以管理的文件句柄数量有不同的限制。

简而言之,此错误出现是因为客户端频繁连接和断开连接。如果您想在您的端处理它,您有两个选项:

1)增加每个进程打开的文件句柄或文件描述符的数量。

在基于UNIX的操作系统(例如Ubuntu或Solaris)中,可以使用命令ulimit -a查找允许每个进程打开多少个文件句柄。

$ ulimit -a
core file size        (blocks, -c) unlimited
data seg size         (kbytes, -d) unlimited
file size             (blocks, -f) unlimited
open files                    (-n) 256
pipe size          (512 bytes, -p) 10
stack size            (kbytes, -s) 8192
cpu time             (seconds, -t) unlimited
max user processes            (-u) 2048
virtual memory        (kbytes, -v) unlimited

你可以看到,打开文件(-n)256,这意味着每个进程只允许256个打开的文件句柄。如果你的Java程序(记得Tomcat、Weblogic或任何其他应用服务器都是Java程序并运行在JVM上)超过了这个限制,它会抛出java.net.SocketException:太多打开的文件错误。
你可以使用ulimit -n将此限制更改为更大的数字,例如4096,但要在UNIX系统管理员的建议下进行操作,并且如果你有单独的UNIX支持团队,最好将其升级给他们。
2)降低操作系统中TIME_WAIT状态的超时时间
在基于UNIX的系统中,你可以查看<强>/proc/sys/net/ipv4/tcp_fin_timeout文件中的当前配置。
在基于Windows的系统中,你可以在Windows注册表中查看此信息。你可以按照以下步骤更改Windows中的TCPTIME_WAIT超时时间:
1) Open Windows Registry Editor, by typing regedit in run command window
2) Find the key HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\tcpip\Parameters
3) Add a new key value pair TcpTimedWaitDelay asa decimal and set the desired timeout in seconds (60-240)
4) Restart your windows machine.

我使用root登录并执行了这些命令。但是当我切换到其他普通用户时,我发现打开的文件数量没有改变。我能为所有用户配置打开文件句柄的数量吗? - niaomingjian
请通过以下方式验证您的设置:tail -f /etc/security/limits.conf。请确保您所做的更改已保存,以便可以应用于所有用户。 - Tung Nguyen
通过使用 ulimit -n 进行验证设置,似乎已经从 1024 更改为 4096(在运行 ulimit -n 4096 后)。但是通过 tail -f /etc/security/limits.conf,我发现文件中没有任何更改。 - niaomingjian
请使用以下命令编辑limits.conf文件:vi /etc/security/limits.conf 将下面两行内容添加到文件末尾,以设置所有用户的软限制和硬限制(*表示适用于所有用户):
  • soft nofile 4096
  • hard nofile 10240
- Tung Nguyen
如果我通过编辑limits.conf文件添加了这两行,运行ulimit -n的结果仍然是之前的值1024。这是正确的吗?我在编辑完这个文件后应该重启我的服务吗? - niaomingjian

15

在第438行,您会得到一个响应流并将其转换为字节数组。由entity.getContent()返回的InputStream没有被关闭。这可能是问题的原因之一。此外,HttpEntity.consumeContent()已被弃用,出于相关原因。


感谢您花时间查看源代码。我将让代码在夜间运行,看看您建议的更改是否有任何差异。 - Raunak
2
我不确定是什么解决了这个问题;是httpClient.getConnectionManager().shutdown(); 还是你的建议。感谢你的帮助。 - Raunak

8

您可能还想检查Linux的最大打开文件限制。这 相关链接 是针对一款定制的基于Java的产品,但它很好地解释了修复问题所需的步骤。


2
还有一个定义,是自“云”以来最令人讨厌的技术术语:http://www.techterms.com/definition/bespoke - David J.
6
我能否因正确使用烦人术语而获得声望分数? ;) - DrUseful

1

已解决

最近我遇到了同样的错误,因为数据库服务器上的var/log已满。

#df -h
S.ficheros            Size  Used Avail Use% Montado en

/dev/cciss/c0d0p3     126G  126G    0G 100% /var


#echo "">/var/log/postgresql/postgresql.log

现在,错误已经消失了!!!
重要提示:查看postgresql.log中记录的内容,检查你的postgresql.conf文件。
再见
@_jpgo

0
我通过在 finally 块中关闭连接来解决问题。
public  static String postMethod(String jsonStr,String sendUrl){
    String resultJson = "";
    HttpClient httpClient = new HttpClient();
    PostMethod post = new PostMethod(sendUrl);
    post.setRequestHeader("Content-Type","application/x-www-form-urlencoded;charset=utf-8");  
    NameValuePair[] param = { new NameValuePair("message",jsonStr)} ;
    post.setRequestBody(param);
    try {
        httpClient.executeMethod(post);
        resultJson = post.getResponseBodyAsString();
    } catch (HttpException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }finally{
         post.releaseConnection();
         ((SimpleHttpConnectionManager)httpClient.getHttpConnectionManager()).shutdown();
    }
    return resultJson;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接