ClusterずSocket.IO に぀いお

閲芧: 921 回
最初の未読メッセヌゞにスキップ

ナベ

未読、
2011/12/15 11:03:372011/12/15
To: nodejs_jp
ワタナベず申したす。

珟圚、Node.js + Express + Socket.IO でアプリケヌションを䜜成しおいるのですが、
あるメ゜ッドで時間のかかる凊理、具䜓的には、他のサむトのWeb APIをルヌプで叩く凊理をしたす。
そのため、そのメ゜ッドが終わらないず、別のリク゚ストを受け付けおも、凊理が始たらないので、
Cluster化をしたした。

通垞のシングルスレッドでの起動の堎合はうたく動䜜するのですが、
Cluster化をしたずころ、Socket.IOのsocket.onが fire されなくなり、
そもそもExpressを䜿った堎合のCluster化がおかしいのか、
Cluster化した堎合のSocket.IOの曞き方が間違っおいるのか
わからず、その点に関しお教えを願いたいず思っおメヌルしたした。

具䜓的には

cluster.js
----------------------------------------------
var cluster = require('cluster');
var http = require('http');
var numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
// Fork workers.
for (var i = 0; i < numCPUs; i++) {
cluster.fork();
}

cluster.on('death', function(worker) {
console.log('worker ' + worker.pid + ' died');
});
} else {
app = require('./app');
}
--------------------------------------------------

Experss のapp.jsのSocket.IO郚分
-----------------------------------------------
var io = require('socket.io');
io = io.listen(app);

io.sockets.on('connection', function (socket) {

console.log('connected --- server');

   socket.on('send_ex_id', function(data) {
^^^^^^^^^^^^^ ここが起動しない
console.log('---------experiment_id---------------',
data.experiment_id);
// other to do
});

console.log("----------io.sockets.on", new Date());

});
-----------------------------------------------

このような圢になっおいたす。
Node V0.6.4
Express 2.5.2
Socket.IO 0.8.7

それでは、よろしくお願いしたす。


Koichi Kobayashi

未読、
2011/12/15 12:30:002011/12/15
To: node...@googlegroups.com
小林 (koichik) です

> あるメ゜ッドで時間のかかる凊理、具䜓的には、他のサむトのWeb APIをルヌプで叩く凊理をしたす。
> そのため、そのメ゜ッドが終わらないず、別のリク゚ストを受け付けおも、凊理が始たらないので、

本題ではないのかもしれたせんがここが根本的に間違っおるのでは
ずいうかfs モゞュヌルなら Sync() があるけれど http/net で
ブロックするのは至難の業のような気も

Socket.IO や Express が Node v0.6 の cluster をサポヌト
しおいるかどうかは他の人よろしくですヌ


--
{
name: "Koichi Kobayashi",
mail: "koi...@improvement.jp",
blog: "http://d.hatena.ne.jp/koichik/",
twitter: "@koichik"
}

Tsutomu Watanabe

未読、
2011/12/16 4:37:372011/12/16
To: node...@googlegroups.com
小林さん、レスありがずうございたす。 

本題ではないのかもしれたせんがここが根本的に間違っおるのでは
ずいうかfs モゞュヌルなら Sync() があるけれど http/net で
ブロックするのは至難の業のような気も 

Node.jsから他のWeb APIをたたくずころは圓然非同期になっおいおいたす。

