2

私は Node でエラーを処理する適切な方法を研究しており、StackOverflow と NodeJS のサイトでいくつかの良い答えを見つけました。try-catch が機能せず、NodeJS ドキュメント自体: http://nodejs.org/api/domain.html

ただし、try/catch および/またはドメインをいつ、どこで使用するかについて、いくつかの質問が残っています。これは非同期コードと同期コードの関係だと思いますが、ドメインに関する NodeJS のサイトで提供されているサンプル コード内でも、ドメインのエラー ハンドラ内で try/catch を使用しています。誰かがこれを詳細に説明できますか? try/catch はエラー ハンドラー内の非同期エラーをキャッチできませんか?

それを超えて、NodeJS のドキュメントは、例外が発生した場合でもプロセスを終了する必要があることを示唆しています。そのため、Domain ドキュメントのコードは、例外がキャッチされたときにクラスターを使用して新しい子プロセス/ワーカーをフォークすることを提案しています。与えられた主な理由は次のとおりです。

JavaScript で throw がどのように機能するかという性質上、参照を漏らしたり、他の種類の未定義の脆弱な状態を作成したりせずに、安全に「中断したところから再開する」方法はほとんどありません。

誰かがこれを説明できますか?javascriptでスローがどのように機能するかの性質は何ですか? リソースが狂ったようにリークするのはなぜですか? そして、プロセスを再起動したり、ワーカーを強制終了/開始したりすることは常に本当に必要ですか?

たとえば、私はJugglingDB ORM を実装していて、ある時点でローカルの mysql サーバーを起動するのを忘れていました。ECONNREFUSEDプロセスをクラッシュさせるエラーが発生しました。これが実稼働環境 (DB がクラッシュするか一時的に利用できない) で発生する可能性があることを認識し、このエラーをキャッチして適切に処理したいと考えました。接続を再試行し、DB に関する状態変数を維持し、場合によっては一時的に利用できないメッセージで応答して要求を処理します。Try/Catch は単にエラーをキャッチできませんでした。ドメインを使用できることがわかりましたが、推奨される戦略を使用すると、DB がオンラインに戻るまで、ワーカーを強制終了して開始するという無限ループに陥ります。

JugglingDBには、何らかの理由で「接続」イベントしかありませんが、エラー オブジェクトを渡すコールバック関数はありません。クラスをインスタンス化した瞬間に接続を試み、適切な方法でキャッチおよび発行されないエラーをスローします。これにより、他の ORM を検討したくなりましたが、そのような状況を処理する方法に関する私の質問にはまだ答えがありません。ドメインを使用して潜在的な接続エラーをキャッチし、新しいプロセスを開始せずに正常に処理するのは間違っているでしょうか?

JugglingDB githubに投稿した質問/問題次のとおりです。プーリング オプションが有効な場合に発生します):

Error: connect ECONNREFUSED
    at errnoException (net.js:901:11)
    at Object.afterConnect [as oncomplete] (net.js:892:19)
    --------------------
    at Protocol._enqueue (/Users/aaronstorck/Sites/site/node_modules/jugglingdb-mysql/node_modules/mysql/lib/protocol/Protocol.js:110:48)
    at Protocol.handshake (/Users/aaronstorck/Sites/site/node_modules/jugglingdb-mysql/node_modules/mysql/lib/protocol/Protocol.js:42:41)
    at PoolConnection.Connection.connect (/Users/aaronstorck/Sites/site/node_modules/jugglingdb-mysql/node_modules/mysql/lib/Connection.js:101:18)
    at Pool.getConnection (/Users/aaronstorck/Sites/site/node_modules/jugglingdb-mysql/node_modules/mysql/lib/Pool.js:42:23)
    at Pool.query (/Users/aaronstorck/Sites/site/node_modules/jugglingdb-mysql/node_modules/mysql/lib/Pool.js:185:8)
    at initDatabase (/Users/aaronstorck/Sites/site/node_modules/jugglingdb-mysql/lib/mysql.js:62:20)
    at initializeConnection (/Users/aaronstorck/Sites/site/node_modules/jugglingdb-mysql/lib/mysql.js:49:9)
    at Object.initializeSchema [as initialize] (/Users/aaronstorck/Sites/site/node_modules/jugglingdb-mysql/lib/mysql.js:33:5)
    at new Schema (/Users/aaronstorck/Sites/site/node_modules/jugglingdb/lib/schema.js:105:13)
    at Application.loadConnections (/Users/aaronstorck/Sites/site/core/application.js:95:40)

Process finished with exit code 8

私が理解するのを助けることができるこの部分について、事前に感謝します! :)

4

1 に答える 1

2

