NodeJSでdomainモジュールを使って非同期を捕獲する

4703 ワード

背景
最近、オンラインで重要なサービスが発生しました.授業中の教師の大面積の断線が発生しました.私たちはエラーログを通じてsocket hang upのエラーを確認しました.しかし、エラーによって異常なコードが位置決めできませんでした.アクセスログを調べても、あたご前にエラーが発生したインターフェースを特定できません.コードの中で異常を捕獲することができることを期待しています.このようにサービスがキャンセルされないようにして、異常な文脈を取得して、ミスを見つけたいです.
events.js:183
      throw er; // Unhandled 'error' event
      ^

Error: socket hang up
    at TLSSocket.onHangUp (_tls_wrap.js:1137:19)
    at Object.onceWrapper (events.js:313:30)
    at emitNone (events.js:111:20)
    at TLSSocket.emit (events.js:208:7)
    at endReadableNT (_stream_readable.js:1064:12)
    at _combinedTickCallback (internal/process/next_tick.js:138:11)
    at process._tickCallback (internal/process/next_tick.js:180:9)
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] start: `node ./app/bin/www;`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the [email protected] start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /root/.npm/_logs/2018-09-30T08_22_32_703Z-debug.log
uncaught Exceptionを使って非同期を捕獲します.
まず、私達はprocess上のuncaught Exception事件を思い付きました.プロcessのuncaught Exceptionイベントにコールバックを登録したら、サービスは異常に終了しません.見たところとても美しいです.少なくとも私達のサービスはキャンセルしません.しかし、他の問題を持ってきました.
uncaught Exceptionイベント
uncaught Exceptionは、NodeJSプロセスの一つのイベントです.プロセス中に異常が発生し、異常が何のtry...catchによっても捕獲されないと、このイベントがトリガされます.
同期異常を例にとって、NodeJSの異常に対するデフォルト処理を紹介します.
process.on('uncaughtException', (error) => {
  console.log('call uncaughtException handle');
});

//           
nonexistentFunc();
まず、processのuncaghtExceptionイベントを登録して、存在しない関数を実行しました.このプログラムを実行すると、関数nonexistentFuncが存在しないため、プログラムは異常を投げます.try...catch...処理が行われていないため、異常が発生するまで泡が発生します.NodeJSの異常に対するデフォルト処理は、以下のようなコードである.
function _MyFatalException(err){
    if(!process.emit('uncaughtException',err)){
        console.error(err.stack);
        process.emit('exit',1);
    }
}
ノードJSの異常に対するデフォルト処理の順序は、まずuncaughtExceptionイベントをトリガし、イベントが傍受されていない場合、スタックのエラー情報を印刷し、最後にプロセスの終了イベントを起動する.イベントが傍受されると、uncaughtExceptionの登録コールバックが処理される.
uncaught Exceptionイベント発生の条件
  • プログラムに異常が発生しました.
  • 、異常はtry...catchに捕獲されていない
  • 欠点と問題
  • は異常なコンテキストを取得できませんでした.
  • は友好的な異常な処理を与えることができない.例えば、インターフェースがuncaughtExceptionで発生した場合、レスポンスオブジェクト(コンテキストが失われました)が取得できず、起動者サービスに現在異常が発生していることを通知します.
  • はメモリリークの原因となります.uncaghtExceptionイベントが発生すると、現在の環境のスタックが失われ、Nodeが正常にメモリを回収できなくなり、メモリが漏洩する恐れがあります.
  • uncaughtExceptionを使用して異常を捕捉すると、メモリが漏れます.提案された使用方法は、uncaughtExceptionイベントが発生したときに、errorを記録し、Nodeプロセスを終了してサービスを再開することである.(その後、私たちはプロジェクトの中でこのようにしていませんでした..業務は再起動できません.)
    domainモジュールを使って非同期異常を捕捉する
    注意:domainモジュールは破棄されます.
    domainモジュールは、異常な処理方式を簡略化し、try...catch.が処理できます.捕獲できない異常です.コンテキストが失われず、プログラムが終了することもありません.
    私達の古いプロジェクトはexpressフレームを使っていますので、中間部品の方式を使って、要求中の非同期異常を処理します.コードは以下の通りです
    const domain = require('domain');
    
    app.use((req, res, next) => {
      const req_domain = domain.create();
    
      req_domain.on('error', (err) => {
        console.log(err);  //       
        res.send(500, err.stack);
      });
    
      req_domain.run(next);
    });
    
    ミドルウェアでは、まずdomainオブジェクトを作成し、errorイベントのコールバックを登録し、最後に作成ドメインのコンテキストでnext関数を実行します.
    いつdomannのerror事件を触発しますか?プロセスは異常を投げました.何のtry catchに捕獲されませんでした.この時プロシーズ全体のprocessFatalを触発します.この時domann小包の中で、domann上でerror事件を触発します.反対に、process上でuncaughtExceptionイベントをトリガします.
    domainモジュールについてもっと知りたいなら、この文章を読んでください.
    方案を最適化する
    domainは使いやすいですが、万能ではありません.部分異常を捕まえられない場合もあります.サービスのために切らないようにします.私はuncaughtException事件を使ってポケットをすることができます.
    基礎版
    domain+uncaught Exceptiondomainは、大部分の異常を捕獲し、かつ適切に異常終了を処理し、uncaughtExceptionはサービスの切断を回避する.
    アップグレード版
    domain+uncaught Exception+Custer
    Clausterモジュールを使って、複数の作業スレッドを開くことができます.domainで捕獲できない異常があった場合、uncaughtExceptionで捕獲したとき、メモリ漏れを避けるために、現在のworkプロセスを終了することができます.具体的な実現は以下の通りです.
    const cluster = require('cluster');
    
    process.on('uncaughtException', (err) => {
    
      console.log(err); //        
    
      server.close(); //       
    
      //    master       
      if (cluster.worker) {
          cluster.worker.disconnect();
      }
    
    });
    
    締め括りをつける
    domainモジュールはまもなく廃棄されますが、それでも使用できます.オンライン異常を発見しました.
    参考資料
  • Node.jsは、domanモジュールを使用して非同期エコー中の異常を捕捉する.
  • NodeJS API-process event uncaghttexception
  • Node安定性の研究の心得
  • CNode-Node.js非同期異常処理とdomainモジュール解析
  • NodeJS異常処理uncaght Exception編
  • 2018:Using Node.js domans in production