节点和错误: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不会修理您的旅行车。)
如何隔离
此命令将输出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,打开的文件太多的主要内容,如果未能解决你的问题,请参考以下文章
EMFile:打开的文件太多,观看 - 构建 Release 反应原生 iOS 应用程序
React Native + Jest EMFILE:打开文件过多错误