使用太多文件描述符/泄漏去ioutil?

时间:2014-06-13 02:57:18

标签: file-io go unmarshalling deferred ulimit

我将浏览一个文件列表并将其中的xml数据解组为一个结构rArray数组。我打算处理大约18000个文件。当我处理大约1300个文件时,程序会感到恐慌,并说太多文件是打开的。如果我将处理的文件数量限制为安全数量1000,则程序不会崩溃。

如下所示,我使用ioutil.ReadFile来读取文件数据。

for _, f := range files {

    func() {
        data, err := ioutil.ReadFile("./" + recordDir + "/" + f.Name())
        if err != nil {
            fmt.Println("error reading %v", err)
            return
        } else {
            if (strings.Contains(filepath.Ext(f.Name()), "xml")) {

                //unmarshal data and put into struct array
                err = xml.Unmarshal([]byte(data), &rArray[a])
                if err != nil {
                    fmt.Println("error decoding %v: %v",f.Name(), err)
                    return
                }
            }
        }
    }()
}

我不确定Go是否使用了太多文件描述符或者没有足够快地关闭文件。

在阅读https://groups.google.com/forum/#!topic/golang-nuts/7yXXjgcOikM并查看http://golang.org/src/pkg/io/ioutil/ioutil.go中的ioutil来源后,ioutil.ReadFile的代码显示它使用defer来关闭文件。 defer在返回调用函数时运行,ReadFile()是调用函数。我理解这个吗? 我也尝试在函数中包装我的代码的ioutil.ReadFile部分,但它没有任何区别。

我的ulimit设置为无限制。

更新 我相信在解压缩功能期间实际发生了太多文件的错误。

func Unzip(src, dest string) error {
    r, err := zip.OpenReader(src)
    if err != nil {
        return err
    }

    for _, f := range r.File {
        rc, err := f.Open()
        if err != nil {
            panic(err)
        }

        path := filepath.Join(dest, f.Name)
        if f.FileInfo().IsDir() {
            os.MkdirAll(path, f.Mode())
        } else {
            f, err := os.OpenFile(
                path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
            if err != nil {
                panic(err)
            }

            _, err = io.Copy(f, rc)
            if err != nil {
                panic(err)
            }
            f.Close()
        }
        rc.Close()
    }
    r.Close()
    return nil
}

我最初从https://gist.github.com/hnaohiro/4572580获得了Unzip函数,但经过进一步检查,在gist作者函数中使用defer似乎是错误的,因为该文件只会是返回Unzip()函数后关闭已经太迟了,因为将会打开18000个文件描述符。 ;)

如上所示,我用明确的Close替换了延迟的Close(),但我仍然得到相同的"太多的打开文件"错误。我修改的解压缩功能有问题吗?

更新#2 哎呀,我在Heroku上运行它,并且整个时间都在推进错误的应用程序。获得的经验:在heroku工具带中验证目标应用程序。

https://gist.github.com/hnaohiro/4572580解压缩代码,因为在处理完所有文件之前它不会关闭文件。

我的解压缩代码显式关闭以上工作,@ peterSO的答案中的延迟版本也是如此。

1 个答案:

答案 0 :(得分:3)

我会将解压缩功能从https://gist.github.com/hnaohiro/4572580修改为以下内容:

package main

import (
    "archive/zip"
    "io"
    "log"
    "os"
    "path/filepath"
)

func unzipFile(f *zip.File, dest string) error {
    rc, err := f.Open()
    if err != nil {
        return err
    }
    defer rc.Close()

    path := filepath.Join(dest, f.Name)
    if f.FileInfo().IsDir() {
        err := os.MkdirAll(path, f.Mode())
        if err != nil {
            return err
        }
    } else {
        f, err := os.OpenFile(
            path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
        if err != nil {
            return err
        }
        defer f.Close()

        _, err = io.Copy(f, rc)
        if err != nil {
            return err
        }
    }
    return nil
}

func Unzip(src, dest string) error {
    r, err := zip.OpenReader(src)
    if err != nil {
        return err
    }
    defer r.Close()

    for _, f := range r.File {
        err := unzipFile(f, dest)
        if err != nil {
            return err
        }
    }

    return nil
}

func main() {
    err := Unzip("./sample.zip", "./out")
    if err != nil {
        log.Fatal(err)
    }
}