
VUE3.0 をすぐに使い始める方法: 学習に入ります。
サービスのリリース後は、必然的に実行環境 (コンテナー、pm2 など) によってスケジュールされ、サービスのアップグレードにより再起動が発生し、さまざまな例外が発生します。プロセスがクラッシュする場合、通常、実行環境ではサービス プロセスの健全性を監視し、プロセスに異常が発生した場合にプロセスを再起動します。また、ローリング アップグレード戦略もあります。ただし、実行環境のスケジューリング戦略はサービス プロセスをブラック ボックスとして扱い、サービス プロセスの内部実行状態を考慮しません。そのため、サービス プロセスは実行環境のスケジューリング アクションを積極的に感知して実行する必要があります。いくつかの終了クリーンアップ アクション。
そこで今日は、Node.js プロセスの終了を引き起こす可能性のあるさまざまな状況と、これらのプロセス終了イベントをリッスンすることで何ができるかを整理します。
原則:
プロセスが終了する場合、プロセスが積極的に終了する場合と、プロセスの終了を要求するシステム信号を受信する場合の 2 つの状況が考えられます。
システム シグナル通知出口
一般的なシステム シグナルは、Node.js 公式ドキュメントにリストされています。主に次のいくつかに焦点を当てます。
場合、非強制終了シグナルを受信すると、Node.js プロセスは終了シグナルをリッスンしてカスタム終了ロジックを実行できます。たとえば、タスクの実行に時間がかかる cli ツールを作成しました。タスクが完了する前にユーザーが ctrl+c でプロセスを終了したい場合、ユーザーは待機するように求められます。
const readline = require('読み取りライン');
process.on('SIGINT', () => {
// コマンドラインでのインタラクションを単純に実装するために readline を使用します const rl = readline.createInterface({
入力: process.stdin、
出力: process.stdout
});
rl.question('タスクはまだ完了していません。終了してもよろしいですか?', Answer => {
if (答え === 'はい') {
console.log('タスクの実行が中断されました、プロセスを終了します');
プロセス終了(0);
} それ以外 {
console.log('タスクは継続します...');
}
rl.close();
});
});
// 実行に 1 分かかるタスクをシミュレートします const longTimeTask = () => {
console.log('タスク開始...');
setTimeout(() => {
console.log('タスク終了');
}, 1000 * 60);
};
Ctrl + Cを
押すたびに、次のようなプロンプトが表示されます。

