2012-02-29 14 views
2

こんにちは皆さん、私は非常に実用的なredisユースケースの質問があります。 Redisで平均要求時間を以下のjsコードで保存したいとします。基本的に私はnode.js + redisを使用して平均リクエスト時間を保存する

var rc=require('redis').createClient() 
    ,rc2=require('redis').createClient() 
    ,test_data=[ 
     ['path/1', 100] 
     ,['path/2', 200] 
     ,['path/1', 50] 
     ,['path/1', 70] 
     ,['path/3', 400] 
     ,['path/2', 150] 
    ]; 

rc.del('reqtime'); 
rc.del('reqcnt'); 
rc.del('avgreqtime'); 

for(var i=0, l=test_data.length; i<l; i++) { 
    var item=test_data[i], req_path=item[0], req_time=item[1]; 
    console.log('debug: iteration # %d, item=%j', i, item); 
    rc.zincrby('reqtime', req_time, req_path); 
    rc.zincrby('reqcnt', 1, req_path, function(err, c) { 
     rc2.zscore('reqtime', req_path, function(err, t) { 
      var avg=t/c; 
      console.log('req_path='+req_path+',t='+t+',c='+c); 
      console.log('debug: added member %s to sorted set "avgreqtime" with score %f', req_path, avg); 
      rc2.zadd('avgreqtime', avg, req_path); 
     }); 
    }); 
} 
rc.quit(); 
rc2.quit(); 

平均要求時間を計算し、各要求項目([req_path、req_time])時Redisのために保存しようとしています。しかしavgreqtimeキーの期待どおりに動作していないです。私が得たstdoutから

debug: iteration # 0, item=["path/1",100] 
debug: iteration # 1, item=["path/2",200] 
debug: iteration # 2, item=["path/1",50] 
debug: iteration # 3, item=["path/1",70] 
debug: iteration # 4, item=["path/3",400] 
debug: iteration # 5, item=["path/2",150] 
req_path=path/2,t=undefined,c=1 
debug: added member path/2 to sorted set "avgreqtime" with score %f NaN 
req_path=path/2,t=undefined,c=1 
debug: added member path/2 to sorted set "avgreqtime" with score %f NaN 
req_path=path/2,t=undefined,c=2 
debug: added member path/2 to sorted set "avgreqtime" with score %f NaN 
req_path=path/2,t=undefined,c=3 
debug: added member path/2 to sorted set "avgreqtime" with score %f NaN 
req_path=path/2,t=undefined,c=1 
debug: added member path/2 to sorted set "avgreqtime" with score %f NaN 
req_path=path/2,t=undefined,c=2 
debug: added member path/2 to sorted set "avgreqtime" with score %f NaN 

Redis関数内のデバッグ行は、各繰り返しではなく、最後に一度に出力されます。私はこれがnode.jsの非同期性と関係していると思いますが、この作業をどのように手に入れるかという手がかりはありません。実験として、私も成功せず、次のとforループを交換してみました:

for(var i=0, l=test_data.length; i<l; i++) { 
    var item=test_data[i], req_path=item[0], req_time=item[1]; 
    console.log('debug: iteration # %d, item=%j', i, item); 
    rc.multi() 
     .zincrby('reqtime', req_time, req_path) 
     .zincrby('reqcnt', 1, req_path) 
     .exec(function(err, replies) { 
      console.log('debug(%s): got %j', req_path, replies); 
      var avg=replies[0]/replies[1]; 
      rc2.zadd('avgreqtime', avg, req_path); 
     }); 
} 

私は、各反復で、この時間を合計要求時間を得たが、問題は、最後のreq_pathある「パス/ 2」とreq_path棒でありますtest_dataでその結果のみ「パス/ 2」avgreqtimeする保存されます、それは間違っている:私はRedisの2.4.5使用しています

debug: iteration # 0, item=["path/1",100] 
debug: iteration # 1, item=["path/2",200] 
debug: iteration # 2, item=["path/1",50] 
debug: iteration # 3, item=["path/1",70] 
debug: iteration # 4, item=["path/3",400] 
debug: iteration # 5, item=["path/2",150] 
debug(path/2): got ["100","1"] 
debug(path/2): got ["200","1"] 
debug(path/2): got ["150","2"] 
debug(path/2): got ["220","3"] 
debug(path/2): got ["400","1"] 
debug(path/2): got ["350","2"] 

を、ノードRedisのクライアントはあなたの推測で正しいhttps://github.com/mranney/node_redis

答えて

5

からですノードの非同期性と関係しています。 、iは、あなたがそれはあなたが(someAsyncFunctionにパラメータとして)それを参照してください最初の時間であることを期待するものになります。ここ

for(var i = 0; i < 10; i++) { 
    someAsyncFunction(i, function(err, data) { 
    console.log("executed function for", i); 
    }); 
} 

:私はここに簡単な例をしようとします。 の中には、その関数へのコールバックiは常に10になります。 forループは、コールバックが実行される時点までに終了しています。これを修正するには、バインドi何とかする必要があります。一つの方法は、すぐに実行、無名関数です:

for(var i = 0; i < 10; i++) { 
    (function(i) { 
    someAsyncFunction(i, function(err, data) { 
     console.log("executed function for", i); 
    }); 
    })(i); // Execute function with parameter i immediately 
} 

さて、iもコールバックの内側に、正しい値にバインドされます。毎回新しい関数を指定する必要があるため、最適ではありません。これは良いです:

var executeTheFunction = function(i) { 
    someAsyncFunction(i, function(err, data) { 
    console.log("executed function for", i); 
    }); 
}; 

for(var i = 0; i < 10; i++) { 
    executeTheFunction(i); 
} 

私たちのexecuteTheFunctionは、コールバックを受けないことに注意してください。つまり、実際には実行を制御することはできません。すべての呼び出しがすぐに実行されます。多くの呼び出しがある場合は、必要でない可能性があります。そのような場合は、async moduleをお勧めします。

更新:はここasyncに例を示します

var calculateAverage = function(item, callback) { 
    var req_path = item[0], req_time = item[1]; 

    rc.multi() 
     .zincrby('reqtime', req_time, req_path) 
     .zincrby('reqcnt', 1, req_path) 
     .exec(function(err, replies) { 
      if(err) return callback(err); 
      console.log('debug(%s): got %j', req_path, replies); 
      var avg=replies[0]/replies[1]; 
      rc2.zadd('avgreqtime', avg, req_path, callback); 
     }); 
} 

async.map(test_data, calculateAverage, function(err) { 
    if(err) 
     console.error("Error:", err); 
    else 
     console.log("Finished"); 
}); 

を今、あなたは簡単に魔法のようにasync.queueなど

+1

作品で原料のこの種を管理することができます。この興味深いモジュールを指摘してくれてありがとう。 – ricochen

+1

このモジュールに慣れてきて、私はちょうどその例を書きました(私のワードプレスの投稿の1つ)[http://ricochen.wordpress.com/2011/10/15/node-js-example-2-parallel-私は数ヶ月前に書いた[処理する]。非同期モジュールを使用するコードはずっと簡単です。 – ricochen

関連する問題