节点和错误:EMFILE,打开的文件太多

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了节点和错误:EMFILE,打开的文件太多相关的知识,希望对你有一定的参考价值。

有些日子我一直在搜索错误的工作解决方案

Error: EMFILE, too many open files

似乎很多人都有同样的问题。通常的答案是增加文件描述符的数量。所以,我试过这个:

sysctl -w kern.maxfiles=20480

默认值是10240.这在我看来有点奇怪,因为我在目录中处理的文件数量低于10240.更奇怪的是,在我增加了文件描述符的数量之后,我仍然收到相同的错误。

第二个问题:

经过多次搜索后,我找到了解决“太多打开文件”问题的方法:

var requestBatches = {};
function batchingReadFile(filename, callback) {
  // First check to see if there is already a batch
  if (requestBatches.hasOwnProperty(filename)) {
    requestBatches[filename].push(callback);
    return;
  }

  // Otherwise start a new one and make a real request
  var batch = requestBatches[filename] = [callback];
  FS.readFile(filename, onRealRead);

  // Flush out the batch on complete
  function onRealRead() {
    delete requestBatches[filename];
    for (var i = 0, l = batch.length; i < l; i++) {
      batch[i].apply(null, arguments);
    }
  }
}

function printFile(file){
    console.log(file);
}

dir = "/Users/xaver/Downloads/xaver/xxx/xxx/"

var files = fs.readdirSync(dir);

