düğüm ve Hata: EMFILE, çok fazla açık dosya


166

Birkaç gündür bir hataya çalışan bir çözüm aradım

Error: EMFILE, too many open files

Birçok insanın aynı sorunu var gibi görünüyor. Genel cevap, dosya tanımlayıcılarının sayısının artırılmasını içerir. Yani, bunu denedim:

sysctl -w kern.maxfiles=20480,

Varsayılan değer 10240'tır. Bu, gözlerimde biraz garip, çünkü dizinde işlediğim dosya sayısı 10240'ın altında. Yabancı bile, dosya tanımlayıcılarının sayısını artırdıktan sonra aynı hatayı alıyorum. .

İkinci soru:

Birkaç aramadan sonra "çok fazla açık dosya" sorunu için bir çözüm buldum:

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

Ne yazık ki hala aynı hatayı alıyorum. Bu kod ile ilgili yanlışlık nedir?

Son bir soru (javascript ve düğüm için yeniyim), yaklaşık 5000 günlük kullanıcı için birçok istek içeren bir web uygulaması geliştirme sürecindeyim. Python ve java gibi diğer dillerle programlama konusunda uzun yıllara dayanan deneyime sahibim. bu yüzden başlangıçta bu uygulamayı django veya oyun çerçevesi ile geliştirmeyi düşündüm. Sonra düğümü keşfettim ve engellemeyen G / Ç modeli fikrinin gerçekten güzel, baştan çıkarıcı ve en önemlisi çok hızlı olduğunu söylemeliyim!

Fakat düğüm ile ilgili ne tür problemler beklemeliyim? Üretimi kanıtlanmış bir web sunucusu mu? Deneyimleriniz neler?

Yanıtlar:


83

Ne zaman için zarif-fs çalışmıyor ... ya da sadece kaçak nereden geldiğini anlamak istiyorum. Bu işlemi takip edin.

(örn. zarif-fs, sorun soketlerle ilgili ise vagonunuzu tamir etmeyecektir.)

Blogumdan Makale: http://www.blakerobertson.com/devlog/2014/1/11/how-to-determine-whats-causing-error-connect-emfile-nodejs.html

Nasıl İzole Edilir

Bu komut, nodejs işlemleri için açık tanıtıcı sayısını verir:

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

Dikkat: 1023u (son satır) - bu varsayılan maksimum 1024. dosya tanıtıcısıdır.

Şimdi, son sütuna bak. Hangi kaynağın açık olduğunu gösterir. Muhtemelen hepsi aynı kaynak adına sahip bir dizi satır göreceksiniz. Umarım, bu şimdi kodunuzda sızıntı için nereye bakacağınızı söyler.

Birden fazla düğüm işlemi bilmiyorsanız, önce hangi işlemin p111211 olduğunu arayın. Bu size işlemi anlatır.

Yukarıdaki durumumda, çok benzer IP Adresleri olduğunu fark ettim. Hepsi 54.236.3.### IP adresi aramaları yaparak, benim durumumda pubnub ile ilgili olduğunu belirleyebildi.

Komut Referansı

Bir işlemin kaç tane açık tanıtıcı açık olduğunu belirlemek için bu sözdizimini kullanın ...

Belirli bir pid için bir dizi açık dosya almak için

Uygulamamda çeşitli etkinlikler yaptıktan sonra açılan dosya sayısını test etmek için bu komutu kullandım.

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

Süreç sınırınız nedir?

ulimit -a

İstediğiniz çizgi şöyle görünecektir:

open files                      (-n) 1024

Sınırı kalıcı olarak değiştirin:

  • Ubuntu 14.04, nodejs / 7.9'da test edildi

Çok sayıda bağlantı açmayı bekliyorsanız (web soketleri iyi bir örnektir), sınırı kalıcı olarak artırabilirsiniz:

  • dosya: /etc/pam.d/common-session (sonuna ekle)

    session required pam_limits.so
  • dosya: /etc/security/limits.conf (sonuna ekleyin veya zaten varsa düzenleyin)

    root soft  nofile 40000
    root hard  nofile 100000
    
  • nodejs'lerinizi yeniden başlatın ve ssh'den oturum açın / oturum açın.

  • bu daha eski NodeJS için çalışmayabilir, sunucuyu yeniden başlatmanız gerekir
  • düğümünüz farklı uid ile çalışıyorsa yerine kullanın.

1
Açık dosya sınırını nasıl değiştirebilirsiniz?
Om3ga

13
ulimit -n 2048 2048 dosya açmak için izin
Gaël Barbin

1
Bu en açıklayıcı ve doğru cevaptır. Teşekkür ederim!
Kostanos

Nadir rakamlarım var. lsof -i -n -P | grep "12843" | wc -l== 4085 ama ulimit -a | grep "open files"== (-n) 1024 maksimum sınırdan daha fazla açık dosya nasıl olabilir herhangi bir ipucu?
Kostanos

1
@ Blak3r'un blogu kapalı gibi göründüğü için, geri dönüş makinesindeki makalesine bir bağlantı. web.archive.org/web/20140508165434/http://… Süper yardımcı ve gerçekten harika bir okuma!
James

