simpul dan Kesalahan: EMFILE, terlalu banyak file yang terbuka

166

Selama beberapa hari saya telah mencari solusi yang berfungsi untuk kesalahan

Error: EMFILE, too many open files

Tampaknya banyak orang memiliki masalah yang sama. Jawaban yang biasa melibatkan peningkatan jumlah deskriptor file. Jadi, saya sudah mencoba ini:

sysctl -w kern.maxfiles=20480,

Nilai defaultnya adalah 10240. Ini agak aneh di mata saya, karena jumlah file yang saya tangani di direktori di bawah 10240. Bahkan lebih aneh, saya masih menerima kesalahan yang sama setelah saya meningkatkan jumlah deskriptor file .

Pertanyaan kedua:

Setelah beberapa pencarian, saya menemukan solusi untuk masalah "terlalu banyak file terbuka":

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

Sayangnya saya masih menerima kesalahan yang sama. Apa yang salah dengan kode ini?

Satu pertanyaan terakhir (saya baru javascript dan node), saya sedang dalam proses mengembangkan aplikasi web dengan banyak permintaan untuk sekitar 5000 pengguna setiap hari. Saya sudah bertahun-tahun pengalaman dalam pemrograman dengan bahasa lain seperti python dan java. jadi awalnya saya pikir untuk mengembangkan aplikasi ini dengan Django atau kerangka bermain. Kemudian saya menemukan simpul dan saya harus mengatakan bahwa ide model I / O non-blocking sangat bagus, menggoda, dan yang paling utama sangat cepat!

Tapi masalah apa yang harus saya harapkan dengan node? Apakah ini server web yang terbukti produksi? Apa pengalaman anda

xaverras
sumber

Jawaban:

83

Karena ketika anggun-fs tidak berfungsi ... atau Anda hanya ingin memahami dari mana kebocoran itu berasal. Ikuti proses ini.

(mis. anggun-fs tidak akan memperbaiki kereta Anda jika masalah Anda adalah dengan soket.)

Dari Blog Saya Artikel: http://www.blakerobertson.com/devlog/2014/1/11/how-to-determine-whats-caus-error-connect-emfile-nodejs.html

Cara Mengisolasi

Perintah ini akan menampilkan jumlah pegangan terbuka untuk proses 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)
....

Perhatikan: 1023u (baris terakhir) - itulah pegangan file ke-1024 yang merupakan maksimum default.

Sekarang, lihat kolom terakhir. Itu menunjukkan sumber daya mana yang terbuka. Anda mungkin akan melihat sejumlah baris semuanya dengan nama sumber daya yang sama. Mudah-mudahan, itu sekarang memberitahu Anda di mana harus mencari kode untuk kebocoran.

Jika Anda tidak tahu beberapa proses simpul, pertama-tama cari proses yang memiliki pid 12211. Itu akan memberi tahu Anda prosesnya.

Dalam kasus saya di atas, saya perhatikan ada banyak alamat IP yang sangat mirip. Mereka semua 54.236.3.### Dengan melakukan pencarian alamat ip, dapat menentukan dalam kasus saya itu terkait pubnub.

Referensi Perintah

Gunakan sintaks ini untuk menentukan berapa banyak buka menangani proses telah terbuka ...

Untuk mendapatkan hitungan file terbuka untuk pid tertentu

Saya menggunakan perintah ini untuk menguji jumlah file yang dibuka setelah melakukan berbagai acara di aplikasi saya.

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

Berapa batas proses Anda?

ulimit -a

Baris yang Anda inginkan akan terlihat seperti ini:

open files                      (-n) 1024

Ubah batas secara permanen:

  • diuji pada Ubuntu 14.04, nodejs v. 7.9

Jika Anda mengharapkan untuk membuka banyak koneksi (soket web adalah contoh yang baik), Anda dapat meningkatkan batas secara permanen:

  • file: /etc/pam.d/common-session (tambahkan sampai akhir)

    session required pam_limits.so
  • file: /etc/security/limits.conf (tambahkan ke bagian akhir, atau edit jika sudah ada)

    root soft  nofile 40000
    root hard  nofile 100000
  • restart nodejs Anda dan logout / login dari ssh.

  • ini mungkin tidak berfungsi untuk NodeJS yang lebih lama Anda harus me-restart server
  • gunakan alih-alih jika simpul Anda berjalan dengan uid yang berbeda.
blak3r
sumber
1
Bagaimana Anda bisa mengubah batas file terbuka?
Om3ga
13
ulimit -n 2048 untuk memungkinkan 2048 file terbuka
Gaël Barbin
1
Ini adalah jawaban yang paling deskriptif dan benar. Terima kasih!
Kostanos
Saya punya nomor langka. lsof -i -n -P | grep "12843" | wc -l== 4085 tetapi ulimit -a | grep "open files"== (-n) 1024 ada petunjuk bagaimana saya bisa memiliki lebih banyak file yang dibuka daripada batas maksimum?
Kostanos
1
Karena blog @ blak3r tampaknya tidak aktif, berikut ini tautan ke artikelnya di mesin wayback. web.archive.org/web/20140508165434/http://… Super bermanfaat dan bacaan yang sangat bagus!
James
72

