2012-09-05 66 views
5

我的設置:node.js的多個實體+蒙戈+原子更新=頭部疼痛

  1. Node.js的
  2. Mongojs
  3. 包含兩個集合一個簡單的數據庫 - 庫存和發票。
  4. 用戶可以同時創建發票。
  5. 發票可能涉及多個庫存項目。

我的問題:

保持庫存的完整性。設想一個場景是兩個用戶提交兩個重疊項目集的發票。

一個天真的(和錯誤的)實施將做到以下幾點:

  1. 對於發票的每一項讀取來自清單收集相應的項目。
  2. 修復庫存物料的數量。
  3. 如果任何項目數量低於零 - 放棄與相關消息的請求給用戶。
  4. 保存庫存項目。
  5. 保存發票。

很明顯,這個實現很糟糕,因爲這兩個用戶的行爲會交錯並相互影響。在典型的阻塞服務器+關係數據庫中,這是通過複雜的鎖定/事務處理方案解決的。

什麼是nodish + mongoish方法來解決這個問題?有沒有node.js平臺爲這些類型提供的工具?

+0

你有看了看MongoDB中,提出了兩個階段提交的方式描述這裏(http://cookbook.mongodb.org/patterns/perform-two-phase-commits/)? – JohnnyHK

回答

5

您可以看看MongoDB的兩階段提交方法,或者您可以完全忘記事務並通過服務總線方法分離您的進程。以亞馬遜爲例 - 他們將允許您提交您的訂單,但他們不會確認,直到他們能夠確保您的庫存物品,爲您的卡收取費用等等。這些都不會發生在單筆交易中 - 這是一個一系列可以單獨出現的步驟,並且可以在必要時應用補償步驟。

一個天真的總線實施將執行以下操作(請記住,這只是一個普通的建議供您工作,確切的實施將取決於您的併發的特殊需求等):

  1. 將訂單放置在隊列中。此時,您可以繼續讓您的客戶等待,或者您可以感謝他們的 訂單,並讓他們知道在處理完 之後他們將收到一封電子郵件。
  2. 「庫存員工」將獲取訂單並鎖定其需要預留的庫存項目 。這可以通過許多不同的方式完成。使用Mongo,您可以創建一個每個orderid具有一個文檔的集合。這個文件的ID應該是庫存項目ID和一個合理的TTL(例如30秒)。只要工作人員擁有鎖定,它就可以管理其鎖定項目的庫存水平。一旦其 進行了更改,它可以刪除「鎖定」文檔。
  3. 如果另一名工人走來想要同時其鎖定到管理同一項目 ,你可以把阻塞的工作進入休眠模式 X秒,然後重試,或者更好的,你可以把 要求放回消息總線稍後將被另一個 工作人員拾起。
  4. 一旦工作人員解決了所有的庫存物品,它便可以在服務總線上放置另一條消息,表明應該收取一張卡 或者處理應該收到通知到 拉出庫存,或者一封電子郵件可以是發送給誰做 順序,

聽起來很複雜的人等等等等,但一旦你有一個消息總線設置,其實際上比較簡單。 A list of Node Message Bus Implementations can be found here.

一些開發人員甚至會完全跳過正式的消息總線,並使用數據庫作爲消息傳遞引擎,它可以在簡單的實現中工作。 Google Mongo和Queues。

如果您不希望超過1個服務器,並且消息總線實現過於龐大,那麼節點可以爲您處理鎖定和消息傳遞。例如,如果您確實想要鎖定節點,則可以創建一個存儲庫存項目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; 

      }); 
     }; 
+0

非常感謝,看起來不錯。 – mark

+0

有些東西我不明白 - 如果在您的示例中發生錯誤,可以做些什麼?放置「對錯誤做些什麼」評論的部分。我現在想堅持最簡單的事情 - 一臺服務器和一個請求返回結果,無論好壞,但沒有長時間運行的工作。服務器是單個節點進程,那麼庫存工作人員的含義是什麼? – mark