嗨伙计们,我有一个非常实用的redis用例问题.假设我想使用以下js代码存储Redis的平均请求时间.基本上我试图计算平均请求时间并在每个请求条目时保存到redis([req_path,req_time])
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();
Run Code Online (Sandbox Code Playgroud)
但它没有像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
Run Code Online (Sandbox Code Playgroud)
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);
});
}
Run Code Online (Sandbox Code Playgroud)
这次我在每次迭代中都得到了总请求时间,但问题是req_path坚持使用'path/2',它是test_data中的最后一个req_path.结果只有'path/2'被保存到avgreqtime,这是错误的:
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"]
Run Code Online (Sandbox Code Playgroud)
我使用的是Redis 2.4.5,节点redis客户端来自https://github.com/mranney/node_redis
您的猜测是正确的,它与节点的异步性质有关.我将在这里尝试一个简单的例子:
for(var i = 0; i < 10; i++) {
someAsyncFunction(i, function(err, data) {
console.log("executed function for", i);
});
}
Run Code Online (Sandbox Code Playgroud)
在这里,i您将第一次引用它(作为参数someAsyncFunction).在该函数的回调中,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
}
Run Code Online (Sandbox Code Playgroud)
现在,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);
}
Run Code Online (Sandbox Code Playgroud)
请注意,我们executeTheFunction不接受回调.这意味着我们无法真正控制执行 - 所有调用都将立即执行,如果有很多调用,这可能不是我们想要的.在这种情况下,我推荐使用异步模块,这使得这个东西变得简单.
更新:这是一个例子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");
});
Run Code Online (Sandbox Code Playgroud)
现在,你可以轻松管理这类东西async.queue等.
| 归档时间: |
|
| 查看次数: |
2086 次 |
| 最近记录: |