プロセスがアクティブに
Node.js を終了します。 プロセスは主に次のような状況で終了します。
あなたの場合、pm2 にはデーモン プロセスの効果があることがわかります。プロセスがエラーで終了すると、pm2 はプロセスを再起動します。 Node.js のクラスター モードの子プロセス (実際には pm2 にも同様のロジックがあります):
const cluster = require('cluster' );
const http = require('http');
const numCPUs = require('os').cpus().length;
const プロセス = require('プロセス');
//メイン処理コード if (cluster.isMaster) {
console.log(`メインプロセスを開始します: ${process.pid}`);
// CPU コアの数に基づいて、(let i = 0; i < numCPUs; i++) の作業プロセスを作成します {
クラスター.フォーク();
}
//ワーカー プロセスの終了イベントをリッスンします。cluster.on('exit', (worker, code, signal) => {
console.log(`ワーカー プロセス ${worker.process.pid} が終了しました、エラー コード: ${code || signal}、再起動中...`);
//子プロセスを再起動しますcluster.fork();
});
}
// ワーカープロセスコード if (cluster.isWorker) {
// キャッチされなかったエラー イベントをリッスンします process.on('uncaughtException', error => {
console.log(`ワーカー プロセス ${process.pid}` でエラーが発生しました、エラー);
process.emit('切断');
プロセス終了(1);
});
//Webサーバーを作成する
// 各ワーカー プロセスはポート 8000 をリッスンします (Node.js が内部で処理し、ポートの競合は引き起こしません)
http.createServer((req, res) => {
res.writeHead(200);
res.end('hello worldn');
}).listen(8000);
console.log(`ワーカープロセスの開始: ${process.pid}`);
アプリケーションの実践
Node.js プロセスが終了するさまざまな状況を分析しました。ここで、Node.js プロセスが終了するときに、
ユーザーが独自の終了ロジックを実行できるようにするツールを作成します。
// フックを終了します。
//実行する必要がある終了タスクを保存します const tables = [];
//終了タスクを追加します const addExitTask = fn => task.push(fn);
const handleExit = (コード、エラー) => {
// ...handleExit の実装を以下に示します。};
//さまざまな終了イベントをリッスンします process.on('exit', code => handleExit(code));
// POSIX 仕様に従って、最終終了コードを取得するには 128 + シグナル番号を使用します。 // シグナル番号については、下の図を参照してください。Linux システムで kill -l を実行すると、すべてのシグナル番号を表示できます。 process.on ('SIGHUP', () => handleExit(128 + 1));
process.on('SIGINT', () => handleExit(128 + 2));
process.on('SIGTERM', () => handleExit(128 + 15));
// Ctrl+break を押してシグナルを終了します process.on('SIGBREAK', () => handleExit(128 + 21));
// 終了コード 1 は、プロセスが終了する原因となった、キャッチされなかったエラーを表します。 process.on('uncaughtException', error => handleExit(1, error));
process.on('unhandledRejection', error => handleExit(1, error)) ;

次に、実際のプロセス終了関数 handleExit を実装する必要があります。これは、ユーザーによって渡されたタスク関数が同期または非同期である可能性があるためです。 process.nextTick を使用すると、ユーザーの同期コードが実行されたことを確認でき、プロセスを簡単に理解できます。 .nextTick は、各イベント ループ ステージでの同期コードの実行が完了した後に実行されます (process.nextTick を理解してください)。非同期タスクの場合は、ユーザーがコールバックを呼び出して、非同期タスクが完了したことを通知する必要があります
。終了中です。 let isExiting = false; を複数回実行しないでください。
const handleExit = (コード、エラー) => {
if (isExiting) が戻る;
isExiting = true;
// let hasDoExit = fasle; への複数回の呼び出しを避けるために、終了アクションが実行されたことをマークします。
const doExit = () => {
if (hasDoExit) が戻る;
hasDoExit = true
process.nextTick(() => process.exit(code))
}
// 非同期タスクの数を記録します let asyncTaskCount = 0;
// 非同期タスクの終了後、ユーザーが呼び出す必要があるコールバック let ayncTaskCallback = () => {
process.nextTick(() => {
asyncTaskCount--
if (asyncTaskCount === 0) doExit()
})
}
//すべての終了タスクを実行します task.forEach(taskFn => {
// taskFn 関数のパラメータの数が 1 より大きい場合、コールバック パラメータが渡されたと見なされ、非同期タスク if (taskFn.length > 1) {
asyncTaskCount++
taskFn(エラー、ayncTaskCallback)
} それ以外 {
タスクFn(エラー)
}
});
// 非同期タスクがある場合 if (asyncTaskCount > 0) {
// 10 秒以上経過したら強制終了 setTimeout(() => {
doExit();
}、10 * 1000)
} それ以外 {
doExit()
}
この時点で、プロセス終了監視ツールが完成しました。完全な実装については、このオープン ソース ライブラリ async-exit-hook https://github.com/darukjs/daruk-exit-hookを
参照してください
。
実行中のコンテナ (pm2 や docker など) によってスケジュールされている再起動時、または例外が発生してプロセスが終了するとき、Web サーバーに接続されたリクエストへの応答を完了するなどの終了アクションを実行することを期待します
。
サービス、データベース接続のクリーンアップ、エラー ログの印刷、アラームのトリガーなどを実行します。終了アクションを完了してプロセスを終了したら、プロセス終了監視ツールを使用して次の実装を実行できます。
const http = require(' http');
//Webサーバーを作成する
const サーバー = http.createServer((req, res) => {
res.writeHead(200);
res.end('hello worldn');
}).listen(8000);
// 上記で開発したツールを使用して、プロセス終了タスクを追加します addExitTask((error, callback) => {
// エラー ログの出力、アラームのトリガー、データベース接続の解放など console.log('プロセスが異常終了しました', error)
// 新しいリクエストの受け付けを停止します。server.close((error) => {
if (エラー) {
console.log('新しいリクエストの受け付けを停止するエラー', エラー)
} それ以外 {
console.log('新しいリクエストの受け付けを停止します')
}
})
// より簡単なアプローチは、既存のリクエストが完了するまで一定時間 (ここでは 5 秒待機します) 待つことです // すべてのリクエストが処理されていることを完全に確認したい場合は、各接続を記録し、完了するまで待つ必要がありますすべての接続が解放されます。終了アクションを実行します。 // オープンソース ライブラリ https://github.com/sebhildebrandt/http-graceful-shutdown を参照できます。
setTimout(コールバック、5 * 1000)
})まとめ
上記の説明を通じて、Node.js プロセスの終了を引き起こすさまざまな状況についてはすでにご存知かと思います。サービスがオンラインになった後、k8s や pm2 などのツールは、プロセスが異常終了したときに継続的にプロセスをプルアップしてサービスの可用性を確保できますが、コード内でプロセスの異常やスケジューリングを積極的に感知する必要もあります。問題を早期に発見できるようになります。