5

私のセットアップ:

  1. Node.js
  2. Mongojs
  3. 在庫と請求書の2つのコレクションを含む単純なデータベース。
  4. ユーザーは同時に請求書を作成できます。
  5. 請求書はいくつかの在庫品目を参照する場合があります。

私の問題:

在庫の整合性を維持します。2人のユーザーが重複するアイテムセットを含む2つの請求書を提出するシナリオを想像してみてください。

ナイーブな(そして間違った)実装は次のことを行います:

  1. 請求書の各アイテムについて、インベントリコレクションからそれぞれのアイテムを読み取ります。
  2. 在庫アイテムの数量を修正します。
  3. アイテムの数量がゼロを下回った場合は、ユーザーへの関連メッセージとともにリクエストを破棄します。
  4. インベントリアイテムを保存します。
  5. 請求書を保存します。

明らかに、この実装は良くありません。2人のユーザーのアクションがインターリーブして相互に影響を与えるためです。典型的なブロッキングサーバー+リレーショナルデータベースでは、これは複雑なロック/トランザクションスキームで解決されます。

これを解決するためのうなずき+モンゴルの方法は何ですか?node.jsプラットフォームがこの種のもののために提供するツールはありますか?

4

1 に答える 1

5

MongoDBを使用した2フェーズコミットアプローチを検討することも、トランザクションを完全に忘れて、サービスバスアプローチを介してプロセスを分離することもできます。例としてAmazonを使用します-注文を送信することはできますが、在庫アイテムを確保したり、カードに請求したりするまで、確認は行われません。これは1回のトランザクションでは発生しません-これは単独で発生する可能性があり、必要に応じて補正ステップを適用できる一連のステップ。

ナイーブなバスの実装は次のことを行います(これは作業を行うための一般的な提案であり、正確な実装は同時実行性などの特定のニーズに依存することに注意してください)。

  1. 注文をキューに入れます。この時点で、引き続きクライアントを待機させるか、注文に感謝して、処理時に電子メールを受信することをクライアントに知らせることができます。
  2. 「在庫作業員」は注文を取得し、予約する必要のある在庫アイテムをロックします。これは、さまざまな方法で実行できます。Mongoを使用すると、orderidごとにドキュメントを持つコレクションを作成できます。このドキュメントのIDには、在庫アイテムIDと妥当なTTL(たとえば、30秒)が含まれます。ワーカーがロックを持っている限り、ロックを持っているアイテムの在庫レベルを管理できます。変更を加えると、「ロック」ドキュメントが削除される可能性があります。
  3. ロックされている間に同じアイテムを管理したい別のワーカーが来た場合は、ブロックされたワーカーをX秒間スリープモードにしてから再試行するか、リクエストをメッセージバスに戻してピックアップすることができます。後で別の労働者によって。
  4. ワーカーがすべての在庫アイテムを解決すると、カードに請求する必要があることを示す別のメッセージをサービスバスに配置するか、処理で在庫をプルする通知を受信するか、作成者に電子メールを送信できます。注文など

複雑に聞こえますが、メッセージバスを設定すると、実際には比較的単純になります。 ノードメッセージバスの実装のリストは、ここにあります。

一部の開発者は、正式なメッセージバスを完全にスキップし、単純な実装で機能するメッセージパッシングエンジンとしてデータベースを使用することさえあります。GoogleMongoとキュー。

複数のサーバーを期待せず、メッセージバスの実装が大きすぎる場合は、ノードがロックとメッセージパッシングを処理できます。たとえば、本当にノードでロックしたい場合は、在庫アイテムIDを格納する配列を作成できます。とはいえ、率直に言って、メッセージバスが最善の方法だと思います。とにかく、Nodeで単純な外部リソースロックを処理するために過去に使用したコードを次に示します。

// 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 に答える