노드 및 오류 : EMFILE, 너무 많은 열린 파일


166

며칠 동안 나는 오류에 대한 해결책을 찾았습니다.

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);

불행히도 여전히 같은 오류가 발생합니다. 이 코드에 어떤 문제가 있습니까?

마지막 질문 (자바 스크립트 및 노드에 익숙하지 않음), 나는 약 5000 명의 일일 사용자에 대한 많은 요청으로 웹 응용 프로그램을 개발하는 과정에 있습니다. 파이썬과 자바와 같은 다른 언어로 프로그래밍하는 데 수년간의 경험이 있습니다. 원래 django 또는 play 프레임 워크로이 애플리케이션을 개발하려고 생각했습니다. 그런 다음 노드를 발견했고 비 차단 I / O 모델이라는 아이디어는 정말 훌륭하고 매혹적이며 무엇보다도 매우 빠릅니다.

그러나 노드에 어떤 종류의 문제가 있습니까? 프로덕션에서 입증 된 웹 서버입니까? 당신의 경험은 무엇입니까?

답변:


83

들어 때 우아한-FS가 작동하지 않습니다 ... 아니면 그냥 누출이 어디에서 오는 이해합니다. 이 과정을 따르십시오.

(예 : 소켓에 문제가있는 경우 graceful-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 주소 조회를 수행하여 내 경우에는 펀 버브와 관련이 있음을 확인할 수있었습니다.

명령 참조

프로세스가 열린 핸들 수를 결정하려면이 구문을 사용하십시오.

특정 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

프로세스 한계는 무엇입니까?

ulimit -a

원하는 줄은 다음과 같습니다.

open files                      (-n) 1024

한계를 영구적으로 변경하십시오.

  • Ubuntu 14.04, nodejs v. 7.9에서 테스트되었습니다.

많은 연결을 열 것으로 예상되는 경우 (웹 소켓이 좋은 예), 제한을 영구적으로 늘릴 수 있습니다.

  • 파일 : /etc/pam.d/common-session (끝에 추가)

    session required pam_limits.so
  • 파일 : /etc/security/limits.conf (끝에 추가하거나 이미 존재하는 경우 편집)

    root soft  nofile 40000
    root hard  nofile 100000
  • ssh에서 nodejs를 다시 시작하고 로그 아웃 / 로그인하십시오.

  • 이전 NodeJS에서는 작동하지 않을 수 있습니다. 서버를 다시 시작해야합니다.
  • 노드가 다른 uid로 실행되는 경우 대신 사용하십시오.

1
열린 파일 제한을 어떻게 변경할 수 있습니까?
Om3ga

13
ulimit를 2048 파일을 공개 할 수 있도록 2048 -n
가엘 Barbin

1
이것은 가장 설명적이고 정답입니다. 감사합니다!
Kostanos 2016 년

나는 드문 숫자가 있습니다. lsof -i -n -P | grep "12843" | wc -l== 4085 but ulimit -a | grep "open files"== (-n) 1024 최대 제한보다 파일을 더 많이 열 수있는 방법에 대한 단서가 있습니까?
Kostanos 2016 년

1
@ blak3r의 블로그가 다운 된 것으로 보이므로 여기에 해당 기계에 대한 기사 링크가 있습니다. web.archive.org/web/20140508165434/http://… 매우 유용하고 정말 읽을 거리!
제임스

72

graceful-fsIsaac Schlueter (node.js 관리자) 의 모듈을 사용하는 것이 가장 적합한 솔루션 일 것입니다. EMFILE이 발생하면 증분 백 오프를 수행합니다. 내장 fs모듈 의 드롭 인 대체품으로 사용할 수 있습니다 .


2
저를 저장했는데 왜 이것이 노드 기본값이 아닙니까? 문제를 해결하기 위해 타사 플러그인을 설치해야하는 이유는 무엇입니까?
Anthony Webb

7
일반적으로 Node는 가능한 한 많은 사용자에게 노출하려고합니다. 이를 통해 (노드 코어 개발자뿐만 아니라) 모든 사람이이 상대적으로 원시적 인 인터페이스를 사용하여 발생하는 모든 문제를 해결할 수 있습니다. 동시에 솔루션을 게시하고 npm을 통해 다른 사람이 게시 한 솔루션을 다운로드하는 것이 매우 쉽습니다. 노드 자체에서 많은 영리함을 기대하지 마십시오. 대신 npm에 게시 된 패키지에서 스마트를 찾으십시오.
Myrne Stol

5
자신의 코드라면 괜찮지 만 많은 npm 모듈은 이것을 사용하지 않습니다.
UpTheCreek

1
이 모듈은 모든 문제를 해결했습니다! 나는 노드가 여전히 약간 원시적 인 것처럼 보이지만 주로 문서가 너무 적어서 무엇이 잘못되었는지 이해하기가 어렵고 알려진 문제에 대한 올바른 해결책을 받아 들였기 때문에 동의합니다.
sidonaldson

어떻게 npm 합니까? 일반 fs 대신 내 코드에서 이것을 어떻게 결합합니까?
Aviram Netanel

11

이것이 누군가에게 도움이 될지 확신 할 수 없으며, 많은 의존성을 가진 큰 프로젝트를 시작하여 같은 오류가 발생했습니다. 동료가 watchmanbrew 를 사용 하여 설치하도록 제안 했으며이 문제를 해결했습니다.

brew update
brew install watchman

2019 년 6 월 26 일 편집 : Github와 파수꾼 링크


이것은 적어도 나에게 도움이되었습니다. 반응 네이티브 프로젝트에서 번 들러는 파일을 기본적으로 열거 나 (설치된 경우) 워치 맨을 사용하여 운영 체제에 더 좋은 방식으로 파일을 열 수 있습니다. 따라서 큰 도움이 될 수 있습니다. macOS의 반응 네이티브 CLI 빠른 시작에도 설명되어 있습니다. facebook.github.io/react-native/docs/getting-started.html- 건배!
Mike Hardy

7

나는 오늘이 문제에 부딪 쳤고 그에 대한 좋은 해결책을 찾지 못했고 그것을 해결할 모듈을 만들었습니다. @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
        }
    });
});