Menggunakan graceful-fsmodul oleh Isaac Schlueter (pengelola node.js) mungkin merupakan solusi yang paling tepat. Itu akan menambah mundur jika EMFILE ditemukan. Ini dapat digunakan sebagai pengganti drop-in untuk fsmodul bawaan.

Myrne Stol
sumber
2
Menyelamatkan saya, mengapa ini bukan simpul default? Mengapa saya perlu menginstal beberapa plugin pihak ke-3 untuk mengatasi masalah ini?
Anthony Webb
7
Saya pikir, secara umum, Node mencoba mengekspos sebanyak mungkin kepada pengguna. Ini memberi semua orang (bukan hanya pengembang inti Node) kesempatan untuk menyelesaikan masalah yang timbul dari penggunaan antarmuka yang relatif mentah ini. Pada saat yang sama, sangat mudah untuk mempublikasikan solusi, dan mengunduh yang dipublikasikan oleh orang lain melalui npm. Jangan berharap banyak kecerdasan dari Node itu sendiri. Sebaliknya, perkirakan untuk menemukan kecerdasan dalam paket yang diterbitkan pada npm.
Myrne Stol
5
Tidak masalah jika ini adalah kode Anda sendiri, tetapi banyak modul npm tidak menggunakan ini.
UpTheCreek
1
Modul ini menyelesaikan semua masalah saya! Saya setuju bahwa node tampaknya masih sedikit mentah, tetapi terutama karena sangat sulit untuk memahami apa yang salah dengan dokumentasi begitu sedikit dan menerima solusi yang tepat untuk masalah yang diketahui.
sidonaldson
bagaimana Anda menemukannya ? bagaimana cara menggabungkan ini dalam kode saya, bukan fs biasa?
Aviram Netanel
11

Saya tidak yakin apakah ini akan membantu siapa pun, saya mulai mengerjakan proyek besar dengan banyak dependensi yang membuat saya mengalami kesalahan yang sama. Rekan saya menyarankan saya untuk menginstal watchmanmenggunakan minuman dan itu memperbaiki masalah ini untuk saya.

brew update
brew install watchman

Sunting pada 26 Juni 2019: Tautan Github ke penjaga

bh4r4th
sumber
Setidaknya ini membantu saya. Pada proyek asli-reaksi, bundler dapat membuka file secara asli atau (jika diinstal) menggunakan penjaga untuk melakukannya dengan cara yang lebih baik untuk sistem operasi. Jadi ini bisa sangat membantu - ini didokumentasikan dalam quick -art CLI reaksi-asli untuk MacOS bahkan: facebook.github.io/react-native/docs/getting-started.html - cheers!
Mike Hardy
7

Saya mengalami masalah ini hari ini, dan tidak menemukan solusi yang baik untuk itu, saya membuat modul untuk mengatasinya. Saya terinspirasi oleh cuplikan @ fbartho, tetapi ingin menghindari menimpa modul fs.

Modul yang saya tulis adalah Filequeue , dan Anda menggunakannya seperti 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
sumber
7

Anda membaca terlalu banyak file. Node membaca file secara tidak sinkron, itu akan membaca semua file sekaligus. Jadi, Anda mungkin membaca batas 10240.

Lihat apakah ini berfungsi:

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.
sumber
6

Seperti kita semua, Anda adalah korban I / O yang tidak sinkron. Dengan panggilan asinkron, jika Anda memutar banyak file, Node.js akan mulai membuka deskriptor file untuk setiap file untuk dibaca dan kemudian akan menunggu tindakan sampai Anda menutupnya.

Deskriptor file tetap terbuka sampai sumber daya tersedia di server Anda untuk membacanya. Meskipun file Anda kecil dan membaca atau memperbarui cepat, dibutuhkan beberapa saat, tetapi pada saat yang sama loop Anda tidak berhenti untuk membuka deskriptor file baru. Jadi jika Anda memiliki terlalu banyak file, batasnya akan segera tercapai dan Anda mendapatkan EMFILE yang indah .

Ada satu solusi, membuat antrian untuk menghindari efek ini.

Terima kasih kepada orang-orang yang menulis Async , ada fungsi yang sangat berguna untuk itu. Ada metode yang disebut Async.queue , Anda membuat antrian baru dengan batas dan kemudian menambahkan nama file ke antrian.

Catatan: Jika Anda harus membuka banyak file, sebaiknya simpan file mana yang saat ini terbuka dan jangan buka kembali secara tak terbatas.

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

Anda dapat melihat bahwa setiap file ditambahkan ke antrian (nama file console.log), tetapi hanya ketika antrian saat ini di bawah batas yang Anda tetapkan sebelumnya.

async.queue mendapatkan informasi tentang ketersediaan antrian melalui panggilan balik, panggilan balik ini dipanggil hanya ketika file data dibaca dan tindakan apa pun yang harus Anda lakukan tercapai. (lihat metode fileRead)

Jadi Anda tidak bisa kewalahan oleh deskriptor file.

> 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
sumber
3

