.NET分佈式事務處理總結【下】 - 包含MSMQ的分佈式事務處理

轉自:http://www.cnblogs.com/daxnet/archive/2011/03/15/1984995.htmlhtml

.NET直接提供對MSMQ的訪問支持,只須要添加System.Messaging程序集引用便可方便地操做MSMQ。MSMQ支持兩種事務處理模式:內部事務處理以及基於MS-DTC的分佈式事務處理。數據庫

MSMQ的內部事務處理框架

MSMQ的內部事務處理是指,僅採用MSMQ自己提供的事務處理機制完成事務處理。好比,假設有一系列的消息須要發佈到MSMQ,那麼,就能夠啓動一個內部事務,確保這些消息的發佈過程是一個原子操做。要使用MSMQ的內部事務處理機制,在建立消息隊列的時候,就須要勾選「事務性」選項,以下圖所示:分佈式

image

首先,須要建立一個MessageQueueTransaction的對象,並使用Begin調用以啓動MSMQ的內部事務處理。而後,在MessageQueue的Send方法中,使用Send(object, MessageQueueTransaction)的重載函數發送消息,將建立好的MessageQueueTransaction對象做爲第二個參數傳遞給Send方法;在完成全部消息的發送以後,使用MessageQueueTransaction對象的Commit方法提交事務。若是在發送消息的過程當中遇到問題,則使用MessageQueueTransaction對象的Abort調用回滾事務。請參見下面的示例代碼:函數

隱藏行號 複製代碼 ?MSMQ的內部事務處理
  1. using (MessageQueue messageQueue = new MessageQueue(@".\private$\TPCDemoQueue", 
    
  2.     false, false, QueueAccessMode.SendAndReceive))
    
  3. {
    
  4.     MessageQueueTransaction trans = new MessageQueueTransaction();
    
  5.     try
    
  6.     {
    
  7.         trans.Begin();
    
  8.         for (int i = 0; i < 5; i++)
    
  9.         {
    
  10.             messageQueue.Send(new Message(i), trans);
    
  11.         }
    
  12.         trans.Commit();
    
  13.     }
    
  14.     catch
    
  15.     {
    
  16.         trans.Abort();
    
  17.     }
    
  18.     messageQueue.Close();
    
  19. }
    
  20.  

注意:若是你的消息隊列在建立的時候沒有設置「事務性」選項,那麼,在完成消息隊列的建立之後,你將沒法修改該選項。更糟糕的是,在非事務性隊列上執行上面的代碼,則沒法將消息發佈到消息隊列上,框架自己也不會提示任何錯誤信息,指示消息並未發佈成功。工具

在分佈式事務處理中使用MSMQ性能

在分佈式事務處理的上下文中(好比,.NET 2.0+的TransactionScope中),上面所提到的MessageQueueTransaction將毫無用處,也就是說,MessageQueueTransaction與分佈式事務處理毫無關係。你所要作的是,用正常的方式初始化一個MessageQueue的實例,而後,調用Send方法發佈消息,在發佈消息的時候,經過設置MessageQueueTransactionType的值來告訴MessageQueue,目前正處於一個分佈式事務的上下文中。因而,你須要使用Send/Receive的重載方法:Send(object, MessageQueueTransactionType)以及Receive(MessageQueueTransactionType)。以下:spa

隱藏行號 複製代碼 ?分佈式事務中的MSMQ調用
  1. using (TransactionScope transaction = new TransactionScope())
    
  2. {
    
  3.     Message inputMsg = inputQueue.Receive(MessageQueueTransactionType.Automatic);
    
  4.     // do some work
    
  5.     transaction.Complete();
    
  6. }
    
  7.  

注意:對於一些生命週期相對較長的事務處理,好比,假設你的用例是這樣的:你首先須要從一個消息隊列中得到消息,而後更新你的數據庫記錄,那麼你的代碼可能會是這樣的:3d

隱藏行號 複製代碼 ?分佈式事務中的MSMQ調用
  1. using (TransactionScope transaction = new TransactionScope())
    
  2. {
    
  3.     using (MessageQueue someQueue = new MessageQueue("<queue connection>"))
    
  4.     {
    
  5.         Message msg = someQueue.Receive();
    
  6.         // do something else
    
  7.     }
    
  8.     transaction.Complete();
    
  9. }
    
  10.  

這樣作實際上是不對的!由於Receive方法是一種同步調用,若是消息隊列中根本沒有任何內容,那麼Receive調用就會被阻塞,直到消息隊列中出現新的消息。這就意味着你的分佈式事務一直都是處於開啓的狀態,並且可能因爲等待時間過長而致使超時,最終致使一個MessageQueueException。htm

正確的作法是,在MessageQueue上使用BeginPeek調用(注意:不是BeginReceive方法,由於BeginReceive方法並非用來處理事務性隊列的),而後訂閱PeekComplete事件,在事件處理過程當中,再使用TransactionScope以及Receive等方法實現消息的獲取。例如:

隱藏行號 複製代碼 ?分佈式事務中的MSMQ調用
  1. MessageQueue inputQueue = new MessageQueue("<queue connection>");
    
  2. inputQueue.PeekCompleted += (s, e) =>
    
  3.     {
    
  4.         using (TransactionScope transaction = new TransactionScope())
    
  5.         {
    
  6.             Message inputMsg = inputQueue.Receive(MessageQueueTransactionType.Automatic);
    
  7.             // do some work
    
  8.             transaction.Complete();
    
  9.         }
    
  10.         inputQueue.BeginPeek();
    
  11.     };
    
  12. inputQueue.BeginPeek();
    
  13.  

最後再提醒一下,就是若是你所要作的事情僅限於與MSMQ打交道,那麼只要使用MSMQ的內部事務處理機制就能夠了,畢竟使用分佈式事務處理會涉及到MS-DTC,從而形成過大的系統開銷,影響性能。

相關文章
相關標籤/搜索