ノードとエラー:EMFILE、開いているファイルが多すぎます


166

数日間、エラーの解決策を探しました

Error: EMFILE, too many open files

多くの人が同じ問題を抱えているようです。通常の答えは、ファイル記述子の数を増やすことです。だから、私はこれを試しました:

sysctl -w kern.maxfiles=20480

デフォルト値は10240です。これは、ディレクトリで処理しているファイルの数が10240未満であるため、少し奇妙です。ファイル記述子の数を増やした後も、同じエラーが発生します。 。

2番目の質問:

何度か検索したところ、「開いているファイルが多すぎる」問題の回避策が見つかりました。

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とnodeが初めてです)で、毎日約5000人のユーザーに大量のリクエストを送信するWebアプリケーションを開発しています。私は、PythonやJavaなどの他の言語を使用したプログラミングに長年の経験があります。だから私はもともとこのアプリケーションをdjangoまたはplayフレームワークで開発することを考えていました。次に、ノードを発見しました。ノンブロッキングI / Oモデルのアイデアは本当に素晴らしく、魅力的で、何よりも非常に高速です。

しかし、nodeにはどのような問題が予想されますか?それは生産実績のあるWebサーバーですか?あなたの経験は何ですか?

回答:


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アドレスのルックアップを行うことで、私の場合はpubnub関連であると判断できました。

コマンドリファレンス

この構文を使用して、プロセスが開いているオープンハンドルの数を判別します...

特定の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でテスト済み

多くの接続を開くことが予想される場合(WebSocketが良い例です)、制限を永続的に増やすことができます。

  • ファイル:/etc/pam.d/common-session (最後に追加)

    session required pam_limits.so
  • ファイル:/etc/security/limits.conf (最後に追加するか、すでに存在する場合は編集します)

    root soft  nofile 40000
    root hard  nofile 100000
    
  • nodejsを再起動し、sshからログアウト/ログインします。

  • これは古いNodeJSでは機能しない可能性があります。サーバーを再起動する必要があります
  • ノードが異なるuidで実行されている場合は、代わりに使用してください。

1
開いているファイルの制限を変更するにはどうすればよいですか?
Om3ga 2014年

13
ulimitのは、2048のファイルオープンできるように、2048 -n
ガエル・Barbin

1
これが最も説明的で正しい答えです。ありがとうございました!
コスタノス2017年

数が少ないです。lsof -i -n -P | grep "12843" | wc -l== 4085ですulimit -a | grep "open files"== (-n)1024任意の手掛かりで、最大制限より多くのファイルを開くことができましたか?
コスタノス2017年

1
@ blak3rのブログがダウンしているように見えるので、ウェイバックマシンに関する彼の記事へのリンクを次に示します。web.archive.org/web/20140508165434/http://… 非常に役に立ち、本当に素晴らしい読み物です!
James

72

graceful-fsIsaac Schlueter(node.jsメンテナー)によるモジュールの使用は、おそらく最も適切なソリューションです。EMFILEが検出された場合、増分バックオフを実行します。組み込みfsモジュールのドロップイン代替品として使用できます。


2
私を救った、なぜこれがノードのデフォルトではないのですか?問題を解決するためにサードパーティのプラグインをインストールする必要があるのはなぜですか?
Anthony Webb

7
一般的に言えば、Nodeはユーザーにできるだけ多く公開することを試みていると思います。これにより、(Nodeコア開発者だけでなく)すべての人に、この比較的未加工のインターフェースの使用から生じる問題を解決する機会が与えられます。同時に、ソリューションを公開したり、npmを介して他のユーザーが公開したソリューションをダウンロードしたりするのは非常に簡単です。Node自体に多くの賢さを期待しないでください。代わりに、npmで公開されたパッケージでスマートを見つけることが期待されます。
Myrne Stol 2013

5
独自のコードであれば問題ありませんが、npmモジュールの多くはこれを使用しません。
UpTheCreek 2013年

1
このモジュールは私のすべての問題を解決しました!私はノードが少し未加工のように見えることに同意しますが、主に非常に少ないドキュメントと既知の問題に対する適切な解決策で何が問題になっているのかを理解することが本当に難しいためです。
sidonaldson 2013年

どうやってそれをnpmしますか?これを通常のfsの代わりにコードで組み合わせるにはどうすればよいですか?
Aviram Netanel 2014

11

これが誰かに役立つかどうかはわかりませんが、依存関係がたくさんある大きなプロジェクトに取り組み始めたため、同じエラーが発生しました。同僚からwatchman、brewを使用してインストールするよう提案されたため、この問題は解決しました。

brew update
brew install watchman

2019年6月26日の編集: ウォッチマンへのGithubリンク


これは少なくとも私を助けました。反応ネイティブプロジェクトでは、バンドラーはファイルをネイティブに開くか、または(インストールされている場合は)ウォッチマンを使用してオペレーティングシステムにより適した方法でファイルを開くことができます。だから、それは大きな助けになるかもしれません-それはmacOSのreact-native CLIクイックスタートでも文書化されています:facebook.github.io/react-native/docs/getting-started.html-乾杯!
マイクハーディ

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が得られます。

この影響を回避するためにキューを作成する1つの解決策があります。

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が機能することを期待しましたが、私のubuntu 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 2013

1

nodemonコマンドの実行時に同じ問題が発生したため、崇高なテキストで開いているファイルの名前を減らし、エラーが消えました。


私もEMFILEエラーになり、試行錯誤の結果、Sublimeウィンドウをいくつか閉じると問題が解決したことに気付きました。理由はまだわかりません。ulimit -n 2560.bash_profileに追加してみましたが、問題は解決しませんでした。これは、代わりにAtomに変更する必要があることを示していますか?
Qodesmith、2016年

1

@ blak3rの回答に基づいて、他の診断に役立つ場合に使用する簡単な表記を以下に示します。

ファイル記述子が不足しているNode.jsスクリプトをデバッグしようとしている場合lsof、問題のノードプロセスによって使用されたの出力を提供する行は次のとおりです。

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

これはlsof、現在実行中のNode.jsプロセスによってフィルター処理されて同期的に実行され、バッファーを介して結果を返します。

次に、を使用console.log(openFiles.toString())してバッファを文字列に変換し、結果を記録します。


0

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


0

最新のを使用してくださいfs-extra

Ubuntu(16と18)で、ファイル/ソケット記述子スペースがたくさんある(で数えるlsof |wc -l)ことで問題が発生しました。中古fs-extra8.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.