2017-06-09 14 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; 
} 

このコードは、基本的にMicrosoftのコード例hereからコピーされています。

スローされたエラーが正しい型である:

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

しかし、私は取得キューからメッセージを取得しよう:

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

私の最初の考えはキューが持っていないかもしれないということでした正しいアクセス権が設定されていますが、ネットワークサービスユーザーに両方のキューへのメッセージの読み取りと書き込みに必要なすべての権限があることを二重チェックしました。

このコードは、数ヶ月間完全に生産されており、過去に多くの中毒メッセージから生き延びてきました。この問題を引き起こした可能性のある情報は、非常に高く評価されています。

+0

を、これはしばらくの間はprodで作業されている場合は、あなたが言うように、私は設定ではなく、コードを見ていると思います。 environment.machinenameまたはappsettings.queuenameが変更されている可能性はありますか?何らかの種類のパッチ、または誤った構成の展開? – GregHNZ

+0

@GregHNZあなたのご意見ありがとうございます。 configは最初にチェックしたものの1つで、キュー名とマシン名の両方が変更されていません。当社のホスティングプロバイダは、問題が発生した日に展開や修正が行われていないと述べています。 – cfj

+0

この問題が発生した後にポイズンメッセージを検出し続けますか?そうでなければ、有害メッセージがキューから実際に消えていることを示唆します(例えば、Time To Be Receivedが期限切れになったためです)。それを検出し続けると、LookupIdが不良である可能性があります。どちらの方法でも、LookupIdをcatchブロック内のLogger.Debug呼び出しに追加することができます。 –

答えて

1

これは、複数のリトライサイクルが指定されている場合に発生します。 maxRetryCyclesが0より大きく、retryCycleDelayが30秒より大きい場合は、問題の説明が表示されます。メッセージは実際には、サイクル間でretryCycleDelayを待つので、「再試行」と呼ばれるサブキーに座っています。したがって、あなたのIErrorHandlerが "main"キューでそれを探すと、それは見つからないでしょう。何らかの理由で、WCFは各リトライサイクルの最後にMsmqPoisonMessageExceptionをスローします。リトライサイクルの最後に1回だけではありません。これはIErrorHandlerが各サイクルの最後に呼び出されることを意味します。私には本当に奇妙に見えますが、それはそうです。

あなたのコードにMSMQ 4.0があることが保証されていれば、より良いアプローチは、あなたのreceiveErrorHandlingを "Fault"から "Move"に変更してIErrorHandlerを取り除くことです。この方法では、すべての再試行および再試行サイクルが完了した後にメッセージが移動されます。それは "poison"と呼ばれるサブキューに移動されます。

は、詳細についてはこちらをご覧ください:

Poison Message Handling in MSMQ 4.0

関連する問題