72

Kullanılması graceful-fsIsaac schlueter (node.js sürdürme) tarafından modülü muhtemelen en uygun çözümdür. EMFILE ile karşılaşılırsa artımlı geri çekilme yapar. Dahili fsmodül için bir yedek yedek olarak kullanılabilir .


2
Beni kurtardı, bu neden düğüm varsayılanı değil? Sorunu çözmek için neden bazı üçüncü taraf eklentileri yüklemem gerekiyor?
Anthony Webb

7
Bence genel olarak konuşursak, Düğüm mümkün olduğunca kullanıcıya göstermeye çalışır. Bu, herkese (sadece Düğüm çekirdek geliştiricileri değil), bu nispeten ham arayüzün kullanımından kaynaklanan sorunları çözme fırsatı verir. Aynı zamanda, çözümleri yayınlamak ve başkaları tarafından yayınlananları npm aracılığıyla indirmek gerçekten kolaydır. Düğümün kendisinden çok fazla akıllı beklemeyin. Bunun yerine, akıllıları npm'de yayınlanan paketlerde bulmayı bekleyin.
Myrne Stol

5
Bu, kendi kodunuzsa iyi, ancak bol miktarda npm modülü bunu kullanmıyor.
UpTheCreek

1
Bu modül tüm sorunlarımı çözdü! Düğümün hala biraz ham gibi göründüğüne katılıyorum, ancak esas olarak, çok az belge ile neyin yanlış gittiğini ve bilinen sorunlara kabul edilen doğru çözümleri anlamak gerçekten zor olduğu için.
sidonaldson

nasıl mı NPM bunu? bunu normal fs yerine kodumda nasıl birleştirebilirim?
Aviram Netanel

11

Bunun kimseye yardım edip etmeyeceğinden emin değilim, bana aynı hatayı atan çok bağımlılık içeren büyük bir proje üzerinde çalışmaya başladım. Meslektaşım watchmandemlemek kullanarak kurmamı önerdi ve bu benim için sorunu çözdü.

brew update
brew install watchman

26 Haziran 2019'da düzenleme: Bekçi için Github bağlantısı


Bu en azından bana yardımcı oldu. Tepki yerel bir projede paketleyici dosyaları yerel olarak açabilir veya (yüklüyse) işletim sistemine daha uygun bir şekilde yapmak için bekçi kullanabilir. Bu yüzden büyük bir yardımcı olabilir - macOS için bile tepki yerel CLI hızlı başlangıçta belgelenmiştir: facebook.github.io/react-native/docs/getting-started.html - Şerefe!
Mike Hardy

7

Bugün bu problemle karşılaştım ve bunun için iyi bir çözüm bulamadım, bunu çözmek için bir modül oluşturdum. @ Fbartho'nun snippet'inden ilham aldım, ancak fs modülünün üzerine yazmaktan kaçınmak istedim.

Yazdığım modül Filequeue'dur ve tıpkı fs gibi kullanırsınız:

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

Çok fazla dosya okuyorsunuz. Düğüm dosyaları eşzamansız olarak okur, tüm dosyaları bir kerede okur. Yani muhtemelen 10240 sınırını okuyorsunuz.

Bunun işe yarayıp yaramadığına bakın:

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

Hepimiz gibi siz de asenkron I / O'nun başka bir kurbanısınız. Eşzamansız çağrılarda, çok fazla dosya etrafında döngü yaparsanız, Node.js okunacak her dosya için bir dosya tanımlayıcı açmaya başlar ve siz kapatana kadar işlem yapılmasını bekler.

Dosya tanımlayıcı, sunucunuzda kaynak okumak için kullanılabilir olana kadar açık kalır. Dosyalarınız küçük ve okuma veya güncelleme hızlı olsa bile, biraz zaman alır, ancak aynı zamanda döngünüz yeni dosya tanımlayıcıyı açmak için durmaz. Çok fazla dosyanız varsa, sınıra yakında ulaşılacak ve güzel bir EMFILE alacaksınız .

Bu etkiden kaçınmak için bir kuyruk oluşturan bir çözüm var.

Async yazan insanlar sayesinde , bunun için çok yararlı bir işlev var. Async.queue adlı bir yöntem var, sınırı olan yeni bir kuyruk oluşturuyor ve ardından kuyruğa dosya adları ekliyorsunuz.

Not: Çok sayıda dosya açmanız gerekiyorsa, o anda hangi dosyaların açık olduğunu depolamak ve bunları sonsuz bir şekilde yeniden açmamak iyi bir fikir olacaktır.

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

Her dosyanın kuyruğa (console.log dosya adı) eklendiğini, ancak yalnızca geçerli kuyruk önceden ayarladığınız sınırın altında olduğunda görebilirsiniz.

async.queue, geri arama ile sıranın kullanılabilirliği hakkında bilgi alır, bu geri arama yalnızca veri dosyası okunduğunda ve yapmanız gereken herhangi bir işlem gerçekleştirildiğinde çağrılır. (bkz. fileRead yöntemi)

