2017-06-09 57 views
1

已經列出MSMQ隊列中有毒郵件的一個奇怪問題。當檢測到中毒消息時,我使用下面的代碼來處理異常並將消息移動到毒性隊列中,但是由於即使從拋出的異常中獲取其lookupId,也找不到消息。請參閱以下相關代碼。未在隊列中找到MSMQ毒訊

public bool HandleError(Exception error) 
{ 
    var poisonException = error as MsmqPoisonMessageException; 
    if (null == poisonException) return false; 

    var lookupId = poisonException.MessageLookupId; 

    var queuePath = Environment.MachineName + "\\" + ConfigurationManager.AppSettings["QueuePath"]; 
    var poisonQueuePath = Environment.MachineName + "\\" + ConfigurationManager.AppSettings["PoisonQueuePath"]; 

    var orderQueue = new System.Messaging.MessageQueue(queuePath); 
    var poisonMessageQueue = new System.Messaging.MessageQueue(poisonQueuePath); 

    // Use a new transaction scope to remove the message from the main queue and add it to the poison queue. 
    using (var txScope = new TransactionScope(TransactionScopeOption.RequiresNew)) 
    { 
     int retryCount = 0; 
     while (retryCount < 3) 
     { 
      retryCount++; 

      try 
      { 
       // Try to get the poison message using the look up id. This line throws InvalidOperationException 
       var message = orderQueue.ReceiveByLookupId(lookupId); 
       // Send the message to the poison message queue. 
       poisonMessageQueue.Send(message, System.Messaging.MessageQueueTransactionType.Automatic); 

       txScope.Complete(); 

       Logger.Debug("Moved poisoned message with look up id: " + lookupId + " to poison queue: " + ConfigurationManager.AppSettings["PoisonQueuePath"]); 
       break; 
      } 
      catch (InvalidOperationException e) 
      { 
       if (retryCount < 3) 
       { 
        Logger.Debug("Trying to move message to poison queue but message is not available, sleeping for 10 seconds before retrying", e); 
        Thread.Sleep(TimeSpan.FromSeconds(10)); 
       } 
       else 
       { 
        Logger.Debug("Giving up on trying to move the message", e); 
       } 
      } 
     } 
    } 

    Logger.Info("Restarting the service to process rest of the messages in the queue"); 
    WaitCallback restartCallback = new WaitCallback(Start); 
    ThreadPool.QueueUserWorkItem(restartCallback); 

    return true; 
} 

此代碼基本上從微軟的示例代碼here複製。

拋出的錯誤是正確的類型:

System.ServiceModel.MsmqPoisonMessageException: The transport channel detected a poison message. 

但試圖從隊列中得到的消息時,我得到:

System.InvalidOperationException: Message requested was not found in the queue specified. 

我首先想到的是,隊列可能沒有正確的權限設置,但我已經仔細檢查了網絡服務用戶具有讀寫消息到這兩個隊列的所有必要權限。

值得一提的是,這些代碼在生產中已經完美運行了數月,並且在過去許多有毒的消息中倖存下來。任何可能導致此問題的輸入都將不勝感激。

+0

如果已經在督促工作了一段時間,就像你說的,我會看配置,而不是代碼。有沒有可能改變environment.machinename或appsettings.queuename?某種修補程序或錯誤配置的部署? – GregHNZ

+0

@GregHNZ感謝您的輸入。配置是我檢查的第一件事情之一,隊列名稱和機器名稱都未更改。我們的託管服務提供商說,在我們開始發生問題的那一天沒有部署或補丁發生。 – cfj

+0

發生此問題後,您是否繼續檢測到有毒消息?如果不是這樣,那麼毒害消息確實已經從隊列中消失了(例如,因爲要接收的時間已過期?)如果您確實繼續檢測到它,則表明LookupId可能不正確。無論採用哪種方式,您都可以將LookupId添加到您的catch塊內的Logger.Debug調用中。 –

回答

1

當您指定了多個重試周期時,會發生這種情況。如果您的maxRetryCycles大於零,並且您的retryCycleDelay大於30秒,則會看到您描述的問題。該消息實際上坐在名爲「重試」的子隊列中,因爲它在循環之間等待retryCycleDelay。所以當你的IErrorHandler在「主」隊列中查找它時,它不會找到它。出於某種原因,WCF在每個重試周期結束時拋出MsmqPoisonMessageException,而不是在所有重試周期結束時拋出一次。這意味着您的IErrorHandler將在每個週期結束時被調用。對我來說似乎很奇怪,但事實就是這樣。

現在更好的方法是,將receiveErrorHandling從「Fault」更改爲「Move」,然後擺脫IErrorHandler,現在幾天(如果可以保證您的代碼將具有MSMQ 4.0)。採用這種方法後,所有重試和重試周期完成後,消息都會移動。它被移動到一個名爲「毒藥」的子隊列中。

在這裏看到更多的細節:

Poison Message Handling in MSMQ 4.0