node.jsがクラッシュしないようにするにはどうすればよいですか?try-catchが機能しない


157

私の経験では、phpサーバーはログまたはサーバーエンドに例外をスローしますが、node.jsは単にクラッシュします。すべてのコードが非同期で行われるため、try-catchでコードを囲むこともできません。他のすべての人が本番サーバーで何をしているのか知りたいのですが。

回答:


132

http://nodejs.org/docs/latest/api/process.html#process_event_uncaughtexceptionでNode自身のドキュメントを読むことができるので、他の答えは本当に狂っています

誰かが他の明記された回答を使用している場合は、ノードドキュメントをお読みください。

uncaughtException例外処理のための非常に大雑把なメカニズムであり、将来削除される可能性があることに注意してください

PM2

まず、のインストールPM2を強くお勧めしNode.jsます。PM2は、クラッシュの処理、Nodeアプリのモニタリング、負荷分散に非常に優れています。PM2は、ノードアプリがクラッシュしたり、何らかの理由で停止したり、サーバーが再起動したりしても、すぐにNodeアプリを起動します。したがって、コードを管理した後でアプリがクラッシュした場合でも、PM2はすぐに再起動できます。詳細については、PM2のインストールと実行

アプリ自体のクラッシュを防ぐためのソリューションに戻ります。

それで、最終的に、Node文書自体が示唆することを思いつきました。

を使用せず、代わりにwith をuncaughtException使用domainsしてくださいcluster。を使用する場合はuncaughtException、未処理の例外が発生するたびにアプリケーションを再起動してください。

DOMAINクラスタ

実際に行うことは、エラーをトリガーしたリクエストにエラー応答を送信する一方で、他のユーザーが通常の時間に完了できるようにし、そのワーカーでの新しいリクエストのリッスンを停止することです。

このように、ワーカーがエラーを検出するとマスタープロセスが新しいワーカーをフォークできるため、ドメインの使用はクラスターモジュールと密接に関連しています。以下のコードを参照して、私の意味を理解してください

を使用しDomain、を使用してプログラムを複数のワーカープロセスに分離するClusterことで、より適切に対応し、はるかに安全にエラーを処理できます。

var cluster = require('cluster');
var PORT = +process.env.PORT || 1337;

if(cluster.isMaster) 
{
   cluster.fork();
   cluster.fork();

   cluster.on('disconnect', function(worker) 
   {
       console.error('disconnect!');
       cluster.fork();
   });
} 
else 
{
    var domain = require('domain');
    var server = require('http').createServer(function(req, res) 
    {
        var d = domain.create();
        d.on('error', function(er) 
        {
            //something unexpected occurred
            console.error('error', er.stack);
            try 
            {
               //make sure we close down within 30 seconds
               var killtimer = setTimeout(function() 
               {
                   process.exit(1);
               }, 30000);
               // But don't keep the process open just for that!
               killtimer.unref();
               //stop taking new requests.
               server.close();
               //Let the master know we're dead.  This will trigger a
               //'disconnect' in the cluster master, and then it will fork
               //a new worker.
               cluster.worker.disconnect();

               //send an error to the request that triggered the problem
               res.statusCode = 500;
               res.setHeader('content-type', 'text/plain');
               res.end('Oops, there was a problem!\n');
           } 
           catch (er2) 
           {
              //oh well, not much we can do at this point.
              console.error('Error sending 500!', er2.stack);
           }
       });
    //Because req and res were created before this domain existed,
    //we need to explicitly add them.
    d.add(req);
    d.add(res);
    //Now run the handler function in the domain.
    d.run(function() 
    {
        //You'd put your fancy application logic here.
        handleRequest(req, res);
    });
  });
  server.listen(PORT);
} 

けれどもはDomain廃止を保留しているとノードのドキュメントに記載されているように新しい交換が来るように削除されます

このモジュールは廃止予定です。代替APIが確定すると、このモジュールは完全に非推奨になります。ドメインが提供する機能を絶対に必要とするユーザーは、当面はそれに依存する可能性がありますが、将来的には別のソリューションに移行する必要があると予想する必要があります。

しかし、新しい置換が導入されない限り、クラスターを備えたドメインは、Node Documentationが示唆する唯一の優れたソリューションです。

深く理解しDomainCluster読むために

https://nodejs.org/api/domain.html#domain_domainStability: 0 - Deprecated

https://nodejs.org/api/cluster.html

クラスターとドメインに関するこのすばらしい詳細な説明を共有してくれた@Stanley Luoに感謝します。

