teratail header banner
teratail header banner
質問するログイン新規登録

質問編集履歴

2

コードの修正

2016/04/11 15:43

投稿

bleurouge
bleurouge

スコア161

title CHANGED
File without changes
body CHANGED
@@ -62,7 +62,7 @@
62
62
  return new Promise(function(resolve) {
63
63
 
64
64
  heavyDataKeyArr.forEach(function(redisKey) {
65
- redisKey.get(redisKey, function(err, heavyData){
65
+ redis-client.get(redisKey, function(err, heavyData){
66
66
  // redisに保存されている redisKey:heavyData に対する計算
67
67
  :
68
68
  RedisSetter(redisKey, heavyData); // 場合により Promise then から resolveへ

1

補足情報を追記

2016/04/11 15:43

投稿

bleurouge
bleurouge

スコア161

title CHANGED
File without changes
body CHANGED
@@ -1,19 +1,89 @@
1
- clusterモジュールを用いて特定の計算処理のみ子プロセスで処理を行っていましたが、結局のところ、clusterを用いた方法が効率的なのか分からなくなって。定期的に発生する頻度の高いCPUバウンドな処理はchild_processのpool系モジュールを使った子プロセスで分散処理が良いのでは??も思います。
1
+ clusterモジュールを用いて特定の重い計算処理のみ子プロセスで処理を行っていましたが、結局のところ、clusterを用いた方法が手法として効率的なのか分からなくなってした。定期的に発生する頻度の高いCPUバウンドな処理はchild_processのpool系モジュールを使った子プロセスで分散処理が効率的ではないか感じています。nodeでの分散処理の手法について、以下の考え方で妥当なのか、向き不向きについて知見がありましたら、ご意見いただければ幸いです。
2
2
 
3
+ ▼当初clusterを用いて分散処理を行った手法
4
+ ```javascript
5
+ /////////////////////
6
+ // master.js
7
+ ////////////////////
8
+ var cluster = require('cluster');
9
+ var numCPUs = require('os').cpus().length;
3
- nodeでの分散処理の手法について、以下の考え方で妥当なのか、向き不向きについて知見がありましたら、ご意見いただければ幸いです。
10
+ var HeavyFunc = require('./heavy-func');
4
-
5
11
 
6
- ---
12
+
7
- **▼nodeアプリケーション自体を並列化、スケールさせたいケース**
8
- cluster.forkを使う
13
+ if(cluster.isWorker) {
14
+ // master 側から処理対象データを受け取る箇所
15
+ process.on('message', function(msg) {
16
+ if(msg.signal === 'heavy-data') {
17
+ HeavyFunc(msg.data)
18
+ .then(function() {
19
+ // cluster で終了した処理を master 側に通知する箇所
20
+ process.send({ workerId: msg.workerId, status: 'complete' });
21
+ })
22
+ .catch(function(e) {
23
+ console.log(e);
24
+ });
25
+ }
26
+ });
27
+ return;
28
+ }
29
+ if(cluster.isMaster) {
30
+
31
+ // master 側から処理対象のデータを cluster に引き渡す箇所
32
+ Object.keys(cluster.workers).forEach(function(id) {
33
+ cluster.workers[id].send(
34
+ { workerId: id, signal: 'heavy-data', data: heavyDataKeyArr }
35
+ );
36
+ });
37
+
38
+ // cluster で終了した処理通知を master 側で受ける箇所
39
+ var returnWorkersId = [];
40
+ Object.keys(cluster.workers).forEach(function(id) {
41
+ cluster.workers[id].on('message', function(msg){
42
+ if(msg.status === 'complete') {
43
+ returnWorkersId.push(msg.workerId);
44
+ if(returnWorkersId.length === numCPUs) {
45
+ console.log('worker processes complete the computing');
46
+ returnWorkersId = [];
47
+ }
48
+ cluster.workers[msg.workerId].removeAllListeners('message');
49
+ }
50
+ return;
51
+ });
52
+ });
53
+
54
+ }
55
+
9
56
 
57
+ ////////////////////
10
- clusterモジュールはアプリケーション自体を並列状態にしてスケールさせたいケースに向いている??
58
+ // heavy-func.js 分散処理対象のモジュール
11
- 特定の処理を分散させるためにclusterでアリ全体の子プロセスを持つは非効率??
59
+ // masterプロセスのみで処理する場合、5000ms以上費やす計算
60
+ ////////////////////
61
+ function HeavyFunc(heavyDataKeyArr) {
62
+ return new Promise(function(resolve) {
63
+
64
+ heavyDataKeyArr.forEach(function(redisKey) {
65
+ redisKey.get(redisKey, function(err, heavyData){
66
+ // redisに保存されている redisKey:heavyData に対する計算
67
+ :
68
+ RedisSetter(redisKey, heavyData); // 場合により Promise then から resolveへ
69
+ resolve();
70
+ });
71
+ });
72
+
73
+ });
74
+ }
75
+
76
+ ```
12
77
 
78
+ 上記方法により各プロセスごとに計算処理を振り分けることは可能でしたが、
13
- 例)
79
+ そもそも、
14
- webサリクエスト待ち受け等、nodeアプリ全体を並列化てスケールさせたい場合
80
+ - **親プロセス←→子プロセスでのデの受けと処理終了通知の通信が必要となる**
81
+ - **上記特定処理(heavy-func.js)のみのために、記載コードすべてを含んだ子プロセス(アプリのコピー)が上がっている ※1 無駄な感じがある**
82
+ - **上2点あわせて、無駄なコード・無駄なメモリ消費につながっている**
15
83
 
84
+ 以上、clusterを利用する手法はnodeアプリ自体を並列化・スケールさせる際に有効な手法なのではないかと感じました。それは、ドキュメントの[サンプル](https://nodejs.org/api/cluster.html#cluster_cluster)がwebサーバを並列化させていることとも関連していそうです。
85
+
16
- **【補足】**
86
+ ※1(補足
17
87
  [http://postd.cc/setting-up-a-node-js-cluster/](http://postd.cc/setting-up-a-node-js-cluster/)
18
88
  > cluster.fork()とchild_process.fork()には、いくつかの主な違いがあります。
19
89
  > :
@@ -21,33 +91,38 @@
21
91
  > したがって、アプリケーションのエントリポイントがindex.jsでありながらワーカがcluster-my-app.jsの
22
92
  > 中で生成された場合でも、ワーカはやはり、index.jsの先頭から実行コードを起動します。
23
93
 
94
+ よって、nodeでの分散処理を考える場合、以下ケースになりそうだと感じています。再掲になりますが、nodeでの分散処理の手法について、以下の考え方で妥当なのか、向き不向きについて知見がありましたら、ご意見いただければ幸いです。
24
95
 
96
+ ---
97
+ **▼nodeアプリケーション自体を並列化、スケールさせたいケース**
98
+ →cluster.forkを使う
99
+
100
+ clusterモジュールはアプリケーション自体を並列状態にしてスケールさせたいケースで有用
101
+
102
+ 使用例)
103
+ nodeのwebサーバを含めて、nodeアプリ自体を並列化してスケールできる場合
25
104
 
26
105
  ---
27
106
  **▼発生頻度の低いCPUヘビーな処理をnodeで行いたいケース**
28
-
29
107
  →child_process.forkを使う
30
108
 
31
- 処理発生時に子プロセスを立ち上げて自分で管理。メインのイベントループがブロックされないように、処理を逃したいケースに向いている??
109
+ 処理発生時に子プロセスを立ち上げて終了時にクローズ頻度の低い重い処理が発生した際、メインのイベントループがブロックされないように、処理を逃したいケースで有用
32
- 処理が頻繁に発生する場合はnode-worker-farm等のpool系モジュールを利用したほうが効率的??
33
110
 
34
- 例)
111
+ 使用例)
35
- バッチ処理的な用途ほか、動画のエンコード、画像処理等ほか、頻度は低いが、数十秒〜数分の長い時間を要する計算処理(nodeでやるかは抜きで
112
+ バッチ処理的な用途ほか、動画のエンコード、画像処理等ほか、頻度は低いが、数十秒〜数分の長い時間を要する計算処理(そもそもnodeでやるかは置いといて
36
113
 
37
-
38
114
  ---
39
115
  **▼発生頻度が高い、あるいは、定期的に発生するCPUバウンドな処理をnodeで行いたいケース**
40
-
41
116
  →node-worker-farmなどchild_processプール系モジュール利用
42
117
 
43
- プール状態のchild_processで、頻繁に発生する負荷のある処理を行いたいケースに向いている??
118
+ プール状態のchild_processで、頻繁に発生する負荷のある処理を行いたいケースで有用
44
119
 
45
- 例)
120
+ 使用例)
46
- リアルタイム性の高い計算処理、集計処理など、頻度の高い数秒〜数十秒時間を要する計算処理
121
+ リアルタイム性の高い計算処理、集計処理など、頻度の高い数秒〜数十秒時間を要する計算処理。私が上記コードでclusterを用いて行っていたような特定処理の分散用途
47
122
 
48
123
 
49
124
  ---
50
- **【参考情報】**
125
+ **【参考にした情報】**
51
126
  [Why you should use Node.js for CPU-bound tasks](http://neilk.net/blog/2013/04/30/why-you-should-use-nodejs-for-CPU-bound-tasks/)
52
127
  [Worker Farm - npm](https://www.npmjs.com/package/worker-farm)
53
128
  [Node.jsのClusterをセットアップして、処理を並列化・高速化する](http://postd.cc/setting-up-a-node-js-cluster/)