Node.jsはシングルスレッドの非同期処理で高いパフォーマンスを発揮しますが、CPUを多用する処理(画像変換・暗号化・大量計算など)ではボトルネックになることがあります。
このような場面で活躍するのが、Child Process(子プロセス)を使ったマルチプロセス処理です。Node.jsでもCPUを並列に使いこなすことで、処理速度を大きく向上できます。
Node.jsはなぜCPUに弱い?
Node.jsはイベントループでノンブロッキングI/Oを得意としますが、CPUを専有する重たい処理が1スレッド内にあると、他のリクエスト処理が詰まってしまいます。
これを解消するには、重い処理だけを別プロセスに切り出し、メインの処理と分離する必要があります。
Child Processモジュールとは
child_process
モジュールは、Node.jsで新しいプロセスを生成し、親プロセスと並列に動かすことができる標準機能です。
以下の4つのメソッドがあります:
exec
:コマンドを実行してバッファで結果を取得spawn
:ストリームベースで長時間実行向きfork
:Node.js専用スクリプトに特化(IPC通信が容易)execFile
:外部バイナリを直接実行
本記事では、forkによる子プロセスの作成と、プロセス間通信による並列処理の実装例を中心に解説します。
基本的なforkの使い方
まずは、子プロセス用のファイルを用意します。
child.js
// 子プロセス側
process.on('message', (msg) => {
console.log('親からの指示:', msg);
const result = msg.num * 2; // 重たい処理の代わり
process.send({ result });
});
main.js
const { fork } = require('child_process');
const child = fork('./child.js');
child.send({ num: 42 });
child.on('message', (msg) => {
console.log('子プロセスからの結果:', msg.result); // 84
});
fork
を使うと、親子でsend()
によるシンプルなデータ通信(IPC)が可能です。
並列処理に応用する
複数のデータセットに対して、同時に複数の子プロセスを立ち上げて処理することもできます。
const { fork } = require('child_process');
const dataset = [10, 20, 30, 40];
const results = [];
dataset.forEach((num, index) => {
const child = fork('./child.js');
child.send({ num });
child.on('message', (msg) => {
results[index] = msg.result;
child.kill();
if (results.length === dataset.length) {
console.log('全結果:', results);
}
});
});
このように、CPUコアを活かした並列処理が可能になります。
注意点とベストプラクティス
- 子プロセスは独立したメモリ空間を持つため、メモリの使用量に注意
- 大量のforkはオーバーヘッドになるため、ワーカープール(
worker_threads
や外部ライブラリ)導入を検討 - プロセス間通信(IPC)のデータサイズは必要最小限に
まとめ|Node.jsでもマルチコアを活かす
Node.jsは非同期I/Oに強みがありますが、CPU負荷の高い処理にはforkを使ったマルチプロセス設計が有効です。
child_process.forkを使えば、Node.jsのコードだけで並列処理を簡単に実現でき、CPUリソースをフルに活かしたアーキテクチャを構築できます。
さらに高度な用途では、worker_threads
やcluster
モジュールと組み合わせて、拡張性のあるマルチプロセス設計を目指すのもよいでしょう。