2008-09-17 127 views
17

我已創建WCF服務並正在使用netMsmqBinding綁定。如何處理WCF的MSMQ綁定中的消息失敗

這是一個簡單的服務,它將Dto傳遞給我的服務方法,並且不期待響應。該消息被放置在MSMQ中,並且一旦被拾取,就插入到數據庫中。

確保沒有數據丟失的最佳方法是什麼?

我已經試過2種以下方法:

  1. 拋出一個異常

    這將郵件放在手動審閱死信隊列。當我開始strvice

  2. 集上的綁定

    3之後嘗試的receiveRetryCount =「3」我可以處理這一點 - 這instantanously發生,這似乎留在隊列中的消息,但故障我的服務。重新啓動我的服務將重複此過程。

我非常希望做如下:

嘗試過程中的消息

  • 如果失敗,等待5分鐘,該郵件,然後再試一次。
  • 如果該過程失敗3次,請將消息移至死信隊列。
  • 重新啓動該服務會將所有來自死信隊列的消息推回到隊列中,以便可以對其進行處理。

我可以做到嗎?如果是這樣如何? 你能指點我一些關於如何最好地利用WCF和MSMQ給我的場景的好文章。

任何幫助將不勝感激。謝謝!

一些額外的信息

我使用MSMQ 3.0在Windows XP和Windows Server 2003 不幸的是我不能使用內置的針對MSMQ 4.0和Vista/2008帶毒郵件支持。

回答

9

有沒有在這可能是你的情況很有用SDK的樣本。基本上,它所做的是將一個IErrorHandler實現附加到您的服務,當WCF將消息聲明爲「毒藥」(即所有配置的重試已用盡時)將捕獲該錯誤。該示例所做的是將消息移動到另一個隊列,然後重新啓動與該消息關聯的ServiceHost(因爲在發現毒害消息時它將發生故障)。

這不是一個很漂亮的樣本,但它可能是有用的。但有一些限制:

1-如果您有多個端點與您的服務相關聯(即通過多個隊列暴露),則無法知道有毒郵件到達哪個隊列。如果您只有一個單隊列,這不會是一個問題。我還沒有看到任何官方的解決方法,但我已經試驗了一個可能的替代方案,我在這裏已經記錄:http://winterdom.com/weblog/2008/05/27/NetMSMQAndPoisonMessages.aspx

2-一旦問題消息移動到另一個隊列,它就成爲你的責任,所以一旦超時完成(或將新服務附加到該隊列來處理它),它將由您決定將其移回到處理隊列中。

說實話,在這兩種情況下,你都在尋找一些「手動」的工作,WCF本身並沒有包括它。

我最近一直在做一個不同的項目,我有一個要求明確控制重試發生的頻率,而我目前的解決方案是創建一組重試隊列並在重試隊列和主體之間手動移動消息基於一組計時器和一些啓發式處理隊列,只需使用原始的System.Messaging內容來處理MSMQ隊列。它似乎很好地工作,但如果你這樣做,有幾個陷阱。

14

我覺得跟MSMQ(繳費僅在Vista),你也許可以做到這樣的:第一次呼叫失敗後

<bindings> 
    <netMsmqBinding> 
     <binding name="PosionMessageHandling" 
      receiveRetryCount="3" 
      retryCycleDelay="00:05:00" 
      maxRetryCycles="3" 
      receiveErrorHandling="Move" /> 
    </netMsmqBinding> 
</bindings> 

WCF將立即重試ReceiveRetryCount倍。批次失敗後,郵件將被移動到重試隊列 。 RetryCycleDelay延遲一分鐘後,消息從重試隊列移至端點隊列,並重試批次。這將會重複 MaxRetryCycle時間。如果所有失敗的消息都是根據receiveErrorHandling來處理的,那麼可以移動 (去毒隊列),拒絕,丟棄或錯誤

順便說一句關於WCF和MSMQ的好文本是Progammig WCF書的第9章朱瓦爾·洛

+0

這適用於MSMQ 4.0而不是MSMQ 3.0 – Perhentian 2011-11-16 12:34:19

1

可惜我卡在Windows XP和Windows Server 2003,這樣是不是我的選擇。 - (我將重新澄清,在我的問題,因爲我發現後發現這個解決方案,並意識到我不能使用它)

我發現一個解決方案是設置一個自定義處理程序,將我的消息移動到另一個隊列或毒隊列並重新啓動我的服務。 這對我來說似乎很瘋狂。想象一下,我的Sql Server停止了服務重啓的頻率。

所以我已經結束了做是允許的線路故障和隊列留言。 我也記錄致命的消息到我的系統日誌記錄服務,這已發生。 一旦我們的問題得到解決,我重新啓動服務,所有的消息開始得到處理。

我意識到重新處理此消息或任何其他都會失敗,那麼爲什麼需要移動此消息和其他人到另一個隊列。我也可以停止我的服務,並在所有操作按預期進行時重新開始。

澳苷,你有完美的答案爲MSMQ 4.0,但遺憾的是不適合我

4

如果您使用的SQL服務器,那麼你應該使用分佈式事務,因爲兩者的MSMQ和SQL-Server的支持它。會發生什麼情況是將數據庫寫入TransactionScope塊並僅在成功時調用scope.Complete()。如果失敗,那麼當你的WCF方法返回時,消息將被放回到隊列中再次嘗試。這裏的代碼修剪的版本我用:

[OperationBehavior(TransactionScopeRequired=true, TransactionAutoComplete=true)] 
    public void InsertRecord(RecordType record) 
    { 
     try 
     { 
      using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required)) 
      { 
       SqlConnection InsertConnection = new SqlConnection(ConnectionString); 
       InsertConnection.Open(); 

       // Insert statements go here 

       InsertConnection.Close(); 

       // Vote to commit the transaction if there were no failures 
       scope.Complete(); 
      } 
     } 
     catch (Exception ex) 
     { 
      logger.WarnException(string.Format("Distributed transaction failure for {0}", 
       Transaction.Current.TransactionInformation.DistributedIdentifier.ToString()), 
       ex); 
     } 
    } 

我的排隊大,但已知數量的記錄測試,讓WCF開始大量的線程同時處理很多的(達到16個線程 - 16個消息立即關閉隊列),然後在操作過程中終止進程。當程序重新啓動時,消息從隊列中讀回並進行再次處理,如同沒有任何事情發生一樣,並且在測試結束時數據庫是一致的並且沒有丟失記錄。

分佈式事務管理器具有環境存在,當您創建一個TransactionScope的新實例時,它會自動搜索方法調用範圍內的當前事務 - 應該在WCF彈出消息離開隊列並調用你的方法。

+0

嗨克里斯。我相信通過將您的操作行爲歸因於TransactionScopeRequired = true,您無需將Sql調用包裝在事務處理範圍中,因爲這已經完成了。 這就是說,我不確定你的答案與我的MSMQ問題有何關係。 – WebDude 2008-09-17 13:23:30