クラスターとドメイン


9
警告の言葉、ドメインは非推奨の保留中です:リンク。Node docsからの推奨される方法は、cluster:linkを使用することです。
ポール

4
restart your application after every unhandled exception!2000ユーザーがビデオのストリーミングにノードWebサーバーを使用していて、1人のユーザーが例外を受け取った場合、再起動しても他のすべてのユーザーが中断されませんか?
Vikas Bansal、2016年

2
@VikasBansalはい、すべてのユーザーを確実に中断します。そのため、1人のユーザーが例外に直面し、自分のスレッドのみがクラスターから削除され、新しいスレッドが作成された場合に、代わりにuncaughtException使用Domainして使用するのはよくありClusterません。また、Nodeサーバーを再起動する必要もありません。一方、使用するuncaughtException場合は、ユーザーが問題に直面するたびにサーバーを再起動する必要があります。したがって、クラスターでドメインを使用します。
エアリー2016年

3
domainが完全に非推奨になり、削除された場合はどうすればよいですか?
Jas

3
概念を理解していない人のために、このチュートリアル発見clusterworkerssitepoint.com/...
スタンレー羅

81

このコードをrequireステートメントとグローバル宣言のすぐ下に配置します。

process.on('uncaughtException', function (err) {
  console.error(err);
  console.log("Node NOT Exiting...");
});

私のために働く。唯一気に入らないのは、物事をクラッシュさせただけの場合ほど情報が得られないことです。


45
注意:この方法は適切に機能しますが、すべてのHTTP応答を適切に終了する必要があることに注意してください。つまり、HTTPリクエストの処理中にキャッチされない例外が発生した場合でも、http.ServerResponseオブジェクトでend()を呼び出す必要があります。ただし、これを実装するかどうかはあなた次第です。これを行わないと、ブラウザが中止するまでリクエストがハングします。これらのリクエストが十分にある場合は、サーバーのメモリが不足する可能性があります。
BMiner

3
@BMiner、より良い実装を提供できますか?私はこの問題(要求のハング)に気付いたので、を使用してサーバーを再起動するだけの場合よりも実際には良くありませんforever
pixelfreak

6
これには、詳細な説明が必要です。私はこれが悪いことを知っていますが、キャッチされない例外が発生するたびに、サーバーはできるだけ早く再起動する必要があります。実際、「uncaughtException」イベントの目的は、それを警告メールを送信する機会として使用し、次にprocess.exit(1);を使用することです。サーバーをシャットダウンします。サーバーを再起動するために、永遠またはそのようなものを使用できます。保留中のHTTP要求はすべてタイムアウトして失敗します。あなたのユーザーはあなたに腹を立てます。しかし、それが最善の解決策です。なぜ聞くの?チェックアウトstackoverflow.com/questions/8114977/...
BMiner

3
キャッチされていないエラーから詳細情報を取得するには、console.trace(err.stack);を使用します。
ジェシーダンラップ2013年

2
WARNING:ノードのドキュメントは、それは危険なクレイジーだとして、あなたがこれを行うべきではありませんので、ノー不確かな用語で、こう述べています。 nodejs.org/api/process.html#process_event_uncaughtexception
ジェレミー・ローガン

28

前述のように、ここであなたは見つけることができますerror.stackこのようなエラーが発生した行番号として、より完全なエラーメッセージを提供します。

process.on('uncaughtException', function (error) {
   console.log(error.stack);
});

12

試す supervisor

npm install supervisor
supervisor app.js

または、forever代わりにインストールできます。

これは、サーバーを再起動してクラッシュしたときにサーバーを回復するだけです。

forever コード内で使用して、クラッシュしたプロセスを適切に回復できます。

foreverドキュメントは、プログラムの取り扱い終了/エラーの固体情報を持っています。


9
確かにこれは解決策にはなりません...サーバーがダウンしている間、サーバーは新しい着信要求に応答できません。アプリケーションコードから例外がスローされる可能性があります。サーバーは、クラッシュするだけでなく、500エラーで応答する必要があり、サーバーが再起動することを期待します。
Ant Kutschera、

20
そのため、ハッカーとして、単純なリクエストをサーバーに送信し、リクエストパラメータを逃す必要があることがわかります。これにより、javascriptでundefが発生し、node.jsがクラッシュします。あなたの提案で、私はあなたのクラスター全体を繰り返し殺すことができます。答えは、アプリケーションを適切に失敗させることです。つまり、キャッチされない例外を処理し、クラッシュしないようにします。サーバーが多数のVoIPセッションを処理している場合はどうなりますか?クラッシュして焼き付けられたり、既存のすべてのセッションがそれに伴って死ぬことは許されません。ユーザーはすぐに去ります。
Ant Kutschera、