ただ、DBから取埗したレコヌド数、1000 ~ 10000に察しおhttpリク゚ストを発行するので、
簡単に曞くず
exports.merged = function(req, res) {

    Result.find({条件}, function(results) {
        for(var i = 0; i < results.length; i++) {
            http.get(options, function(req, res) {
                res.on('data', function(chunk) {
                    // chunkに察する凊理
                }.on('end') {
                    // 凊理
          }
            }
        }
        res.render('/hoge');
    }
} 

これ曞き方たずいでしょうか
 

Koichi Kobayashi

未読、
2011/12/16 5:00:462011/12/16
To: node...@googlegroups.com
小林 (koichik) です

> ただ、DBから取埗したレコヌド数、1000 ~ 10000に察しおhttpリク゚ストを発行するので、

(略)
> これ曞き方たずいでしょうか

http.get() ずいうより、倧量のデヌタを䞀気に凊理するのが
たずいのではないでしょうか。
process.nextTick() を挟むなり、それをやっおくれるフロヌ制埡
モゞュヌルを䜿うなりした方がいいでしょう。

Result.find({条件}, function(results) {
var i = 0, len = results.length;
function processRow() {
for (var j = 0; i < len && j < 10; ++i, ++j) { // 10件ず぀凊理
http.get(...);
}
if (i < len) {
process.nextTick(processRow);
}
}
processRow();
}


Date: Fri, 16 Dec 2011 18:37:37 +0900
From: Tsutomu Watanabe <watan...@gmail.com>
Subject: Re: [nodejs_jp:492] ClusterずSocket.IO に぀いお

--

Tsutomu Watanabe

未読、
2011/12/16 11:22:172011/12/16
To: node...@googlegroups.com
小林さん、どうもです。
 
http.get() ずいうより、倧量のデヌタを䞀気に凊理するのが
たずいのではないでしょうか。
process.nextTick() を挟むなり、それをやっおくれるフロヌ制埡
モゞュヌルを䜿うなりした方がいいでしょう。

process.nextTick()の存圚は知りたせんでしたが、調べた限り、フロヌ制埡などを䜿っおも
結局は、そのリク゚スト䟋えばhttp://localhost/mergeのようなURLに察する凊理から
は抜けれないので、぀たりメモリスタック䞊から陀かれない限り、そのリク゚ストに察する
凊理はブロックされるのではないでしょうか
1぀のリク゚ストの䞭の凊理では、フロヌ制埡なり、ノンブロッキングIOで前の凊理の終了を
埅たずに、凊理が進んでいきたすが。
今、manualを読み぀぀、child_processがプロセスのUNIXのforkみたいな感じなので、
Node.jsっぜくないですが、トラむしおみたす。

Koichi Kobayashi

未読、
2011/12/17 2:00:002011/12/17
To: node...@googlegroups.com
小林 (koichik) です

> 結局は、そのリク゚スト䟋えばhttp://localhost/mergeのようなURLに察する凊理から
> は抜けれないので、぀たりメモリスタック䞊から陀かれない限り、そのリク゚ストに察する
> 凊理はブロックされるのではないでしょうか
> 1぀のリク゚ストの䞭の凊理では、フロヌ制埡なり、ノンブロッキングIOで前の凊理の終了を
> 埅たずに、凊理が進んでいきたすが。

ちょっず意味が分かりたせんが
䞀぀のリク゚ストに察する凊理 (http.get() を呌び出す凊理) が
仮に 1 ミリ秒だった堎合10000 件䞀気に行うず 10 秒かかり
その間はむベントルヌプに戻らないので他の I/O をブロック
するこずになりたすが10 件ごずにむベントルヌプに戻れば
10 ミリ秒ごずに他の I/O を凊理できたすよね

そういうこずが可胜なフロヌ制埡モゞュヌルもありたす
Async.js (fjacobs/async.js) ずか
# 最近 JS アドカレで玹介された Async (caolan/async) ずは
# 䌌た名前の別物

https://github.com/fjakobs/async.js

Result.find({条件}, function(results) {
async.list(results)
.each(function(row) {
... // http.get() など 1 件ごずの凊理
})
.delay(0, 10) // 10 件ごずにむベントルヌプに戻る
.end();
}

みたいな

> 今、manualを読み぀぀、child_processがプロセスのUNIXのforkみたいな感じなので、
> Node.jsっぜくないですが、トラむしおみたす。

child_process.fork() は実際はただの fork() ではなく
fork()+exec() なので泚意
たぶんそれをトラむする必芁はないはずですが

それよりも小さな単䜍でむベントルヌプに戻るように
する方が重芁だず思いたすよ
クラむアントサむド (ブラりザ) でも setTimeout() を
䜿うなどしお凊理を小さく分割するのず同じです

どうせ http.get() は同じホストに察しおは (デフォルトでは)
5 接続しか同時に実行しないのでそれ以䞊䞀気に呌び出しおも
効果はありたせん

根本的な問題の解決に向かわずCluster を詊したり
child_process.fork() にトラむするずいうのは結局は
遠回りになるんじゃないかず思いたす

Tsutomu Watanabe

未読、
2011/12/17 6:40:052011/12/17
To: node...@googlegroups.com
小林さん、どうも。

結論から蚀うず、processTickを䜿っおも、同じように
぀のリク゚ストがデヌタを裁いおいる間は、他のリク゚ストは
埅たされたした。
forkした堎合は、圓たり前ですが同時に耇数のリク゚ストを裁きたした。

httpを発行する堎所ず、res.endの堎所でconsole.logで確認しおみたしたが、
ルヌプでhttpを発行しおいる間にも、res.endが怜知されるず、res.endの実行をしおいたす。

> 仮に 1 ミリ秒だった堎合10000 件䞀気に行うず 10 秒かかり
> その間はむベントルヌプに戻らないので他の I/O をブロック

ここがよく分からないのですが、他のI/Oをブロックしおいる
ずは、ルヌプに入るずころですよね
そこがブロックされるので、他のリク゚ストが、そこで埅たされるず
いうこずだず思うのですが、
小林さんが蚀っおるのは、ルヌプを现切れにしお、その途䞭で、
そのルヌプの䞭に他からのリク゚ストのルヌプ凊理を走らすずいうこずでしょうか
そういうこずは可胜なんでしょうか、その堎合の倉数の倀などはどうなるのでしょうか





 
 

Koichi Kobayashi

未読、
2011/12/17 8:00:002011/12/17
To: node...@googlegroups.com
小林 (koichik) です

> 結論から蚀うず、processTickを䜿っおも、同じように
> ぀のリク゚ストがデヌタを裁いおいる間は、他のリク゚ストは
> 埅たされたした。

nextTick() の䜿い方が悪いのでは
繰り返したすが「぀のリク゚ストがデヌタを裁いおいる間」の
「途䞭」で现かくむベントルヌプに戻ればそんなこずにはなりたせん

> httpを発行する堎所ず、res.endの堎所でconsole.logで確認しおみたしたが、
> ルヌプでhttpを発行しおいる間にも、res.endが怜知されるず、res.endの実行をしおいたす。

そんなはずはありたせん
res.end() はむベントルヌプに戻っおから呌び出されたす

var http = require('http');

http.createServer(function(req, res) {
res.writeHead(200);
res.end();
}).listen(3000, function() {
processRequest('Foo');
});

function processRequest(name) {
for (var i = 0; i < 10; ++i) {
(function(i) {
http.get({port: 3000}, function(res) {
res.on('end', function() {
console.log(name, i, 'response end');
});
});
console.log(name, i, 'request end');
})(i);
}
console.log('=====', name, 'processRequest end =====');
}

実行結果 (Ctrl-C で終了)

Foo 0 request end
Foo 1 request end
Foo 2 request end
Foo 3 request end
Foo 4 request end
Foo 5 request end
Foo 6 request end
Foo 7 request end
Foo 8 request end
Foo 9 request end
===== Foo processRequest end =====
Foo 0 response end
Foo 1 response end
Foo 2 response end
Foo 3 response end
Foo 4 response end
Foo 5 response end
Foo 6 response end
Foo 7 response end
Foo 8 response end
Foo 9 response end

このようにres.end() は http.get() を繰り返し呌び出しおいる
processRequest() のルヌプが終了するたで決しお呌び出されたせん
繰り返しをどれだけ倧きくしおも同様です
この䟋はサヌバを同じプロセスで実行しおいるので
ルヌプ䞭にレスポンスが返っおきおいるわけではありたせんが
別プロセスにしおも結果は同じです

> 小林さんが蚀っおるのは、ルヌプを现切れにしお、その途䞭で、
> そのルヌプの䞭に他からのリク゚ストのルヌプ凊理を走らすずいうこずでしょうか

そうです

> そういうこずは可胜なんでしょうか、

むベントルヌプに戻れば可胜です

> その堎合の倉数の倀などはどうなるのでしょうか

JavaScript なのでクロヌゞャヌを倚甚するこずになりたす
先の䟋の

}).listen(3000, function() {
processRequest('Foo');
});

を

}).listen(3000, function() {
processRequest('Foo');
processRequest('Bar');
});

ず倉曎するず実行結果は次のようになりたす

Foo 0 request end
Foo 1 request end
Foo 2 request end
Foo 3 request end
Foo 4 request end
Foo 5 request end
Foo 6 request end
Foo 7 request end
Foo 8 request end
Foo 9 request end
===== Foo processRequest end =====
Bar 0 request end
Bar 1 request end
Bar 2 request end
Bar 3 request end
Bar 4 request end
Bar 5 request end
Bar 6 request end
Bar 7 request end
Bar 8 request end
Bar 9 request end
===== Bar processRequest end =====
Foo 0 response end
Foo 1 response end
Foo 2 response end
Foo 3 response end
Foo 4 response end
Foo 5 response end
Foo 6 response end
Foo 7 response end
Foo 8 response end
Foo 9 response end
Bar 0 response end
Bar 1 response end
Bar 2 response end
Bar 3 response end
Bar 4 response end
Bar 5 response end
Bar 6 response end
Bar 7 response end
Bar 8 response end
Bar 9 response end

Foo (これが䞀぀のリク゚ストだず考えおください) を凊理
しおいる間Bar (これは別のリク゚スト) は凊理されたせん
これが今回の問題です

そこで processRow() を次のようにしたす

function processRequest(name) {
var i = 0, len = 10;
(function processRows() {
for (var j = 0; i < len && j < 3; ++i, ++j) { // 3件ず぀凊理
(function(i) {
http.get({port: 3000}, function(res) {
res.on('end', function() {
console.log(name, i, 'response end');
});
});
console.log(name, i, 'request end');
})(i);
}
if (i < len) {
process.nextTick(processRows);
}
})();
console.log('=====', name, 'processRequest end =====');
}

結果は次のようになりたす

Foo 0 request end
Foo 1 request end
Foo 2 request end
===== Foo processRequest end =====
Bar 0 request end
Bar 1 request end
Bar 2 request end
===== Bar processRequest end =====
Foo 3 request end
Foo 4 request end
Foo 5 request end
Bar 3 request end
Bar 4 request end
Bar 5 request end
Foo 6 request end
Foo 7 request end
Foo 8 request end
Bar 6 request end
Bar 7 request end
Bar 8 request end
Foo 9 request end
Bar 9 request end
Foo 0 response end
Foo 1 response end
Foo 2 response end
Bar 0 response end
Bar 1 response end
Bar 2 response end
Foo 3 response end
Foo 4 response end
Foo 5 response end
Bar 3 response end
Bar 4 response end
Bar 5 response end
Foo 6 response end
Foo 7 response end
Foo 8 response end
Bar 6 response end
Bar 7 response end
Bar 8 response end
Foo 9 response end
Bar 9 response end

Foo の凊理の合間に Bar の凊理が挟たりたした
Foo・Bar それぞれ 10 件ず぀だず党おの GET リク゚ストが
完了しおからレスポンスを受信したすがlen = 100 ずすれば
GET リク゚ストが党お完了する前にレスポンスの受信も
挟たっおきたす

おそらくNode に限らずむベント駆動のプログラミングに
慣れおいないのではないかず思いたす
ひずたず䞊蚘のサンプルがどのような動きをするのか
むベントルヌプずどのように協調動䜜すべきなのか
じっくり考えおみおは

党員に返信
投皿者に返信
転送
新着メヌル 0 ä»¶