25

ドメインの Node.js ドキュメントの冒頭を見ると、次のように記載されています。

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

再びコード例では、最初のセクションで次のように述べています。

突然のプロセスの再起動を防いでいますが、狂ったようにリソースをリークしています

なぜそうなのかを理解したいのですが?どのリソースがリークしていますか? 彼らは、ドメインのみを使用してエラーをキャッチし、プロセスを安全にシャットダウンすることを推奨しています。これは、ドメインを操作するときだけでなく、すべての例外で問題になりますか? Javascriptで例外をスローしてキャッチするのは悪い習慣ですか? 私はそれがPythonの一般的なパターンであることを知っています。

編集

例外がスローされた場合、オブジェクトをクリーンアップするために実行する可能性のあるコードは実行されないため、例外をスローすると、ガベージコレクションされていない言語でリソースリークが発生する可能性がある理由を理解できます。

私がJavascriptで想像できる唯一の理由は、例外をスローすると、例外がスローされたスコープ(およびおそらくコールスタック内のもの)に変数への参照が保存され、参照が保持され、例外オブジェクトが保持され、決して保持されない場合です。クリーンアップされます。言及されているリークしているリソースがエンジン内部のリソースでない限り。

アップデート

これに対する答えを説明するブログを書きました。見てみな

4

3 に答える 3

14

予期しない例外は、心配する必要があるものです。特定の例外の処理を追加し、必要な状態のクリーンアップを管理するためにアプリの状態について十分に把握していない場合、定義上、アプリの状態は定義されておらず、認識できません。ぶらぶらしてはいけません。心配しなければならないのはメモリリークだけではありません。アプリケーションの状態が不明であると、予測不能で望ましくないアプリケーションの動作が発生する可能性があります (たとえば、間違った出力を配信するなど)。部分的にレンダリングされたテンプレート、不完全な計算結果、さらに悪いことに、後続のすべての出力が間違っている状態などです。そのため、未処理の例外が発生したときにプロセスを終了することが重要です。これにより、アプリはそれ自体を修復する機会が与えられます。

例外が発生しますが、それで問題ありません。抱きしめて。プロセスをシャットダウンし、Foreverなどを使用してそれを検出し、元の状態に戻します。クラスターとドメインも素晴らしいです。あなたが読んでいたテキストは、例外をスローすることや、予期していた例外を処理したときにプロセスを続行することに対する警告ではありません。予期しない例外が発生したときにプロセスを実行し続けることに対する警告です。

于 2013-04-08T08:02:43.957 に答える
11

彼らが「リソースをリークしている」と言ったとき、彼らは本当に「リソースをリークしているかもしれない」という意味だったと思います。http.createServer が例外を適切に処理する場合、スレッドとソケットはリークされません。ただし、物事を適切に処理しないと、確かにそうなる可能性があります。一般的なケースでは、何かが常に適切にエラーを処理しているかどうかを実際に知ることはできません。

彼らが言ったとき、彼らは間違っている/非常に誤解を招くと思います。throw が Javascript で (他の言語に対して) どのように機能するかについて、それを安全でなくするものがあってはなりません。また、 throw/catch が一般的にどのように機能するかについても、安全ではないということはありません-もちろん、それらを間違って使用しない限り。

彼らが言うべきだったのは、(例外が使用されているかどうかに関係なく) 例外的なケースは適切に処理する必要があるということです。認識すべきいくつかの異なるカテゴリがあります。

状態

  1. 外部状態(データベース書き込み、ファイル出力など)が過渡状態のときに発生する例外
  2. 共有メモリが一時的な状態にあるときに発生する例外
  3. ローカル変数のみが一時的な状態になる可能性がある例外

B.可逆性

  1. 可逆/可逆状態 (データベースのロールバックなど)
  2. 不可逆的な状態 (データが失われた、元に戻す方法が不明、または元に戻すことができない)

C. データの重要性

  1. データは破棄できます
  2. データを使用する必要があります (破損していても)

いじっている状態のタイプに関係なく、元に戻すことができる場合は、それを行う必要があります。問題は不可逆状態です。破損したデータを破棄 (または別の検査のために隔離) できる場合は、元に戻せない状態にするための最善の方法です。これは、例外がスローされたときにローカル変数に対して自動的に行われます。これが、例外が純粋に機能的なコード (つまり、副作用のない関数) でエラーを処理するのに優れている理由です。同様に、許容できる場合は、共有状態または外部状態を削除する必要があります。共有状態の場合、その共有状態がローカル状態になり、スタックのアンロールによって (静的または GC を介して) クリーンアップされるまで例外をスローするか、プログラムを再起動します (何かの使用を提案する人々を読みました永遠の結実のように)。

最後のケースは、データが重要な場合です。では、作成したバグと一緒に暮らす必要があります。誰もがバグに対処する必要がありますが、バグにデータの破損が含まれている場合は最悪です。これには通常、手作業による介入 (失われた/破損したデータの再構築、選択的なプルーニングなど) が必要になります。最後のケースでは、例外処理では完全に処理できません。

一部のデータストレージへの複数の更新のコンテキストで、さまざまなケースで操作中の障害を処理する方法に関連する同様の回答を書きました: https://stackoverflow.com/a/28355495/122422

于 2013-04-10T06:50:15.500 に答える
8

node.js ドキュメントからサンプルを取得します。

var d = require('domain').create();
d.on('error', function(er) {
  // The error won't crash the process, but what it does is worse!
  // Though we've prevented abrupt process restarting, we are leaking
  // resources like crazy if this ever happens.
  // This is no better than process.on('uncaughtException')!
  console.log('error, but oh well', er.message);
});
d.run(function() {
  require('http').createServer(function(req, res) {
    handleRequest(req, res);
  }).listen(PORT);
});

この場合handleRequest、ソケットを閉じる前に例外が発生すると、接続がリークしています。

後でクリーンアップせずにリクエストの処理を終了したという意味での「リーク」。最終的には接続がタイムアウトしてソケットが閉じられますが、サーバーの負荷が高い場合は、それが発生する前にソケットが不足する可能性があります。

何をするかによってはhandleRequest、ファイル ハンドル、データベース接続、イベント リスナーなどもリークしている可能性があります。

理想的には、例外を処理して、後でクリーンアップできるようにする必要があります。

于 2013-04-08T10:21:34.207 に答える