7

너무 많은 파일을 읽고 있습니다. 노드는 파일을 비동기 적으로 읽으며 모든 파일을 한 번에 읽습니다. 따라서 아마도 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()

6

우리 모두와 마찬가지로 비동기 I / O의 또 다른 희생자입니다. 비동기 호출을 사용하면 많은 파일을 반복하면 Node.js가 읽을 각 파일에 대해 파일 설명자를 열기 시작한 다음 닫을 때까지 작업을 기다립니다.

서버에서 리소스를 읽을 수있을 때까지 파일 디스크립터는 열려 있습니다. 파일이 작고 읽기 또는 업데이트가 빠르더라도 시간이 걸리지 만 루프가 새 파일 설명자를 여는 것을 멈추지 않습니다. 따라서 파일이 너무 많으면 한도에 도달하고 아름다운 EMFILE 을 얻습니다 .

이 효과를 피하기 위해 큐를 작성하는 솔루션이 하나 있습니다.

Async 를 작성한 사람들 덕분에 매우 유용한 기능이 있습니다. Async.queue 라는 메서드가 있습니다 . 제한이있는 새 대기열을 만든 다음 대기열에 파일 이름을 추가합니다.

참고 : 많은 파일을 열어야하는 경우 현재 열려있는 파일을 저장하고 무한정 다시 열지 않는 것이 좋습니다.

const fs = require('fs')
const async = require("async")

var q = async.queue(function(task, callback) {
    console.log(task.filename);
    fs.readFile(task.filename,"utf-8",function (err, data_read) {
            callback(err,task.filename,data_read);
        }
    );
}, 4);

var files = [1,2,3,4,5,6,7,8,9,10]

for (var file in files) {
    q.push({filename:file+".txt"}, function (err,filename,res) {
        console.log(filename + " read");
    });
}

각 파일이 대기열에 추가 된 것을 확인할 수 있지만 (console.log 파일 이름) 현재 대기열이 이전에 설정 한 한도 미만인 경우에만 가능합니다.

async.queue는 콜백을 통해 큐의 가용성에 대한 정보를 얻습니다.이 콜백은 데이터 파일을 읽고 수행해야 할 조치가있을 때만 호출됩니다. (fileRead 메소드 참조)

따라서 파일 설명자에 압도 될 수 없습니다.

> node ./queue.js
0.txt
    1.txt
2.txt
0.txt read
3.txt
3.txt read
4.txt
2.txt read
5.txt
4.txt read
6.txt
5.txt read
7.txt
    1.txt read (biggest file than other)
8.txt
6.txt read
9.txt
7.txt read
8.txt read
9.txt read

3

방금이 문제를 직접 해결하기 위해 약간의 코드 스 니펫 작성을 마쳤습니다. 다른 모든 솔루션은 너무 무거워 보이므로 프로그램 구조를 변경해야합니다.

이 솔루션은 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]);
        }
        activeCount++;
        origRead.apply(fs,args);
    } else {
        console.log("Delaying read:",args[0]);
        pending.push(function(){
            fs.readFile.apply(fs,args);
        });
    }
};

fs.writeFile = 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]);
        }
        activeCount++;
        origWrite.apply(fs,args);
    } else {
        console.log("Delaying write:",args[0]);
        pending.push(function(){
            fs.writeFile.apply(fs,args);
        });
    }
};

U는 github에서 이에 대한 저장소를 만들어야합니다.
Nick