Saya baru saja selesai menulis sedikit cuplikan kode untuk menyelesaikan masalah ini sendiri, semua solusi lain tampak terlalu berat dan mengharuskan Anda untuk mengubah struktur program Anda.

Solusi ini hanya menghentikan panggilan fs.readFile atau fs.writeFile sehingga tidak ada lebih dari satu nomor yang ditetapkan dalam penerbangan pada waktu tertentu.

// 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
sumber
Anda harus membuat repo untuk ini di github.
Nick
Ini bekerja sangat baik jika anggun-fs tidak bekerja untuk Anda.
Ceekay
3

Saya melakukan semua hal yang disebutkan di atas untuk masalah yang sama tetapi tidak ada yang berhasil. Saya mencoba di bawah ini bekerja 100%. Perubahan konfigurasi sederhana.

Opsi 1 set batas (Ini tidak akan bekerja sebagian besar waktu)

user@ubuntu:~$ ulimit -n 65535

periksa batas yang tersedia

user@ubuntu:~$ ulimit -n
1024

Opsi 2 Untuk meningkatkan batas yang tersedia untuk mengatakan 65535

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

tambahkan baris berikut ke sana

fs.file-max = 65535

jalankan ini untuk menyegarkan dengan konfigurasi baru

user@ubuntu:~$ sudo sysctl -p

edit file berikut

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

tambahkan baris berikut ke sana

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

edit file berikut

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

tambahkan baris ini ke sana

session required pam_limits.so

logout dan login dan coba perintah berikut

user@ubuntu:~$ ulimit -n
65535

Opsi 3 Cukup tambahkan baris di bawah ini

DefaultLimitNOFILE=65535

ke /etc/systemd/system.conf dan /etc/systemd/user.conf

Rohit Parte
sumber
opsi 2 agak panjang, dan berharap opsi 3 berfungsi, tetapi ini bukan untuk Ubuntu saya 18
eugene
1

Dengan bagpipe, Anda hanya perlu kembalian

FS.readFile(filename, onRealRead);

=>

var bagpipe = new Bagpipe(10);

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

Bagpipe membantu Anda membatasi paralelnya. lebih detail: https://github.com/JacksonTian/bagpipe

pengguna1837639
sumber
Semuanya menggunakan bahasa Cina atau bahasa Asia lainnya. Apakah ada dokumentasi yang ditulis dalam bahasa Inggris?
Fatih Arslan
@FatihArslan English doc tersedia sekarang.
user1837639
1

Punya masalah yang sama ketika menjalankan perintah nodemon jadi saya mengurangi nama file yang terbuka di teks luhur dan kesalahan menghilang.

Buhiire Keneth
sumber
Saya juga mendapatkan EMFILEkesalahan dan melalui percobaan dan kesalahan melihat bahwa menutup beberapa jendela Sublime menyelesaikan masalah. Saya masih tidak tahu mengapa. Saya mencoba menambahkan ulimit -n 2560.bash_profile saya, tetapi itu tidak menyelesaikan masalah. Apakah ini mengindikasikan perlunya mengubah ke Atom ?
The Qodesmith
1

Membangun jawaban @ blak3r, inilah sedikit singkatan yang saya gunakan untuk berjaga-jaga jika ini membantu diagnosa lain:

Jika Anda mencoba men-debug skrip Node.js yang kehabisan deskriptor file, inilah baris untuk memberi Anda output yang lsofdigunakan oleh proses simpul yang dimaksud:

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

Ini akan berjalan secara sinkron lsof difilter oleh proses Node.js yang sedang berjalan dan mengembalikan hasilnya melalui buffer.

Kemudian gunakan console.log(openFiles.toString())untuk mengonversi buffer ke string dan mencatat hasilnya.

James
sumber
0

cwait adalah solusi umum untuk membatasi eksekusi bersamaan dari setiap fungsi yang mengembalikan janji.

Dalam kasus Anda, kodenya bisa berupa:

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
sumber
0

Untuk pengguna nodemon : Cukup gunakan bendera --ignore untuk menyelesaikan masalah.

Contoh:

nodemon app.js --ignore node_modules/ --ignore data/
Serdar Değirmenci
sumber
0

Gunakan yang terbaru fs-extra .

Saya punya masalah itu pada Ubuntu(16 dan 18) dengan banyak ruang file / socket-deskriptor (hitung dengan lsof |wc -l). fs-extraVersi yang digunakan 8.1.0. Setelah pembaruan ke 9.0.0"Kesalahan: EMFILE, terlalu banyak file yang terbuka" menghilang.

Saya telah mengalami beragam masalah pada beragam OS 'dengan node yang menangani sistem file. Sistem file jelas tidak sepele.

dr0i
sumber
0

Saya memiliki masalah ini, dan saya menyelesaikannya dengan menjalankan npm update dan berhasil.

Dalam beberapa kasus, Anda mungkin perlu menghapus node_modules rm -rf node_modules/

Adnane Lamghari
sumber
0

Saya menginstal penjaga, mengubah batas dll dan tidak bekerja di Gulp.

Restart iterm2 sebenarnya membantu.

Runnick
sumber