如何在节点中每个用户同步执行?

时间:2019-08-22 10:04:06

标签: node.js fifo task-queue event-loop

我对Node.js很陌生。 我正在基于express编写节点服务器。 我的服务器收到http请求,请求需要由第三方处理。此过程可能需要几秒钟。

由于节点的非阻塞I / O特性,我无法保证任何处理顺序,而我需要对每个用户/客户端强加顺序处理,这意味着我无法开始处理某个用户的请求,直到先前的请求该用户完成。 具体而言,该解决方案要求:

  • 请求将按用户同步/顺序处理
  • 使用状态层来确保服务器重启时不会丢失任何请求
  • 非阻塞-事件循环应继续处理新请求。

enter image description here


我想出的解决方案是对每个用户使用fifo队列:

enter image description here

“任务”实际上是将请求发送给第三方进行进一步处理。

我正在想象一个user.id->队列的映射,我有几个问题:

  1. 这是nodejs中的常见/有效做法吗?
  2. 同一节点进程是否有可能同时充当生产者(插入队列)和消费者(我知道这不是最佳实践,但仍然是)?
  3. 我遍历了很多队列:BullRSMQconcurrent-queuemongodb-queue等。我需要我的任务是持久性的,以便它们不会丢失,以防服务器崩溃。我想就我的要求的首选队列和最佳实践获得建议。
  4. 在nodejs中是否有其他/更好的方法来满足我的要求?

更新 这是一个示例:

想象一下两种类型的请求-存款和全部提款,每种请求都需要花费几秒钟的时间来执行。用户可以在每个存款请求中存入不同的金额,而“全部提取”只是在用户帐户中提取当前金额。 如果用户执行以下操作:。

  • 存款30
  • 存款40
  • 存款30
  • 全部提款

调用全部提款时,应该总共返回100。 为了提供正确的响应,只有在前一个请求完全完成后才能处理每个请求。 如果第三次存款需要5秒钟,而“全提”(在之后执行)需要1秒钟,那么如果我们不等到存款完成后,用户很有可能会得到70而不是100。为了避免这种情况,我需要依次执行请求,并且还要等一个完成后再执行下一个。

1 个答案:

答案 0 :(得分:0)

好的。因此,我将在这里尝试尽可能直接,因为这确实是一个常见的体系结构问题。简而言之,如果我理解正确,您的问题是:

天生是节点异步的,我该如何处理竞赛条件?

任何人都可以解决吗?

您仍然可以想到什么是正常的解决方案,推荐是这种情况:

  • Mutex(信号量N-1)
  • 信号量
  • 强制同步(通过使用队列和function / Event.emitter执行数组)

每种模式都试图实现与保持数据完整性相同的目的。由于技术和服务种类繁多,因此不可能推荐或指示实现神圣 圣杯解决方案的正确方法(我个人对此表示反对)。

我可以提供的是如何考虑到简单的银行存款/提款方案来解决该问题。

[UPDATE,ERROR,UPDATE ..]

我将使用带有锁定(乐观)模式的快速访问/写入数据库,例如dynamodb: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBMapper.OptimisticLocking.html并重试该操作,直到成功。

    const update = (params)=>{
    //considering all checks and sanitazions and every security measure
     var params = {      //data          };
     var documentClient = new AWS.DynamoDB.DocumentClient();
     documentClient.update(params, function(err, data) {
        if (err) {
           console.log(err);
           return update(params); //recursive, prone to errors ofc //retries a must
        } else {
           console.log(data);
           return data
        }
        });   
    }

     //express
    const route.get('/operation', async (req,res)=>{
            update(req.body)
        });

Correct way to use DynamoDB Optimistic Locking(JAVA)

更细粒度

[更新,队列,发出执行器,推送客户端]

或者我可以通过手动将数据库行的状态编辑为'LOCKED'并将其作为操作添加到另一个表中并向事件发送信号来管理Node中的事件。每次完成执行更新功能后,发射器都会发出信号订阅了上传值(websockets):

//orm way
const checkQueue= async ()=>{

const {id} = await Account.findAll({order:'DESC', limit:1})

Account.update({amount: amount + newTransaction}, {where:{id})
.then((success)=>{
operation.emit('TRANSACTIONS_QUEUE'); //would call checkQueue again
res.json({state...})
//we would have previous set a function to check the Operation table..

// Websockets...
ws.on('ACCOUNT_UPDATED',()=>
ws.send({amount:X})

}

router.post('/operation',(req,res)=>{
//considering all checks and sanitazions and every security measure
const {accountId,newTransaction} = req.body;

Account.findById(accountId)
     .then(({status,amount})=>status)
     .then(status => {
    if (status === 'LOCKED'){
    return Operation.create(amount)
    .then(created => res.json ({state: 'PENDING' ...});
    }

return checkQueue();

});

哑巴

[清除?,清除?,更新]

或者我可以等到所有任务都由LOCK完成(如果有的话)。(这是一种愚蠢的方法,因为它阻止了一切哈哈哈)

对不起,我懒于编写更多的伪代码...

每种方法都有其自身的含义和挑战。

  • 每秒多少个并发请求?
  • 系统的可扩展性。
  • 如果操作多次失败,它的安全性如何。 (死信)

您还可以使用RabitMQ,SQS,Redis(QUEUING)之类的解决方案一次执行一项操作,而这需要承担大部分Node职责。

总而言之,我的意思是:

TL; DR有许多解决方法。还有很多服务。

但是通过了解主要问题是什么。希望我对解决方案有所了解。

好运:)

更多信息: https://thecodebarbarian.com/mutual-exclusion-patterns-with-node-promises