Node.js Cluster モジュール
Node.js のclusterモジュールを使用すると、同じサーバーポートを共有する子プロセス(worker processes)を簡単に作成できます。Node.js はシングルスレッドであるため、clusterモジュールは、マルチコア CPU システムのパフォーマンスを最大限に活用するのに役立ちます。
簡単に言うと、clusterモジュールはレストランのマネージャーのようなもので、1人のウェイターがすべての客を対応するのではなく、複数のウェイター(ワーカー)を作成して客(リクエスト)を処理します。
なぜ Cluster モジュールが必要なのか?
パフォーマンスの向上:現代の CPU は通常複数のコアを搭載していますが、Node.js はデフォルトでは1つのコアしか使用しません。
clusterモジュールを使用すると、利用可能なすべての CPU コアを活用できます。信頼性の向上:1つのワーカーがクラッシュしても、他のワーカーはリクエストを処理し続けることができ、マスタープロセスはクラッシュしたワーカーを再起動できます。
ゼロダウンタイム:ワーカーを1つずつ再起動することで、サービスを中断せずにコードを更新できます。
Cluster モジュールの基本用法
例
const http = require('http');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
console.log(`マスタープロセス ${process.pid}が実行中です`);
// worker プロセスを生成
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code, signal) => {
console.log(`worker ${worker.process.pid}が終了しました`);
});
} else {
// Worker は同じ TCP 接続を共有できます
// ここは HTTP サーバーです
http.createServer((req, res) => {
res.writeHead(200);
res.end('こんにちは世界\n');
}).listen(8000);
console.log(`Worker ${process.pid}が起動しました`);
}
核心概念の解説
マスタープロセス(Master Process)
- すべての worker プロセスの管理を担当します
- 通常は実際のリクエストを処理しません
- worker のさまざまなイベントを監視できます
Worker プロセス
- 実際にリクエストを処理する子プロセス
- 各 worker は独立した Node.js プロセスです
- 同じサーバーポートを共有します
Cluster モジュールの常用 API
cluster.fork()
新しい worker プロセスを作成します。このメソッドを呼び出すと worker オブジェクトが返されます。
cluster.isMaster
マスタープロセスの場合は、true。
cluster.isWorker
worker プロセスの場合は、true。
cluster.workers
アクティブなすべての worker オブジェクトを含むハッシュテーブルで、キーはworker.idをキー名とします。
実際の適用シナリオ
1. 負荷分散
例
const cluster = require('cluster');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
} else {
require('./app'); // アプリケーションのエントリーファイル
}
2. グレースフル再起動
例
console.log(`Worker ${worker.process.pid}が終了しました`);
// ここで新しい worker を fork できます
cluster.fork();
});
3. プロセス間通信
例
worker.send('Hello worker!');
// Worker がメッセージを受信
process.on('message', (msg) => {
console.log(`Worker ${process.pid}メッセージを受信しました: ${msg}`);
});
パフォーマンス最適化のヒント
CPU コア数に応じて worker を作成:通常、worker の数は CPU コア数と同じにすることをお勧めします。
ポートの共有:すべての worker が同じポートを共有し、オペレーティングシステムが自動的にリクエストを各 worker に割り当てます。
接続の共有:長時間接続(WebSocket など)では、接続をどのように異なる worker に割り当てるかを考慮する必要があります。
状態の共有:worker 間でメモリは共有されないため、状態を共有するには Redis などの外部ストレージを使用する必要があります。
よくある問題と解決策
1. メモリリーク
各 worker は独立したプロセスであるため、1つの worker のメモリリークは他の worker に影響しませんが、異常な worker を監視して再起動する必要があります。
2. セッション維持
セッション維持が必要な場合は、スティッキーセッション(sticky session)を使用するか、セッションデータを外部(Redis など)に保存できます。
3. ファイルディスクリプタ制限
多数の worker はファイルディスクリプタの枯渇を引き起こす可能性があるため、システム制限を調整する必要があります:
例
Cluster モジュールの代替案
とはいえclusterモジュールは強力ですが、他の選択肢もあります:
- PM2:プロセスマネージャーであり、負荷分散と監視機能を内蔵しています
- Nginx:リバースプロキシであり、複数の Node.js インスタンス間で負荷分散できます
- Kubernete:コンテナオーケストレーションシステムであり、Node.js サービスを自動的にスケールできます
まとめ
Node.js のclusterモジュールは、高性能で高可用性のネットワークサービスを構築するための重要なツールです。複数の worker プロセスを作成することで、以下のことが可能になります:
- マルチコア CPU のパフォーマンスを最大限に活用
- アプリケーションの信頼性を向上
- ゼロダウンタイムでのデプロイを実現
マルチプロセスアプリケーションのセットアップと管理はシングルプロセスよりも複雑ですが、本番環境の Node.js アプリケーションでは、clusterモジュールまたは類似のソリューションを使用することがほぼ必須です。
このガイドが Node.js のclusterモジュールの理解と使用に役立つことを願っています!