węzeł i błąd: EMFILE, zbyt wiele otwartych plików

166

Od kilku dni szukałem działającego rozwiązania błędu

Error: EMFILE, too many open files

Wydaje się, że wiele osób ma ten sam problem. Zwykła odpowiedź polega na zwiększeniu liczby deskryptorów plików. Więc spróbowałem tego:

sysctl -w kern.maxfiles=20480,

Wartość domyślna to 10240. Moim zdaniem jest to trochę dziwne, ponieważ liczba plików, które obsługuję w katalogu, jest poniżej 10240. Co dziwniejsze, nadal otrzymuję ten sam błąd po zwiększeniu liczby deskryptorów plików .

Drugie Pytanie:

Po kilku wyszukiwaniach znalazłem obejście problemu „zbyt wielu otwartych plików”:

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

Niestety nadal otrzymuję ten sam błąd. Co jest nie tak z tym kodem?

Ostatnie pytanie (jestem nowy w javascript i node): jestem w trakcie tworzenia aplikacji internetowej z dużą ilością zapytań dla około 5000 użytkowników dziennie. Mam wieloletnie doświadczenie w programowaniu w innych językach, takich jak Python i Java. więc początkowo myślałem, że stworzę tę aplikację za pomocą django lub frameworka play. Potem odkryłem node'a i muszę powiedzieć, że pomysł nieblokującego modelu I / O jest naprawdę fajny, uwodzicielski i przede wszystkim bardzo szybki!

Ale jakich problemów należy się spodziewać w przypadku węzła? Czy jest to sprawdzony serwer WWW? Jakie masz doświadczenia?

xaverras
źródło

Odpowiedzi:

83

Gdy graceful-fs nie działa ... lub po prostu chcesz zrozumieć, skąd pochodzi wyciek. Postępuj zgodnie z tym procesem.

(np. graceful-fs nie naprawi twojego wagonu, jeśli problem dotyczy gniazd).

Artykuł z mojego bloga: http://www.blakerobertson.com/devlog/2014/1/11/how-to-determine-whats-causing-error-connect-emfile-nodejs.html

Jak izolować

To polecenie wyświetli liczbę otwartych uchwytów dla procesów 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)
....

Zwróć uwagę na: 1023u (ostatnia linia) - to jest 1024-ty uchwyt pliku, który jest domyślnym maksimum.

Teraz spójrz na ostatnią kolumnę. To wskazuje, który zasób jest otwarty. Prawdopodobnie zobaczysz kilka wierszy o tej samej nazwie zasobu. Miejmy nadzieję, że teraz powie Ci, gdzie szukać wycieku w kodzie.

Jeśli nie znasz procesów z wieloma węzłami, najpierw sprawdź, który proces ma pid 12211. To powie ci o procesie.

W moim przypadku powyżej zauważyłem, że istnieje kilka bardzo podobnych adresów IP. Wszyscy oni byli, 54.236.3.### wykonując wyszukiwanie adresów IP, stwierdzili, że w moim przypadku jest to związane z pubnub.

Opis poleceń

Użyj tej składni, aby określić, ile otwartych dojść proces ma otwarte ...

Aby uzyskać liczbę otwartych plików dla określonego pid

Użyłem tego polecenia, aby przetestować liczbę plików, które zostały otwarte po wykonaniu różnych zdarzeń w mojej aplikacji.

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

Jaki jest Twój limit procesu?

ulimit -a

Linia, którą chcesz, będzie wyglądać następująco:

open files                      (-n) 1024

Trwale zmień limit:

  • testowany na Ubuntu 14.04, nodejs v.7.9

W przypadku, gdy spodziewasz się otwierania wielu połączeń (dobrym przykładem jest websockets), możesz trwale zwiększyć limit:

  • plik: /etc/pam.d/common-session (dodaj na końcu)

    session required pam_limits.so
  • plik: /etc/security/limits.conf (dodaj na końcu lub edytuj, jeśli już istnieje)

    root soft  nofile 40000
    root hard  nofile 100000
  • zrestartuj nodejs i wyloguj się / zaloguj z ssh.

  • to może nie działać w przypadku starszych NodeJS, będziesz musiał zrestartować serwer
  • użyj zamiast, jeśli twój węzeł działa z innym identyfikatorem UID.
blak3r
źródło
1
Jak zmienić limit otwartych plików?
Om3ga
13
ulimit -n 2048, aby zezwolić na otwarcie 2048 plików
Gaël Barbin
1
To jest najbardziej opisowa i poprawna odpowiedź. Dziękuję Ci!
Kostanos
Mam rzadkie liczby. lsof -i -n -P | grep "12843" | wc -l== 4085 ale ulimit -a | grep "open files"== (-n) 1024 jakaś wskazówka, jak mogę mieć więcej otwartych plików niż maksymalny limit?
Kostanos
1
Ponieważ wydaje się, że blog @ blak3r nie działa, oto link do jego artykułu o maszynie powrotnej. web.archive.org/web/20140508165434/http://... Super pomocna i naprawdę świetna lektura!
James
72

