Node js中的信号量等效项,在并发请求中修改了变量?

INF*_*SYS 3 javascript concurrency node.js

过去1周,我一直面对这个问题,对此我感到困惑。简明扼要地解释问题。

我们有一个内存模型,用于存储预算等值,现在调用API时会产生花费。

然后,我们检查内存中的模型并将支出增加到现有支出中,然后检查预算,如果超出预算,我们将不再接受该模型的任何点击。对于每个调用,我们还将对数据库进行udpate,但这是一个异步操作。

一个简短的例子

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. 
}
Run Code Online (Sandbox Code Playgroud)

过去效果不错,但现在对于并发请求,我们得到的虚假支出超出了预算,超出了预算,并且在一段时间后停止。我们用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);
                    }
                })
            });
        }
    });
Run Code Online (Sandbox Code Playgroud)

代码的GIST现在任何人都可以提供帮助

Sam*_*Toh 6

问:是否存在semaphore或等效的NodeJs?

答: 不可以。

问:那么NodeJs用户如何处理比赛条件?

答:从理论上讲,你不应该因为没有thread在javascript。

在深入研究我提出的解决方案之前,我认为对于您来说了解NodeJs工作原理很重要。

因为NodeJs它是由基于事件的体系结构驱动的。这意味着在此Node过程中,将有一个包含所有“待办事项”事件的事件队列。

当event得到pop从队列中,node将执行所有,直到它完成了所需的代码。async运行期间发出的所有呼叫都会与其他呼叫一样产生,events并在队列中排队,event queue直到听到响应并可以再次运行它们为止。

问:那么我该怎么做才能确保一次只能updates对数据库执行一个请求?

答:我相信可以通过多种方法来实现这一目标,但是更简单的方法之一就是使用set_timeoutAPI。

例:

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
}
Run Code Online (Sandbox Code Playgroud)

注意:我在这里得到的也是伪代码,因此您可能需要对其进行一些调整。

这里的想法是在模型中有一个标志,以指示a是否HTTP request可以继续执行关键代码路径。在这种情况下,您的checkbudget功能将超越其他。

当请求进入时,检查is_updating标志以查看其是否可以继续。如果是,true则它安排一个事件,将在一秒钟后触发,此“ setTimeout”基本上变成一个事件,并放入node的事件队列中,以供以后处理

稍后触发此事件时,将再次进行检查。这种情况一直持续到is_update标志变为false然后请求继续执行它的工作为止,并is_update在所有关键代码完成后再次设置为false。

这不是最有效的方法,但是它可以完成工作,当性能出现问题时,您始终可以重新访问该解决方案。

  • @SamuelToh 好吧,如果 NodeJs 将请求放入队列。那么很明显只有1个请求在更新数据库吗?为什么要使用setTimeout? (2认同)