过去一周我面对这个问题,我对此感到困惑。 保持简短而简单的解释问题。
我们有一个内存模型,用于存储预算等值。现在,当调用API时,它会花费与之关联的费用。
然后我们检查内存模型并将花费添加到现有支出中,然后检查预算,如果超出预算,我们就不再接受该模型的点击次数。对于每个调用,我们也会对数据库进行操作,但这是异步操作。
一个简短的例子
api.get('/clk/:spent/:id', function(req, res) {
checkbudget(spent, id);
}
checkbudget(spent, id){
var obj = in memory model[id]
obj.spent+= spent;
obj.spent > obj.budjet // if greater.
obj.status = 11 // 11 is the stopped status
update db and rebuild model.
}
这曾经工作正常,但现在有了并发请求,我们得到的假花费增加超过预算,并在一段时间后停止。我们用j meter模拟了这个电话,发现了这一点。
据我们所知,节点是异步的,所以当状态更新为11时,许多线程已经更新了广告系列的花费。
如何为Node.js提供信号量逻辑,以便变量预算与模型同步
更新
db.addSpend(campaignId, spent, function(err, data) {
campaign.spent += spent;
var totalSpent = (+camp.spent) + (+camp.cpb);
if (totalSpent > camp.budget) {
logger.info('Stopping it..');
camp.status = 11; // in-memory stop
var History = [];
History.push(some data);
db.stopCamp(campId, function(err, data) {
if (err) {
logger.error('Error while stopping );
}
model.campMAP = buildCatMap(model);
model.campKeyMap = buildKeyMap(model);
db.campEventHistory(cpcHistory, false, function(err) {
if (err) {
logger.error(Error);
}
})
});
}
});
代码的GIST现在可以有人帮忙
答案 0 :(得分:3)
问: semaphore
中是NodeJs
还是等效?
答: 否。
问:那么NodeJs
用户如何应对竞争条件?
答:理论上你不应该因为thread
中没有javascript
。
在深入研究我提出的解决方案之前,我认为了解NodeJs
的工作原理非常重要。
对于NodeJs
,它由基于事件的体系结构驱动。这意味着在Node
进程中有一个包含所有“待办事项”事件的事件队列。
当event
从队列中获得pop
时,node
将执行所需代码的全部,直到完成为止。在运行期间进行的任何async
次调用都会以其他events
生成,并且它们会在event queue
中排队,直到听到回复为止,是时候再次运行它们了。 / p>
问:那么我该怎样做才能确保一次只有1个请求可以对数据库执行updates
?
答:我相信有很多方法可以实现这一目标,但其中一个更简单的方法就是使用set_timeout
API。
示例:强>
api.get('/clk/:spent/:id', function(req, res) {
var data = {
id: id
spending: spent
}
canProceed(data, /*functions to exec after canProceed=*/ checkbudget);
}
var canProceed = function(data, next) {
var model = in memory model[id];
if (model.is_updating) {
set_timeout(isUpdating(data, next), /*try again in=*/1000/*milliseconds*/);
}
else {
// lock is released. Proceed.
next(data.spending, data.id)
}
}
checkbudget(spent, id){
var obj = in memory model[id]
obj.is_updating = true; // Lock this model
obj.spent+= spent;
obj.spent > obj.budjet // if greater.
obj.status = 11 // 11 is the stopped status
update db and rebuild model.
obj.is_updating = false; // Unlock the model
}
注意:我在这里得到的是伪代码,所以你可能需要调整一下。
这里的想法是在模型中有一个标志,指示HTTP request
是否可以继续执行关键代码路径。在这种情况下,您的checkbudget
功能及其他功能。
当请求进入时,它会检查is_updating
标志以查看它是否可以继续。如果它是true
,那么它会调度一个事件,稍后会被触发,这个“setTimeout”基本上会成为一个事件并被放入node
的事件队列中以供以后处理
当此事件稍后被触发时,再次进行检查。这种情况会发生,直到is_update
标志变为false
,然后请求继续执行其内容,并且当所有关键代码完成后,is_update
再次设置为false。
不是最有效的方式,但它完成了工作,当性能成为问题时,您总是可以重新访问解决方案。