質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

Q&A

解決済

2回答

4779閲覧

nodeでのCPUバウンドな処理、分散処理への考え方について(child_processを使う?clusterを使う?)

bleurouge

総合スコア161

Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

1グッド

0クリップ

投稿2016/04/10 12:12

編集2016/04/11 15:43

clusterモジュールを用いて特定の重い計算処理のみ子プロセスで処理を行っていましたが、結局のところ、clusterを用いた方法が手法として効率的なのか分からなくなってきました。定期的に発生する頻度の高いCPUバウンドな処理はchild_processのpool系モジュールを使った子プロセスで分散処理が効率的ではないかと感じています。nodeでの分散処理の手法について、以下の考え方で妥当なのか、向き不向きについて知見がありましたら、ご意見いただければ幸いです。

▼当初clusterを用いて分散処理を行った手法

javascript

1///////////////////// 2// master.js 3//////////////////// 4var cluster = require('cluster'); 5var numCPUs = require('os').cpus().length; 6var HeavyFunc = require('./heavy-func'); 7 89if(cluster.isWorker) { 10 // master 側から処理対象データを受け取る箇所 11 process.on('message', function(msg) { 12 if(msg.signal === 'heavy-data') { 13 HeavyFunc(msg.data) 14 .then(function() { 15 // cluster で終了した処理を master 側に通知する箇所 16 process.send({ workerId: msg.workerId, status: 'complete' }); 17 }) 18 .catch(function(e) { 19 console.log(e); 20 }); 21 } 22 }); 23 return; 24} 25if(cluster.isMaster) { 2627 // master 側から処理対象のデータを cluster に引き渡す箇所 28 Object.keys(cluster.workers).forEach(function(id) { 29 cluster.workers[id].send( 30 { workerId: id, signal: 'heavy-data', data: heavyDataKeyArr } 31 ); 32 }); 3334 // cluster で終了した処理通知を master 側で受ける箇所 35 var returnWorkersId = []; 36 Object.keys(cluster.workers).forEach(function(id) { 37 cluster.workers[id].on('message', function(msg){ 38 if(msg.status === 'complete') { 39 returnWorkersId.push(msg.workerId); 40 if(returnWorkersId.length === numCPUs) { 41 console.log('worker processes complete the computing'); 42 returnWorkersId = []; 43 } 44 cluster.workers[msg.workerId].removeAllListeners('message'); 45 } 46 return; 47 }); 48 }); 4950} 5152 53//////////////////// 54// heavy-func.js 分散処理対象のモジュール 55// masterプロセスのみで処理する場合、5000ms以上費やす計算 56//////////////////// 57function HeavyFunc(heavyDataKeyArr) { 58 return new Promise(function(resolve) { 5960 heavyDataKeyArr.forEach(function(redisKey) { 61 redis-client.get(redisKey, function(err, heavyData){ 62 // redisに保存されている redisKey:heavyData に対する計算 63 : 64 RedisSetter(redisKey, heavyData); // 場合により Promise then から resolveへ 65 resolve(); 66 }); 67 }); 6869 }); 70} 71

上記方法により各プロセスごとに計算処理を振り分けることは可能でしたが、
そもそも、

  • 親プロセス←→子プロセスでのデータの受け渡しと処理終了通知の通信が必要となる
  • 上記特定処理(heavy-func.js)のみのために、記載コードすべてを含んだ子プロセス(アプリのコピー)が上がっている ※1 無駄な感じがある
  • 上2点あわせて、無駄なコード・無駄なメモリ消費につながっている

以上、clusterを利用する手法はnodeアプリ自体を並列化・スケールさせる際に有効な手法なのではないかと感じました。それは、ドキュメントのサンプルがwebサーバを並列化させていることとも関連していそうです。

※1(補足)
http://postd.cc/setting-up-a-node-js-cluster/

cluster.fork()とchild_process.fork()には、いくつかの主な違いがあります。

clusterが、自身が実行を始めたのと同じモジュールの先頭からワーカの実行コードを起動することです。
したがって、アプリケーションのエントリポイントがindex.jsでありながらワーカがcluster-my-app.jsの
中で生成された場合でも、ワーカはやはり、index.jsの先頭から実行コードを起動します。

よって、nodeでの分散処理を考える場合、以下ケースになりそうだと感じています。再掲になりますが、nodeでの分散処理の手法について、以下の考え方で妥当なのか、向き不向きについて知見がありましたら、ご意見いただければ幸いです。


▼nodeアプリケーション自体を並列化、スケールさせたいケース
→cluster.forkを使う

clusterモジュールはアプリケーション自体を並列状態にしてスケールさせたいケースで有用

使用例)
nodeのwebサーバを含めて、nodeアプリ自体を並列化してスケールできる場合


▼発生頻度の低いCPUヘビーな処理をnodeで行いたいケース
→child_process.forkを使う

処理発生時に子プロセスを立ち上げて処理終了時にクローズ。頻度の低い重い処理が発生した際、メインのイベントループがブロックされないように、処理を逃したいケースで有用

使用例)
バッチ処理的な用途ほか、動画のエンコード、画像処理等ほか、頻度は低いが、数十秒〜数分の長い時間を要する計算処理(そもそもnodeでやるかは置いといて)


▼発生頻度が高い、あるいは、定期的に発生するCPUバウンドな処理をnodeで行いたいケース
→node-worker-farmなどchild_processプール系モジュール利用

プール状態のchild_processで、頻繁に発生する負荷のある処理を行いたいケースで有用

使用例)
リアルタイム性の高い計算処理、集計処理など、頻度の高い数秒〜数十秒時間を要する計算処理。私が上記コードでclusterを用いて行っていたような特定処理の分散用途


【参考にした情報】
Why you should use Node.js for CPU-bound tasks
Worker Farm - npm
Node.jsのClusterをセットアップして、処理を並列化・高速化する
Do we have a standard way to run CPU-bound tasks in node yet?

ikuwow👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

回答がつかないと思っていた中、頂けた回答をベストアンサーとしました。

補足です。
現在、質問にあげた特定の処理に対する複数プロセス利用はnode-worker-farmを利用しています。
clusterについてはsyuiloさんに回答頂いた通りかと思います。

また、node-worker-farmは最後の更新が1年以上前ですが、react-nativeのモジュールが使い始めたこともあり最近ダウンロードが伸びているようです。

いまの所、大量の処理を複数プロセスで分散させて扱いたい場合はnode-worker-farmを使うのがベストプラクティスかもしれません。
https://www.npmjs.com/package/worker-farm

投稿2016/06/26 04:01

bleurouge

総合スコア161

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

その考え方で大丈夫だと思いますが、基本的にNode.jsは重い処理を実行するのは向かないので、複数プロセスをforkして複数のWebサーバーへのリクエストを捌く程度しか向かないと思います。
その場合は複数のCPUコアも活用できますので有用です。

clusterはそういった用途向けだと思われます。

投稿2016/06/22 03:19

syuilo

総合スコア393

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

bleurouge

2016/06/26 03:59

回答いただきありがとうございました。 いまの所、単にCPUヘビーではなく負荷のある大量の処理を捌きたい場合はnode-worker-farmを使うの良さそうという結論に至りました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問