graceful-fs가 작동하지 않는 경우 매우 효과적입니다.
Ceekay

3

나는 동일한 문제에 대해 위에서 언급 한 모든 것을했지만 아무것도 효과가 없었습니다. 나는 아래에서 100 % 효과를 보았습니다. 간단한 구성 변경.

옵션 1 제한 설정 (대부분 작동하지 않음)

user@ubuntu:~$ ulimit -n 65535

사용 가능한 한도 확인

user@ubuntu:~$ ulimit -n
1024

옵션 2 65535와 같이 사용 가능한 제한을 늘리려면

user@ubuntu:~$ sudo nano /etc/sysctl.conf

그것에 다음 줄을 추가하십시오

fs.file-max = 65535

새로운 설정으로 새로 고치려면 이것을 실행하십시오.

user@ubuntu:~$ sudo sysctl -p

다음 파일을 편집하십시오

user@ubuntu:~$ sudo vim /etc/security/limits.conf

그것에 다음 줄을 추가하십시오

root soft     nproc          65535    
root hard     nproc          65535   
root soft     nofile         65535   
root hard     nofile         65535

다음 파일을 편집하십시오

user@ubuntu:~$ sudo vim /etc/pam.d/common-session

이 줄을 추가하십시오

session required pam_limits.so

로그 아웃하고 로그인 한 후 다음 명령을 시도하십시오

user@ubuntu:~$ ulimit -n
65535

옵션 3 아래 줄을 추가하십시오.

DefaultLimitNOFILE=65535

/etc/systemd/system.conf 및 /etc/systemd/user.conf로


옵션 2는 다소 길고 옵션 3이 작동하기를 희망했지만 내 우분투 18
eugene

1

백파이프를 사용하면 변경 만하면됩니다.

FS.readFile(filename, onRealRead);

=>

var bagpipe = new Bagpipe(10);

bagpipe.push(FS.readFile, filename, onRealRead))

백파이프는 평행을 제한하는 데 도움이됩니다. 자세한 내용은 https://github.com/JacksonTian/bagpipe


모두 중국어 나 다른 아시아 언어에 있습니다. 영어로 작성된 설명서가 있습니까?
Fatih Arslan

@FatihArslan 영어 문서를 지금 사용할 수 있습니다.
user1837639

1

nodemon 명령을 실행할 때 동일한 문제가 발생 하여 숭고한 텍스트로 열리는 파일 이름을 줄이고 오류가 사라졌습니다.


나도 EMFILE오류 가 발생하고 시행 착오를 통해 일부 Sublime 창 을 닫으면 문제가 해결 되었음을 알았습니다 . 나는 아직도 이유를 모른다. ulimit -n 2560.bash_profile을 추가하려고 시도했지만 문제가 해결되지 않았습니다. 대신 Atom 으로 변경해야합니까 ?
Qodesmith

1

@ blak3r의 답변을 바탕으로, 다른 진단에 도움이되는 경우를 위해 사용하는 약간의 속기입니다.

파일 디스크립터가 부족한 Node.js 스크립트를 디버깅하려는 경우 lsof문제의 노드 프로세스에서 사용되는 출력을 제공하는 행이 있습니다 .

openFiles = child_process.execSync(`lsof -p ${process.pid}`);

이것은 lsof현재 실행중인 Node.js 프로세스에 의해 동 기적으로 필터링되어 실행되며 버퍼를 통해 결과를 반환합니다.

그런 다음 console.log(openFiles.toString())버퍼를 문자열로 변환하고 결과를 기록하는 데 사용하십시오.


0

cwait 는 약속을 반환하는 모든 함수의 동시 실행을 제한하기위한 일반적인 솔루션입니다.

귀하의 경우 코드는 다음과 같습니다.

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));
})

0

들어 nodemon 사용자 : 그냥 사용 --ignore 문제를 해결하기 위해 플래그를.

예:

nodemon app.js --ignore node_modules/ --ignore data/

0

최신 사용 fs-extra .

Ubuntu충분한 파일 / 소켓 설명자 공간 (으로 계산)이있는 (16 및 18) 에 그 문제가있었습니다 lsof |wc -l. 사용 된 fs-extra버전 8.1.0입니다. 업데이트 후9.0.0"오류 : EMFILE, 너무 많은 열린 파일"로 사라졌습니다.

노드 처리 파일 시스템으로 다양한 OS에서 다양한 문제를 겪었습니다. 파일 시스템은 명백하지 않습니다.


0

이 문제가 있었고 실행하여 해결했습니다. npm update 하여 했으며 효과가있었습니다.

경우에 따라 node_modules를 제거해야 할 수도 있습니다 rm -rf node_modules/


0

나는 워치 맨을 설치하고, 한계를 변경하는 등 Gulp에서 작동하지 않았습니다.

iterm2를 다시 시작하면 실제로 도움이되었습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.