JugglingDB を見て、自分のラップトップに存在しない mysql サーバーに接続ECONNREFUSEDしようとすると、. jugglingdb-mysql を 0.0.6 にダウングレードすると、あなたの言うことを再現できます。そして、私は jugglingdb-mysql のソースを調べたところ、接続に関するエラーが見つかった場合、それはスローされただけであることがわかりました。それは悪いことであり、良い行動ではありません。ログをスパムするのも悪いことですが、処理できるエラーをスローするのはもっと悪いことです。したがって、0.0.7 または 0.0.8 (最新) にアップグレードすることをお勧めします。そうすれば、このエラーについて心配する必要はありません。node-mysql と同じように、JugglingDB または jugglingdb-mysql のバグ レポートを作成して、エラーを適切に反映させます。node-mysql は、それがどのように行われたかの非常に良い例です。

それでは、node.js でエラーを処理する方法について説明します。node.js ですべてのエラーがスローされるわけではありません。たとえば、これを行うことができます:

require('fs').readFile('non-existent-file-yes-gimmie-a-error', function (error) { })

コールバックはエラーで呼び出されますが、スローされません。あなたがモジュール開発者である場合、このような場合、常にエラーを伝搬してください。エラーでコールバックを呼び出すか、エラー ハンドラーを呼び出すかerror、イベント エミッターでイベントを発行します。古いバージョンの jugglingdb-mysql は、エラーの処理方法の非常に悪い例です。でエラーをキャッチできない場合はtry catch、スローしないでください。node.js コア ライブラリ関数と同様に、キャッチできる場合にのみスローします。実行するrequire('fs').readFile()と、すぐにエラーがスローされますが、これはキャッチ可能です。ただし、エラーが見つかった場合は、関数が返された後 (非同期イベントの処理時)、エラーでコールバックが呼び出されます。

これで、キャッチできないエラーがスローされて、さらに多くのクラッシュを経験したことがあると思います。それらは、おそらくイベント エミッターerrorイベントからのものです。node.js では、errorイベントが発行されたときに、ハンドラーがない場合はスローされます。したがって、イベント エミッターからのエラーをキャッチしたい場合は、errorイベントを追加するだけです。この例はfs.createReadStream、イベント エミッターを返すものです。

require('fs').createReadStream('non-exitent-file-gimmie-a-error')

これは確かにプロセスをクラッシュさせますが、エラーを処理できる場合 (たとえば、これを引き起こした http 要求に 404 を与える)、errorハンドラーを追加すると、エラーはスローされなくなります。

require('fs').createReadStream('non-exitent-file-gimmie-a-error').on('error', handleError)

I/O エラーの他に、型エラーと参照エラーがあります。これらは、取り扱いに注意する必要があるものです。

たとえば、次のようなものを使用できます (これは教育目的のためではありません)。

var routes = {
    '/' : function () {...},
    '/one' : function () {...},
    '/two' : function () {...}
}

require('http').createServer(function (req, res) {
    fs.open('layout.html', 'r', function (err, fd) {
        if (err) return errorHandler(err);
        var buffer = new Buffer(4000); // Lets say we know our file is 4000 bytes exatly

        fs.read(fd, buffer, 0, 4000, function (err, data) {
            if (err) return errorHandler(err);

            try {
                routes[req.url](req, res, data);
                fs.close(fd);
            } catch (e) {
                errorHandler(err);
            }
        });
    });

    function errorHandler(err) {
        res.writeHead(404);
        res.end();
    }
}).listen(1337)

routes[req.url]存在しない場合は、errorHandler を呼び出そうとしているため、エラーがスローされますが、ファイルが開いたままになり、エラー時にファイルを閉じるのを忘れていることがわかります。間違った URL で 10000 件のリクエストが行われると、プロセスの最大オープン ファイル制限を使い果たします。これを修正することはできますが、fs.close(fd)代わりに finally 句に入れます。

これを想像してみましょう。ただし、tryandはありませんcatchが、グローバル ドメインでエラーをキャッチします。場合によっては、プログラムの状態がわからなくなることがあります。そのため、エラーが発生してもアプリを続行することを決定することはできません。このような場合、ファイル記述子がリークするためです。

この問題は、クリーンアップ コードを記述しなければならないあらゆる場所に当てはまります。開発者として、受け取ったさまざまな入力すべてについて常に考えるとは限らず、常に間違いを犯します。そのため、プロセスをクラッシュさせることをお勧めします。process.on('uncaughtException')orでエラーをキャッチできdomain.on('error')ますが、クリーンアップを行った後にプロセスを強制終了する必要があります。

しかし、これには注意が必要です。エラーの原因がわからない場合は、クラッシュしてみてください。どこからのものかわかっている場合 (上記の例のように、開いているファイルは 1 つだけです)、そのリソースをクリーンアップしてプロセスを続行させます。 DOS します。

何かをしてからクラッシュすることにした場合は、必ずタイムアウトを設定し、タイムアウトが発生したときにプロセスをクラッシュさせてください。unrefノード v0.10 のタイムアウトで、プロセスが存続しないようにします。v0.8では、コールバックが呼び出されたときにタイムアウトをクリアするaddTimeoutなどのモジュールを使用できます。

于 2014-05-27T13:22:36.307 に答える