【问题标题】:Too Many Open File Error in GolangGolang中打开文件过多错误
【发布时间】:2016-06-14 06:47:16
【问题描述】:

我只是在阅读/proc/diskstats 文件。我的代码是:

func ReadFromFile(filepath string)(string){
    defer func() {
        if err1 := recover(); err1 != nil {
            fmt.Println("!!!!!!!!!!!!!!!!Panic Occured and Recovered in readFromFile(), Error Info: ", err1)
        }
     }()

    strData := ""

    data, err := ioutil.ReadFile(filepath)
    if err != nil{
        fmt.Println("File read error: ", err)
        return ""
    }

    strData = string(data)
    return strData
}

我得到的错误是:

File read error: open /proc/diskstats: too many open files

不仅对于这个文件,我对其他一些文件也遇到了同样的错误。

我也运行了这个命令:

root@golang:~# lsof|wc -l

785

请指导我。

【问题讨论】:

  • 你的 ulimit 是多少?
  • 我重新启动了我的服务然后我没有收到任何错误,但是 lsof|wc -l 输出的输出一直在增加。
  • 看完是不是要关门了?
  • 不,我想是的,怎么做。你能帮忙吗?
  • 你是怎么调用函数的?泄漏似乎来自其他地方。

标签: go


【解决方案1】:

我遇到了同样的问题(可能是不同的环境或设置)并以不同的方式解决了它:

func some_func(file_name []string) {
    for _, file_name := range file_names {
        f, _ := os.Create(file_name)
        // defer f.Close() // bad idea
        _, _ = f.Write([]byte("some text"))
        f.Close() // better idea
    }
}

问题是 defer 将在函数返回时执行,这可能需要一段时间 - 取决于循环大小(坏主意)。所以只要明确表达(更好的主意)

【讨论】:

  • f.Write 需要一个 []byte 作为输入,即[]byte("some text")
【解决方案2】:

基本上在 UNIX 平台中,操作系统会限制进程在任何给定时间可能拥有的打开文件描述符的数量。
由于您已达到当前打开的文件(和/或管道或套接字)的限制,并且您正在尝试打开一个新文件(和/或管道或套接字),因此引发了错误 too many open files
为避免此问题,您必须在使用完后关闭文件才能使用 Close() 函数打开文件

【讨论】:

  • 老实说,我本以为 ioutil.ReadFile 会关闭文件(尽管很可能是在其他地方打开了其他文件描述符导致问题)。
  • 如果我们查看 ReadFile,我们可以看到 Close() 函数仅在打开文件失败时调用 (golang.org/src/io/ioutil/ioutil.go?s=1464:1510#L39)。还有另一个选项可以自动调用 Close() 函数并使用 return 语句(作为 void,因此您必须不返回任何内容)
  • 不,第 54 行有一个“defer f.Close()”,导致文件在函数返回期间关闭。如果 Open 调用失败,则会提前返回(以便不对未定义的值调用 Close() 方法)。
  • 你是对的@Vatine 我没有看到早先的回报,我的错
【解决方案3】:

OP 不提供Minimal, Reproducible Example。有问题的错误是由未发布的代码引起的。演示这一点的一种简单方法是简单地在一个最小示例中运行提供的代码(没有其他活动),并查看它是否没有失败。

ioutil.ReadFile 函数当然会关闭文件。在这种情况下,它被暗示只是因为它试图在资源限制已经达到时打开一个新文件。


Go 中一个常见的gotcha 是无法关闭隐式打开的流。这种情况的一个具体情况是,在使用 http 库的客户端函数时打开了一个流。

示例:(1), (2)

客户端完成后必须关闭响应正文:

resp, err := http.Get("http://example.com/")
if err != nil {
    // handle error
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)

此类请求应始终包括对Close的调用,使用如上所示的表单。

可能还有其他类似的隐式流被打开的情况...


这是一个特别棘手的问题,因为对于琐碎的程序,您永远不会知道其中的区别。直到您通过数百或数千次迭代运行此程序时,您才会知道存在问题。然后,错误可能经常表现为某些不相关的函数调用失败——正如 OP 所展示的那样。

【讨论】:

  • // 正文是请求的正文。 // // 对于客户端请求,nil body 表示请求没有 // body,例如 GET 请求。 HTTP 客户端的传输 // 负责调用 Close 方法。 // // 对于服务器请求,请求正文始终为非零 // 但当没有正文时将立即返回 EOF。 // 服务器将关闭请求正文。 ServeHTTP // 处理程序不需要。正文 io.ReadCloser
  • 如果 defer 用于较大函数的上下文中,最好将该部分包装在单独的函数中(可能是 IIFE)——因为执行被延迟"until the surrounding function returns".
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-08
  • 2020-06-08
相关资源
最近更新 更多