Node js中的信号量等价,变量在并发请求中得到修改?

时间:2017-08-18 11:24:19

标签: javascript node.js concurrency

过去一周我面对这个问题,我对此感到困惑。 保持简短而简单的解释问题。

我们有一个内存模型,用于存储预算等值。现在,当调用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现在可以有人帮忙

1 个答案:

答案 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。

不是最有效的方式,但它完成了工作,当性能成为问题时,您总是可以重新访问解决方案。