Użycie graceful-fsmodułu autorstwa Isaaca Schluetera (opiekuna node.js) jest prawdopodobnie najbardziej odpowiednim rozwiązaniem. Wykonuje przyrostowe wycofywanie, jeśli napotkany zostanie EMFILE. Może być używany jako zamiennik wbudowanego fsmodułu.

Myrne Stol
źródło
2
Zapisano mnie, dlaczego nie jest to domyślny węzeł? Dlaczego muszę zainstalować jakąś wtyczkę innej firmy, aby rozwiązać problem?
Anthony Webb
7
Myślę, że ogólnie rzecz biorąc, Node stara się ujawnić użytkownikowi jak najwięcej. Daje to każdemu (nie tylko programistom Node core) możliwość rozwiązania wszelkich problemów wynikających z użycia tego stosunkowo surowego interfejsu. Jednocześnie bardzo łatwo jest publikować rozwiązania i pobierać te opublikowane przez innych za pośrednictwem npm. Nie oczekuj wielu sprytnych rozwiązań od samego Node. Zamiast tego spodziewaj się znaleźć inteligentne rozwiązania w pakietach opublikowanych na npm.
Myrne Stol
5
To dobrze, jeśli to twój własny kod, ale wiele modułów npm go nie używa.
UpTheCreek
1
Ten moduł rozwiązał wszystkie moje problemy! Zgadzam się, że węzeł wydaje się być trochę surowy, ale głównie dlatego, że naprawdę trudno jest zrozumieć, co jest nie tak z tak małą ilością dokumentacji i zaakceptowanymi poprawnymi rozwiązaniami znanych problemów.
sidonaldson
jak można NPM go? jak połączyć to w moim kodzie zamiast zwykłego pliku FS?
Aviram Netanel
11

Nie jestem pewien, czy to komuś pomoże, zacząłem pracę nad dużym projektem z wieloma zależnościami, które wyrzuciły mi ten sam błąd. Mój kolega zasugerował mi instalację watchmanza pomocą brew i to rozwiązało ten problem.

brew update
brew install watchman

Edycja 26 czerwca 2019 r .: łącze Github do watchman

bh4r4th
źródło
To mi przynajmniej pomogło. W projekcie natywnym do reagowania bundler może otwierać pliki natywnie lub (jeśli jest zainstalowany) użyć programu Watchman, aby zrobić to w sposób przyjemniejszy dla systemu operacyjnego. Może to być duża pomoc - jest to udokumentowane w natywnym reagowaniu na CLI szybkiego startu dla macOS, nawet: facebook.github.io/react-native/docs/getting-started.html - pozdrawiam!
Mike Hardy
7

Dzisiaj napotkałem ten problem i nie znajdując dla niego dobrych rozwiązań, stworzyłem moduł, aby go rozwiązać. Zainspirował mnie fragment @ fbartho, ale chciałem uniknąć nadpisywania modułu fs.

Moduł, który napisałem, to Filequeue i używasz go tak jak 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
        }
    });
});
Trey Griffith
źródło
7

Czytasz za dużo plików. Węzeł czyta pliki asynchronicznie, będzie czytać wszystkie pliki naraz. Więc prawdopodobnie czytasz limit 10240.

Sprawdź, czy to działa:

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()
Tim P.
źródło
6

Podobnie jak my wszyscy, jesteś kolejną ofiarą asynchronicznych operacji we / wy. W przypadku wywołań asynchronicznych, jeśli zapętlisz wiele plików, Node.js zacznie otwierać deskryptor pliku dla każdego pliku do odczytania, a następnie będzie czekał na akcję, aż go zamkniesz.

Deskryptor pliku pozostaje otwarty, dopóki na serwerze nie będzie dostępny zasób umożliwiający jego odczytanie. Nawet jeśli twoje pliki są małe, a odczyt lub aktualizacja jest szybka, zajmuje to trochę czasu, ale w tym samym czasie twoja pętla nie zatrzymuje się, aby otworzyć nowy deskryptor plików. Więc jeśli masz zbyt wiele plików, wkrótce limit zostanie osiągnięty i otrzymasz piękny EMFILE .

Jest jedno rozwiązanie, tworząc kolejkę, aby uniknąć tego efektu.

Dzięki ludziom, którzy napisali Async , jest do tego bardzo przydatna funkcja. Istnieje metoda o nazwie Async.queue , tworzysz nową kolejkę z limitem, a następnie dodajesz nazwy plików do kolejki.

Uwaga: jeśli musisz otworzyć wiele plików, dobrym pomysłem byłoby zapisanie, które pliki są aktualnie otwarte i nie otwieraj ich ponownie w nieskończoność.

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

Możesz zobaczyć, że każdy plik jest dodawany do kolejki (nazwa pliku console.log), ale tylko wtedy, gdy bieżąca kolejka jest poniżej limitu, który ustawiłeś wcześniej.

async.queue uzyskuje informacje o dostępności kolejki poprzez wywołanie zwrotne, to wywołanie zwrotne jest wywoływane tylko wtedy, gdy plik danych jest odczytywany i wykonywana jest każda czynność, którą musisz wykonać. (patrz metoda fileRead)

Nie możesz więc być przytłoczony deskryptorem plików.