5
@AntKutscheraは、例外が例外的なケースであるべき理由です。例外は、回復できない場合やプロセスクラッシュする必要ある場合にのみ発生します。これらの例外的なケースを処理するには、他の手段を使用する必要があります。しかし、私はあなたの要点を理解しています。可能な限り、優雅に失敗する必要があります。ただし、破損した状態を継続すると、さらに多くの損傷が発生する場合があります。
Raynos、

2
はい、ここにはさまざまな考え方があります。私がそれを学んだ方法(JavascriptではなくJava)には、ビジネス例外として知られている、予期すべき許容可能な期待があり、メモリ不足など、回復が予期されないランタイム例外またはエラーがあります。正常に失敗しない場合の問題の1つは、ユーザーが入力を修正できる場所など、回復可能な何かが発生した場合に例外をスローすることを私が書いた一部のライブラリが宣言する可能性があることです。あなたのアプリでは、あなたは私のドキュメントを読まずにクラッシュしました、そこでユーザーは回復できたかもしれません
Ant Kutschera

1
@AntKutscheraこれが例外を記録する理由です。一般的な例外について運用ログを分析し、サーバーをクラッシュさせるのではなく、それらからどのようにして回復できるかを把握する必要があります。私はその方法論をPHP、Ruby on Rails、およびNodeで使用しました。プロセスを終了するかどうかに関係なく、500エラーをスローするたびに、ユーザーに害を与えています。これはJavaScriptまたはノード固有のプラクティスではありません。
エリックエリオット

7

try-catchを使用すると、キャッチされなかったエラーを解決できる場合がありますが、一部の複雑な状況では、非同期関数のキャッチなどの機能を正しく実行できません。Nodeでは、非同期関数呼び出しには、潜在的なアプリのクラッシュ操作が含まれる可能性があることに注意してください。

使用uncaughtExceptionは回避策ですが、非効率的であると認識されており、Nodeの将来のバージョンでは削除される可能性があるため、これを当てにしないでください。

理想的なソリューションは、ドメインを使用することです:http : //nodejs.org/api/domain.html

サーバーがクラッシュした場合でもアプリが稼働していることを確認するには、次の手順に従います。

  1. ノードクラスターを使用して、コアごとに複数のプロセスをフォークします。したがって、1つのプロセスが停止すると、別のプロセスが自動的に起動します。チェックアウト:http : //nodejs.org/api/cluster.html

  2. try-catchまたはuncaughtを使用する代わりに、ドメインを使用して非同期操作をキャッチします。try-catchまたはuncaughtが悪い考えだと言っているのではありません!

  3. forever / supervisorを使用してサービスを監視する

  4. ノードアプリを実行するデーモンを追加:http : //upstart.ubuntu.com

お役に立てれば!


4

pm2ノードモジュールを試してみてください。一貫性があり、ドキュメントが充実しています。ロードバランサーが組み込まれたNode.jsアプリのプロダクションプロセスマネージャー。この問題のuncaughtExceptionは避けてください。 https://github.com/Unitech/pm2


`処理されない例外が発生するたびにアプリケーションを再起動してください!` 2000ユーザーがストリーミングビデオにノードWebサーバーを使用していて、1人のユーザーが例外を取得した場合、再起動しても他のすべてのユーザーが中断されませんか?
Vikas Bansal、2016年

PM2を発見したときはとても幸せでした。すばらしいソフトウェア
Mladen Janjetovic 2017年

0

UncaughtExceptionは「非常に大まかなメカニズム」であり(真であるため)、ドメインは現在非推奨になっています。ただし、(論理)ドメインに関するエラーをキャッチするためのメカニズムがまだ必要です。図書館:

https://github.com/vacuumlabs/yacol

これを行うのに役立ちます。少しだけ追加の記述を行うことで、コード全体で優れたドメインセマンティクスを実現できます。


0

Restifyでうまく機能します:

server.on('uncaughtException', function (req, res, route, err) {
  log.info('******* Begin Error *******\n%s\n*******\n%s\n******* End Error *******', route, err.stack);
  if (!res.headersSent) {
    return res.send(500, {ok: false});
  }
  res.write('\n');
  res.end();
});
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.