for (i in files){
    filename = dir + files[i];
    console.log(filename);
    batchingReadFile(filename, printFile);

不幸的是我仍然收到同样的错误。这段代码有什么问题?

最后一个问题(我是javascript和节点的新手),我正在开发一个Web应用程序,其中包含大约5000个日常用户的大量请求。我在使用python和java等其他语言编程方面有多年的经验。所以最初我想用django或play框架来开发这个应用程序。然后我发现了节点,我必须说非阻塞I / O模型的想法非常好,诱人,而且最重要的是非常快!

但是我应该对节点有什么样的问题?它是经过生产验证的Web服务器吗?你有什么经历?

答案

因为当graceful-fs不起作用时......或者你只想了解泄漏的来源。按照这个过程。

(例如,如果您的问题是插座,优雅的fs不会修理您的旅行车。)

来自我的博客文章:http://www.blakerobertson.com/devlog/2014/1/11/how-to-determine-whats-causing-error-connect-emfile-nodejs.html

如何隔离

此命令将输出nodejs进程的打开句柄数:

lsof -i -n -P | grep nodejs

COMMAND     PID    USER   FD   TYPE    DEVICE SIZE/OFF NODE NAME
...
nodejs    12211    root 1012u  IPv4 151317015      0t0  TCP 10.101.42.209:40371->54.236.3.170:80 (ESTABLISHED)
nodejs    12211    root 1013u  IPv4 151279902      0t0  TCP 10.101.42.209:43656->54.236.3.172:80 (ESTABLISHED)
nodejs    12211    root 1014u  IPv4 151317016      0t0  TCP 10.101.42.209:34450->54.236.3.168:80 (ESTABLISHED)
nodejs    12211    root 1015u  IPv4 151289728      0t0  TCP 10.101.42.209:52691->54.236.3.173:80 (ESTABLISHED)
nodejs    12211    root 1016u  IPv4 151305607      0t0  TCP 10.101.42.209:47707->54.236.3.172:80 (ESTABLISHED)
nodejs    12211    root 1017u  IPv4 151289730      0t0  TCP 10.101.42.209:45423->54.236.3.171:80 (ESTABLISHED)
nodejs    12211    root 1018u  IPv4 151289731      0t0  TCP 10.101.42.209:36090->54.236.3.170:80 (ESTABLISHED)
nodejs    12211    root 1019u  IPv4 151314874      0t0  TCP 10.101.42.209:49176->54.236.3.172:80 (ESTABLISHED)
nodejs    12211    root 1020u  IPv4 151289768      0t0  TCP 10.101.42.209:45427->54.236.3.171:80 (ESTABLISHED)
nodejs    12211    root 1021u  IPv4 151289769      0t0  TCP 10.101.42.209:36094->54.236.3.170:80 (ESTABLISHED)
nodejs    12211    root 1022u  IPv4 151279903      0t0  TCP 10.101.42.209:43836->54.236.3.171:80 (ESTABLISHED)
nodejs    12211    root 1023u  IPv4 151281403      0t0  TCP 10.101.42.209:43930->54.236.3.172:80 (ESTABLISHED)
....

注意:1023u(最后一行) - 这是第1024个文件句柄,这是默认的最大值。

现在,看看最后一栏。这表明哪个资源是开放的。您可能会看到许多行都具有相同的资源名称。希望现在告诉您在代码中查找泄漏的位置。

如果你不知道多个节点进程,首先查找哪个进程有pid 12211.那将告诉你进程。

在上面的例子中,我注意到有一堆非常相似的IP地址。他们都是54.236.3.###通过做IP地址查找,能够确定在我的情况下它是pubnub相关。

命令参考

使用此语法确定进程已打开的打开句柄数...

To get a count of open files for a certain pid

我使用此命令来测试在我的应用程序中执行各种事件后打开的文件数。

lsof -i -n -P | grep "8465" | wc -l

# lsof -i -n -P | grep "nodejs.*8465" | wc -l
28
# lsof -i -n -P | grep "nodejs.*8465" | wc -l
31
# lsof -i -n -P | grep "nodejs.*8465" | wc -l
34

What is your process limit?

ulimit -a

你想要的那条线看起来像这样: open files (-n) 1024

Permanently change the limit:

  • 在Ubuntu 14.04上测试,nodejs v.7.9

如果您希望打开许多连接(websockets是一个很好的例子),您可以永久增加限制:

  • file:/etc/pam.d/common-session(添加到最后) session required pam_limits.so
  • file:/etc/security/limits.conf(添加到最后,或编辑,如果已经存在) root soft nofile 40000 root hard nofile 100000
  • 从ssh重新启动nodejs并注销/登录。
  • 这可能不适用于旧版NodeJS,您需要重启服务器
  • 如果您的节点使用不同的uid运行,请使用。
另一答案

cwait是一种通用的解决方案,用于限制任何返回promise的函数的并发执行。

在您的情况下,代码可能是这样的:

var Promise = require('bluebird');
var cwait = require('cwait');

// Allow max. 10 concurrent file reads.
var queue = new cwait.TaskQueue(Promise, 10);
var read = queue.wrap(Promise.promisify(batchingReadFile));

Promise.map(files, function(filename) {
    console.log(filename);
    return(read(filename));
})
另一答案

使用Isaac Schlueter(node.js维护者)的graceful-fs模块可能是最合适的解决方案。如果遇到EMFILE,它会执行增量后退。它可以用作内置fs模块的直接替代品。

另一答案

我今天遇到了这个问题,找不到好的解决方案,我创建了一个模块来解决它。我的灵感来自@ fbartho的片段,但我想避免覆盖fs模块。

我写的模块是Filequeue,你就像fs一样使用它:

var Filequeue = require('filequeue');
var fq = new Filequeue(200); // max number of files to open at once

fq.readdir('/Users/xaver/Downloads/xaver/xxx/xxx/', function(err, files) {
    if(err) {
        throw err;
    }
    files.forEach(function(file) {
        fq.readFile('/Users/xaver/Downloads/xaver/xxx/xxx/' + file, function(err, data) {
            // do something here
        }
    });
});
另一答案

你正在读太多文件。节点异步读取文件,它将立即读取所有文件。所以你可能正在阅读10240的限制。

看看这是否有效:

var fs = require('fs')
var events = require('events')
var util = require('util')
var path = require('path')

var FsPool = module.exports = function(dir) {
    events.EventEmitter.call(this)
    this.dir = dir;
    this.files = [];
    this.active = [];
    this.threads = 1;
    this.on('run', this.runQuta.bind(this))
};
// So will act like an event emitter
util.inherits(FsPool, events.EventEmitter);

FsPool.prototype.runQuta = function() {
    if(this.files.length === 0 && this.active.length === 0) {
        return this.emit('done');
    }
    if(this.active.length < this.threads) {
        var name = this.files.shift()

        this.active.push(name)
        var fileName = path.join(this.dir, name);
        var self = this;
        fs.stat(fileName, function(err, stats) {
            if(err)
                throw err;
            if(stats.isFile()) {
                fs.readFile(fileName, function(err, data) {
                    if(err)
                        throw err;
                    self.active.splice(self.active.indexOf(name), 1)
                    self.emit('file', name, data);
                    self.emit('run');

                });
            } else {
                self.active.splice(self.active.indexOf(name), 1)
                self.emit('dir', name);
                self.emit('run');
            }
        });
    }
    return this
};
FsPool.prototype.init = function() {
    var dir = this.dir;
    var self = this;
    fs.readdir(dir, function(err, files) {
        if(err)
            throw err;
        self.files = files
        self.emit('run');
    })
    return this
};
var fsPool = new FsPool(__dirname)

fsPool.on('file', function(fileName, fileData) {
    console.log('file name: ' + fileName)
    console.log('file data: ', fileData.toString('utf8'))

})
fsPool.on('dir', function(dirName) {
    console.log('dir name: ' + dirName)

})
fsPool.on('done', function() {
    console.log('done')
});
fsPool.init()
另一答案

我刚刚写完了一小段代码来解决这个问题,所有其他解决方案看起来都太重了,需要你改变你的程序结构。

这个解决方案只是停止任何fs.readFile或fs.writeFile调用,以便在任何给定时间飞行中只有一个设定的数字。

// Queuing reads and writes, so your nodejs script doesn't overwhelm system limits catastrophically
global.maxFilesInFlight = 100; // Set this value to some number safeish for your system
var origRead = fs.readFile;
var origWrite = fs.writeFile;

var activeCount = 0;
var pending = [];

var wrapCallback = function(cb){
    return function(){
        activeCount--;
        cb.apply(this,Array.prototype.slice.call(arguments));
        if (activeCount < global.maxFilesInFlight && pending.length){
            console.log("Processing Pending read/write");
            pending.shift()();
        }
    };
};
fs.readFile = function(){
    var args = Array.prototype.slice.call(arguments);
    if (activeCount < global.maxFilesInFlight){
        if (args[1] instanceof Function){
            args[1] = wrapCallback(args[1]);
        } else if (args[2] instanceof Function) {
            args[2] = wrapCallback(args[2]);
        }

以上是关于节点和错误:EMFILE,打开的文件太多的主要内容,如果未能解决你的问题,请参考以下文章

android EMFILE(打开的文件太多)

EMFile:打开的文件太多,观看 - 构建 Release 反应原生 iOS 应用程序

nodejs应用程序中未处理的错误EMFILE

React Native + Jest EMFILE:打开文件过多错误

生产环境中Error:EMFILE,too many open files的解决方案

使用 Jest 进行测试失败并出现错误:查看文件以进行更改时出错:EMFILE