grails 应用程序中的“打开的文件太多” - 如何正确关闭打开的文件和流?

Posted

技术标签:

【中文标题】grails 应用程序中的“打开的文件太多” - 如何正确关闭打开的文件和流?【英文标题】:"Too many open files" in a grails application - how do I close opened files and streams correctly? 【发布时间】:2012-02-03 05:36:34 【问题描述】:

我正在开发一个相当复杂的 Web 应用程序,它调用几个外部执行的进程、一个 grails 后台进程以及读取/写入多个文件 - 所有这些都在一个控制器中。一切都很好,直到我用近距离的许多请求对其进行了测试。当我这样做时,我在我的 tomcat catalina 日志文件中收到以下 java 错误消息:

WARNING: Exception executing accept
java.net.SocketException: Too many open files
    at java.net.PlainSocketImpl.socketAccept(Native Method)
    at java.net.PlainSocketImpl.accept(PlainSocketImpl.java:408)
    at java.net.ServerSocket.implAccept(ServerSocket.java:462)
    at java.net.ServerSocket.accept(ServerSocket.java:430)
    at org.apache.jk.common.ChannelSocket.accept(ChannelSocket.java:312)
    at org.apache.jk.common.ChannelSocket.acceptConnections(ChannelSocket.java:666)
    at org.apache.jk.common.ChannelSocket$SocketAcceptor.runIt(ChannelSocket.java:877)
    at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:690)
    at java.lang.Thread.run(Thread.java:662)

起初,经过一番愉快的谷歌搜索和阅读后,我怀疑这可能是一个“系统问题”,即我必须提高执行 tomcat 的用户的打开文件限制 - 但事实并非如此。

然后我开始阅读很多关于 Java 的建议,而不是关于 Grails 和 Groovy 的建议,因为如果你在谷歌上搜索 Grails 的这个问题,你找不到那么多。我现在怀疑我的问题是由“打开的流太多”(或类似的东西)引起的,而不是实际打开的文件太多(因为打开的文件数量实际上并没有那么大)。

我在一个闭包中有很多以下四种类型的操作:

1) 打开文件并写入:

def someFile = new File("/some/file.txt")
someFile << "Some content\n"

2) 执行命令:

def cmd = "bash some-cmd".execute()
cmd.waitFor()

3) 从文件中读取内容:

def fileContent = new File("/some/file.txt").text

4) 从网络上的文件中读取内容:

def URL url = new URL("http://www.some.link");
def URLConnection uc = url.openConnection()
def BufferedReader br = new BufferedReader(new InputStreamReader(uc.getInputStream()))
...
br.close()

如您所见,我唯一明确关闭的是带有 InputStream 的 BufferedReader,我相信 br.close() 会同时关闭它们。

我是否必须关闭任何其他打开的连接,或者更好:我可以这样做吗?执行此操作的命令是什么?还是您认为我的问题真的不是由“被遗忘的开放流”引起的?

我的问题主要来源于Why do I get "Too many open files" errors?和IOException: Too many open files的答案。

我正在使用 grails 1.1.1(我知道它已经过时了,但是我在将应用程序迁移到当前版本时遇到了严重的问题,经过数小时的工作我放弃了它),groovy 1.8.0,tomcat 6.0。 28,ubuntu 10.10 上的 apache 2.2.16。

解决我的“打开的文件太多”问题的答案与 Stephen C 的答案非常相关。似乎我的错误的第一个原因确实是未关闭的 BufferedReader Stream。我基本上将他的 java-code 建议直接转移到了 grails:

def URL url = new URL("http://www.some.link");
def URLConnection uc = url.openConnection()
def BufferedReader br = new BufferedReader(new InputStreamReader(uc.getInputStream()))
try
    ...
finally
    br.close()

-> 这绝对解决了“打开的文件太多”的问题,我现在什至可以看到问题的真正根源。

【问题讨论】:

几天前我遇到了与 UrlConnection 类似的问题。我通过阅读流的全部内容来解决它。 嗯...您能否详细说明一下,即给我一个简短的代码示例?不过,我不确定这是否真的能解决我的问题。我使用 BufferedReader/URLConnection 的目的是在将一个非常大的文件(最大 1 GB)传输到我的服务器之前,只检查其头部是否有正确的文件格式。 我暂时无法访问代码,但我可以稍后发布... 【参考方案1】:

如您所见,我唯一明确关闭的是带有 InputStream 的 BufferedReader,我相信 br.close() 会同时关闭它们。

它确实......但只有当它被执行时。

我不是一个 groovy / grails 程序员,但在 Java 中,人们犯的一个常见错误可能导致文件描述符泄漏。例如,

InputStream is = new FileInputStream(someFile);

// do some work
...

is.close();

问题是省略号 (...) 指示的语句可能会引发异常。如果他们这样做,is.close() 调用就不会发生,并且文件描述符会被泄露。解决方案(在 Java 中)是这样编写代码:

InputStream is = new FileInputStream(someFile);
try 
   // do some work
   ...
 finally 
   is.close();

或(在 Java 7 中)为:

try (InputStream is = new FileInputStream(someFile)) 
   // do some work
   ...

【讨论】:

是的,我在 java-questions 的答案中阅读了类似的解决方案。这就是为什么我要问这应该如何在 Groovy 中完成。也许一些 groovy 程序员可以回答这个问题? 我基本上将您的代码复制到了 Groovy(try 和 finally 语句)中,并且它起作用了。非常感谢!【参考方案2】:

似乎在您的应用程序中创建了太多文件或套接字。你需要增加文件限制。

如果您使用的是 linux,请使用ulimit 设置系统的最大文件限制。 在 linux 中,默认文件限制为 1024

ulimit -n 2048 // to set the file limit to 2048

【讨论】:

我在 /etc/security/limits.conf 中全局增加了我系统的文件限制,我为软 nofile 和硬 nofile 做了一个条目,我的 tomcat 用户现在有权打开一个可笑的大量文件,我已经测试过我的更改使用 ulimit -n 生效,并且确实有效。但这并不能解决我的问题。另外,当我在引发错误的时间点检查打开文件的数量时,例如使用 lsof,打开文件的数量相当少。所以我认为这不是我的问题的解决方案? @funnypixy - 你是对的。这是一个创可贴的解决方案,而不是真正的解决方案。【参考方案3】:

@Sunil Kumar Sahoo 和@Stephen C 的答案都适用。

通常您会按此顺序执行以下步骤:

确保您有 try/catch/finally 块以关闭 IO 资源 如果处理 UNIX/Linux 机器,请检查您的 ulimit 设置。这实际上取决于应用程序的需求。

【讨论】:

以上是关于grails 应用程序中的“打开的文件太多” - 如何正确关闭打开的文件和流?的主要内容,如果未能解决你的问题,请参考以下文章

火花中止火花作业中打开的文件太多

删除许多文件后出现“打开的文件太多”错误

无法打开链接描述文件 libgcc_s.so.1:打开的文件太多

Wildfly - 打开的文件太多

打开的文件太多 c

C++ 管道,打开的文件太多,Errno 25