> 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
Plaute
źródło
3

Właśnie skończyłem pisać mały fragment kodu, aby samodzielnie rozwiązać ten problem, wszystkie inne rozwiązania wydają się zbyt ciężkie i wymagają zmiany struktury programu.

To rozwiązanie po prostu blokuje wszystkie wywołania fs.readFile lub fs.writeFile, tak że w locie w danym momencie nie ma więcej niż ustalona liczba.

// 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);
        });
    }
};
fbartho
źródło
Powinieneś zrobić to repozytorium na githubie.
Nick
Działa to bardzo dobrze, jeśli graceful-fs nie działa dla ciebie.
Ceekay
3

Zrobiłem wszystkie powyższe rzeczy dla tego samego problemu, ale nic nie działało. Próbowałem poniżej to działało w 100%. Proste zmiany konfiguracji.

Opcja 1 ustaw limit (nie będzie działać przez większość czasu)

user@ubuntu:~$ ulimit -n 65535

sprawdź dostępny limit

user@ubuntu:~$ ulimit -n
1024

Opcja 2 Zwiększenie dostępnego limitu do 65535

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

dodaj do niego następujący wiersz

fs.file-max = 65535

uruchom to, aby odświeżyć nową konfigurację

user@ubuntu:~$ sudo sysctl -p

edytuj następujący plik

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

dodaj do niego następujące wiersze

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

edytuj następujący plik

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

dodaj do niego tę linię

session required pam_limits.so

wyloguj się, zaloguj się i wypróbuj następujące polecenie

user@ubuntu:~$ ulimit -n
65535

Opcja 3 Po prostu dodaj poniższą linię

DefaultLimitNOFILE=65535

do /etc/systemd/system.conf i /etc/systemd/user.conf

Rohit Parte
źródło
opcja 2 jest dość długa i mam nadzieję, że opcja 3 zadziała, ale nie jest dla mojego ubuntu 18
eugene
1

Z dudami potrzebujesz tylko zmiany

FS.readFile(filename, onRealRead);

=>

var bagpipe = new Bagpipe(10);

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

Dudy pomagają ograniczyć podobieństwo. więcej szczegółów: https://github.com/JacksonTian/bagpipe

user1837639
źródło
Wszystko w języku chińskim lub innym języku azjatyckim. Czy jest jakaś dokumentacja napisana w języku angielskim?
Fatih Arslan,
Dokument @FatihArslan w języku angielskim jest już dostępny.
user1837639
1

Miałem ten sam problem podczas uruchamiania polecenia nodemon, więc zredukowałem nazwy plików otwieranych wysublimowanym tekstem i błąd zniknął.

Buhiire Keneth
źródło
Ja również otrzymywałem EMFILEbłędy i metodą prób i błędów zauważyłem, że zamknięcie niektórych okien Sublime rozwiązało problem. Nadal nie wiem dlaczego. Próbowałem dodać ulimit -n 2560do mojego .bash_profile, ale to nie rozwiązało problemu. Czy to wskazuje na potrzebę przejścia na Atom zamiast tego?
The Qodesmith
1

Opierając się na odpowiedzi @ blak3r, oto trochę skrótu, którego używam na wypadek, gdyby pomóc innym zdiagnozować:

Jeśli próbujesz debugować skrypt Node.js, w którym brakuje deskryptorów plików, oto wiersz zawierający dane wyjściowe lsofużywane przez dany proces węzła:

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

Będzie to działać synchronicznie, lsofprzefiltrowane przez aktualnie działający proces Node.js i zwróci wyniki przez bufor.

Następnie użyj, console.log(openFiles.toString())aby przekonwertować bufor na ciąg i zarejestrować wyniki.

James
źródło
0

cwait to ogólne rozwiązanie ograniczające jednoczesne wykonywanie wszelkich funkcji, które zwracają obietnice.

W twoim przypadku kod może wyglądać następująco:

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));
})
jjrv
źródło
0

Dla użytkowników nodemon : po prostu użyj flagi --ignore, aby rozwiązać problem.

Przykład:

nodemon app.js --ignore node_modules/ --ignore data/
Serdar Değirmenci
źródło
0

Użyj najnowszego fs-extra.

Miałem ten problem na Ubuntu(16 i 18) z dużą ilością miejsca na deskryptory plików / gniazd (licząc z lsof |wc -l). fs-extraWersja używana 8.1.0. Po aktualizacji 9.0.0komunikatu „Błąd: EMFILE, zbyt wiele otwartych plików” zniknął.

Doświadczyłem różnych problemów w różnych systemach operacyjnych z systemami plików obsługującymi węzły. Systemy plików nie są oczywiście trywialne.

dr0i
źródło
0

Miałem ten problem i rozwiązałem go, uruchamiając npm updatei zadziałało.

W niektórych przypadkach może być konieczne usunięcie modułów node_modules rm -rf node_modules/

Adnane Lamghari
źródło
0

Zainstalowałem stróża, zmieniłem limit itp. I nie zadziałało to w Gulp.

Ponowne uruchomienie iterm2 faktycznie pomogło.

Runnick
źródło