Node.jsの新常識:BlobとBroadcastChannelで実現する、Worker間通信の「革命的シンプル化」
こんにちは。開発環境アーキテクトです。
Node.jsで「並列処理」や「非同期通信」を実装しようとしたとき、これまで皆さんはどうしていましたか? `EventEmitter`でイベントを張り巡らせたり、`worker_threads`の複雑な`postMessage`のハンドリングに頭を抱えたり……。正直、あのコードの海を泳ぐのは骨が折れますよね。
しかし、Node.js 18以降、状況は一変しました。ブラウザ標準のAPIである `Blob` と `BroadcastChannel` がNode.jsに標準搭載されたことで、「プロセス間通信の設計」は劇的にシンプルになったのです。
今日は、なぜこのAPIを使うとコードが美しくなるのか、その本質を紐解いていきましょう。
—
1. なぜ「Blob + BroadcastChannel」なのか?
従来の `worker_threads` における `parentPort.postMessage()` は、非常に強力ですが「双方向の接続管理」にコストがかかりすぎます。通信相手が増えるたびに、誰がどのチャンネルを監視するかのロジックがスパゲッティ化しがちでした。
対して `BroadcastChannel` は、「名前付きのチャンネルを介したPub/Subモデル」です。
- 疎結合: 送信側は受信側の存在を知る必要がありません。
- 直感的: チャンネル名さえ合っていれば、Node.jsのメインプロセスだろうがWorkerスレッドだろうが、どこでもメッセージを投げ合えます。
- Blobの役割: `Blob`を使うと、実行時に動的に生成したコード(Workerのロジック)をファイルとして保存せず、インメモリでWorkerに渡せます。これにより、ビルド工程なしで動的にスレッドを生成する「究極のオンデマンド並列処理」が可能になるのです。
—
2. 構築:インメモリ・Workerの魔法
今回は、わざわざ別ファイルを用意せず、コード内でWorkerを生成し、チャンネルを通じて通信するサンプルを作ります。
準備
Node.js 18以上がインストールされていれば追加のインストールは不要です。標準APIだけで完結します。
実装:BroadcastChannelによる疎結合通信
// main.js
const { Worker } = require(‘worker_threads’);
const { Blob } = require(‘node:buffer’);
// 1. Workerで実行するコードをBlobとして定義(外部ファイル不要)
const workerCode = `
const { BroadcastChannel } = require(‘worker_threads’);
const channel = new BroadcastChannel(‘chat_channel’);
// メインからのメッセージを待ち受ける
channel.onmessage = (event) => {
console.log(‘[Worker] 受信したデータ:’, event.data);
// 処理して返信する
channel.postMessage(‘Workerからの応答: ‘ + event.data.toUpperCase());
};
`;
// 2. Blobを作成し、Workerを起動
const blob = new Blob([workerCode], { type: ‘application/javascript’ });
const worker = new Worker(URL.createObjectURL(blob));
// 3. メイン側も同じチャンネルに参加
const mainChannel = new BroadcastChannel(‘chat_channel’);
mainChannel.onmessage = (event) => {
console.log(‘[Main] Workerからのメッセージ:’, event.data);
};
// 4. メッセージを送信
console.log(‘[Main] メッセージ送信…’);
mainChannel.postMessage(‘hello world’);
—
3. この設計が「現場」にもたらす利益
この手法をマスターすると、以下の3つの利益を享受できます。
1. デプロイ単位の簡素化: 多数のWorkerファイルを管理する必要がありません。複雑な計算ロジックを文字列として動的に生成・注入できるため、特定のビジネスロジックだけを隔離して計算させるような「マイクロサービス的な構成」を1ファイル内で完結できます。
2. 分散キャッシュ連携: 例えば、複数のWorkerで共有する「ローカルキャッシュの更新通知」を、この`BroadcastChannel`で行うことで、共有メモリ(SharedArrayBuffer)の複雑な排他制御から解放されます。
3. 可読性の劇的な向上: 「どこで何が投げられたか」がチャンネル名だけで追跡できるため、コードの追跡コストが数分の一になります。
—
4. 実行ログと確認
上記のコードを `node main.js` で実行すると、以下のような美しい結果が得られます。
$ node main.js
[Main] メッセージ送信…
[Worker] 受信したデータ: hello world
[Main] Workerからのメッセージ: Workerからの応答: HELLO WORLD
この「メインとWorkerが、まるで同じ場所にいるかのように会話する」体験こそが、現代のNode.jsアーキテクチャの真骨頂です。
—
先輩からのアドバイス:ここが落とし穴!
最後に一つだけ。`BroadcastChannel` は非常に便利ですが、「送ったメッセージが自分自身に届くことはない」 という仕様を忘れないでください。もし自分自身にも通知を送りたい場合は、別のロジックを組む必要があります。
また、`Blob` を使ったWorkerの生成は、メモリ上にコードを展開するため、頻繁に生成・破棄を繰り返すとメモリリークの原因になります。Workerは「使い捨て」ではなく「プーリング(再利用)」する設計と組み合わせるのが、プロの仕事です。
これをマスターすれば、あなたの書くNode.jsコードは、これまで以上にスケーラブルで、美しく、そして何より「動かしていて楽しい」ものになるはずです。ぜひ、今日からあなたのプロジェクトの通信層を `BroadcastChannel` に置き換えてみてください。世界が変わりますよ。