Yani dosya tanımlayıcı tarafından bunaltılamazsınız.

> 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

Bu sorunu kendim çözmek için küçük bir kod snippet'i yazmayı bitirdim, diğer tüm çözümler çok ağır görünüyor ve program yapınızı değiştirmenizi gerektiriyor.

Bu çözüm herhangi bir fs.readFile veya fs.writeFile çağrısını durdurur, böylece belirli bir zamanda uçuşta belirli bir sayıdan fazla olmaz.

// 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 bunun için github'da bir repo yapmalı.
Nick

Graceful-fs sizin için çalışmıyorsa, bu çok iyi çalışır.
Ceekay

3

Yukarıda belirtilen her şeyi aynı sorun için yaptım ama hiçbir şey çalıştı. Aşağıda denedim% 100 çalıştı. Basit yapılandırma değişiklikleri.

Seçenek 1 ayar sınırı (Çoğu zaman çalışmaz)

user@ubuntu:~$ ulimit -n 65535

kullanılabilir limiti kontrol et

user@ubuntu:~$ ulimit -n
1024

Seçenek 2 Mevcut limiti 65535 olarak artırmak için

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

ona aşağıdaki satırı ekle

fs.file-max = 65535

yeni yapılandırma ile yenilemek için bunu çalıştır

user@ubuntu:~$ sudo sysctl -p

aşağıdaki dosyayı düzenle

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

ona aşağıdaki satırları ekle

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

aşağıdaki dosyayı düzenle

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

bu satırı ona ekle

session required pam_limits.so

oturumu kapatın ve aşağıdaki komutu deneyin

user@ubuntu:~$ ulimit -n
65535

Seçenek 3 Sadece aşağıdaki satırı ekleyin

DefaultLimitNOFILE=65535

/etc/systemd/system.conf ve /etc/systemd/user.conf


seçenek 2 oldukça uzun ve umarım seçenek 3 çalışması, ama benim ubuntu 18
eugene

1

Gayda ile, sadece değişime ihtiyacın var

FS.readFile(filename, onRealRead);

=>

var bagpipe = new Bagpipe(10);

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

Gayda, paraleli sınırlamanıza yardımcı olur. daha fazla detay: https://github.com/JacksonTian/bagpipe


Hepsi Çince veya diğer Asya dillerinde. İngilizce yazılmış herhangi bir belge var mı?
Fatih Arslan

@FatihArslan İngilizce doc şimdi hazır.
user1837639

1

Nodemon komutunu çalıştırırken aynı sorun vardı, bu yüzden yüce metinde açık dosyaların adını azalttı ve hata kayboldu.


Ben de EMFILEhata alıyordum ve deneme ve hata yoluyla bazı Yüce pencereleri kapatmanın sorunu çözdüğünü fark ettim . Hala nedenini bilmiyorum. ulimit -n 2560.Bash_profile'ımı eklemeyi denedim , ancak bu sorunu çözmedi. Bu bunun yerine Atom'a geçilmesi gerektiğini gösteriyor mu ?
Qodesmith

1

@ Blak3r'ın cevabına dayanarak, diğer teşhislere yardımcı olması için kullandığım biraz stenografi:

Dosya tanımlayıcıları biten bir Node.js komut dosyasında hata ayıklamaya çalışıyorsanız, söz lsofkonusu düğüm işlemi tarafından kullanılan çıktıyı veren bir satır :

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

Bu lsof, geçerli çalışan Node.js işlemi tarafından eşzamanlı olarak filtrelenir ve sonuçları arabelleğe döndürür.

Ardından console.log(openFiles.toString()), arabelleği bir dizeye dönüştürmek ve sonuçları günlüğe kaydetmek için kullanın .


0

cwait , vaatlerde bulunan herhangi bir işlevin eşzamanlı yürütülmesini sınırlamak için genel bir çözümdür.

Sizin durumunuzda kod şöyle olabilir:

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

İçin nodemon kullanıcılar: Sadece kullanmak --ignore sorunu çözmek için bayrağı.

Misal:

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

0

En son sürümü kullanın fs-extra.

Ben Ubuntudosya / soket tanımlayıcı alanı (ile saymak lsof |wc -l) bol (16 ve 18) bu sorunu vardı . Kullanılan fs-extrasürüm 8.1.0. 9.0.0"Hata: EMFILE, çok fazla açık dosya" güncelleştirmesinden sonra kayboldu.

Düğüm işleme dosya sistemleri ile çeşitli işletim sistemlerinde çeşitli sorunlar yaşadım. Dosya sistemleri açıkçası önemsiz değildir.


0

Bu sorunu yaşadım ve çalıştırarak çözdüm npm updateve işe yaradı.

Bazı durumlarda node_modules'ü kaldırmanız gerekebilir rm -rf node_modules/


0

Bekçi taktım, limiti değiştirdim vb. Ve Gulp'te işe yaramadı.

Yine de iterm2'nin yeniden başlatılması yardımcı oldu.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.