5

我的设置:

  1. 节点.js
  2. 蒙古人
  3. 一个包含两个集合的简单数据库 - 库存和发票。
  4. 用户可以同时创建发票。
  5. 一张发票可能涉及多个库存项目。

我的问题:

保持库存完整性。想象一个场景,两个用户提交两张带有重叠项目集的发票。

一个天真的(和错误的)实现会执行以下操作:

  1. 对于发票中的每个项目,从库存集合中读取相应的项目。
  2. 固定库存物品的数量。
  3. 如果任何项目数量低于零 - 放弃请求并向用户发送相关消息。
  4. 保存库存项目。
  5. 保存发票。

显然,这种实现方式很糟糕,因为两个用户的行为会相互交错并相互影响。在典型的阻塞服务器 + 关系数据库中,这是通过复杂的锁定/事务方案解决的。

解决这个问题的 nodish + mongoish 方法是什么?node.js 平台是否为此类事情提供了任何工具?

4

1 回答 1

5

您可以查看 MongoDB 的两阶段提交方法,或者您可以完全忘记事务并通过服务总线方法解耦您的流程。以亚马逊为例 - 他们将允许您提交订单,但在他们能够保护您的库存物品、从您的卡中扣款等之前,他们不会确认。这一切都不会发生在单笔交易中 - 这是一个可以单独发生的一系列步骤,并且可以在必要时应用补偿步骤。

一个简单的总线实现会执行以下操作(请记住,这只是一个通用的建议,供您使用,具体的实现将取决于您对并发的特定需求等):

  1. 将订单放在队列中。此时,您可以继续让您的客户等待,或者您可以感谢他们的订单,并让他们知道他们会在处理完成后收到一封电子邮件。
  2. “库存工作人员”将抓取订单并锁定需要保留的库存项目。这可以通过许多不同的方式来完成。使用 Mongo,您可以创建一个集合,其中每个 orderid 都有一个文档。该文档的 ID 是库存项目 ID 和一个合理的 TTL(比如 30 秒)。只要工人有锁,它就可以管理它有锁的物品的库存水平。一旦进行了更改,它就可以删除“锁定”文档。
  3. 如果另一个工作人员出现想要在锁定时管理相同的项目,您可以将被阻止的工作人员置于睡眠模式 X 秒,然后重试,或者更好的是,您可以将请求放回消息总线上以被拾取后来由另一名工人。
  4. 一旦工作人员解决了所有库存项目,它就可以在服务总线上放置另一条消息,指示应该对卡进行收费,或者处理应该收到通知以提取库存,或者可以向制作人发送电子邮件订单等

听起来很复杂,但是一旦您设置了消息总线,它实际上就相对简单了。 可以在此处找到节点消息总线实现的列表。

一些开发人员甚至会完全跳过正式的消息总线并使用数据库作为他们的消息传递引擎,它可以在简单的实现中工作。谷歌 Mongo 和队列。

如果您不希望超过 1 个服务器并且消息总线实现过于庞大,节点可以为您处理锁定和消息传递。例如,如果您真的想用节点锁定,您可以创建一个存储库存项目 ID 的数组。虽然,坦率地说,我认为消息总线是最好的方式。不管怎样,这里有一些我过去用来处理简单的外部资源锁定的代码。

// attempt to take out a lock, if the lock exists, then place the callback into the array.
this.getLock = function( id, cb ) {

        if(locks[id] ) {
            locks[id].push( cb );
            return false;
        }
        else {
            locks[id] = [];
            return true;
        }
    };

// call freelock when done
 this.freeLock = function( that, id ) {
            async.forEach(locks[id], function(item, callback) {
                item.apply( that,[id]);
                callback();
            }, function(err){
                if(err) {
                  // do something on error
                }

                locks[id] = null;

            });
        };
于 2012-09-05T04:02:40.403 回答