有害メッセージ処理

有害メッセージとは、アプリケーションへの配信試行の回数が最大値を超えたメッセージのことです。この状況は、キュー ベースのアプリケーションがエラーによってメッセージを処理できないときに発生する可能性があります。信頼性に対する要求を満たすために、キューに置かれたアプリケーションはトランザクションの下でメッセージを受信します。キューに置かれたメッセージを受信したトランザクションを中止すると、メッセージはそのままキューに残り、新しいトランザクションの下で再試行されます。トランザクションを中止させた問題が解決されなければ、受信側のアプリケーションは、配信試行回数の最大値を超えるまで同じメッセージの受信と中止を繰り返す悪循環に陥る可能性があり、その結果、有害メッセージが生じることになります。

メッセージは、さまざまな理由で有害メッセージになる可能性があります。最も一般的な理由は、アプリケーション固有の理由です。たとえば、アプリケーションがキューからメッセージを読み取り、なんらかのデータベース処理を実行する場合は、アプリケーションがデータベースをロックできないことによって、トランザクションが中止されることが考えられます。データベース トランザクションが中止されたために、メッセージはキューに残ります。これにより、アプリケーションではメッセージを再度読み取り、データベースのロックの取得を再試行します。メッセージに無効な情報が含まれている場合にも、有害メッセージになる可能性があります。たとえば、発注書に無効な顧客番号が含まれている場合があります。このような場合、アプリケーションは自発的にトランザクションを中止し、メッセージを強制的に有害メッセージにすることがあります。

まれに、メッセージをアプリケーションにディスパッチできないことがあります。メッセージに不適切なフレームがあったり、無効なメッセージ資格情報が割り当てられていたり、無効なアクション ヘッダーが含まれていたりする場合などは、Windows Communication Foundation (WCF) レイヤーがメッセージの問題を検出することがあります。このような場合、アプリケーションはメッセージを受信しません。ただし、メッセージは有害メッセージとなるため、手動で処理できます。

有害メッセージの処理

WCF の有害メッセージ処理は、アプリケーションにディスパッチできないメッセージや、アプリケーションにディスパッチされても、アプリケーション固有の理由によって処理できないメッセージを、受信側アプリケーションで処理する機構を提供します。有害メッセージ処理は、キューに置かれた使用可能な各バインディングの次のプロパティで構成されます。

  • ReceiveRetryCount。アプリケーション キューからアプリケーションへのメッセージの配信を再試行する最大回数を示す整数値。既定値は 5 です。データベースの一時的なデッドロックなどの問題を即時再試行で解決する場合は、この値で十分です。

  • MaxRetryCycles。再試行サイクルの最大数を示す整数値。再試行サイクルは、アプリケーション キューから再試行サブキューにメッセージを転送し、構成可能な遅延の後に再試行サブキューからアプリケーション キューにメッセージを転送して配信を再試行するプロセスで構成されます。既定値は 2 です。Windows Vista では、メッセージの配信試行が最大で (ReceiveRetryCount + 1) × (MaxRetryCycles + 1) 回実行されます。Windows Server 2003 と Windows XP では、MaxRetryCycles は無視されます。

  • RetryCycleDelay。再試行サイクル間の遅延時間。既定値は 30 分です。MaxRetryCyclesRetryCycleDelay の組み合わせにより、問題に対処する機構が提供されます。この場合、定期的な遅延後に再試行が行われ、問題が解決されます。たとえば、SQL Server の保留中のトランザクションのコミットでロックされた行セットが処理されます。

  • ReceiveErrorHandling。再試行を最大回数実行しても配信できなかったメッセージに対して実行するアクションを示す列挙値。値には、Fault、Drop、Reject、および Move の 4 つがあります。既定のオプションは Fault です。

  • Fault : このオプションでは、ServiceHost のエラーの原因となったリスナーにエラーが送信されます。アプリケーションでキューのメッセージの処理を継続するには、なんらかの外部機構によってアプリケーション キューからメッセージを削除する必要があります。

  • Drop : このオプションでは、有害メッセージが破棄されます。メッセージがアプリケーションに配信されることはありません。この時点でメッセージの TimeToLive プロパティの有効期限が既に切れている場合は、メッセージが送信側の配信不能キューに表示されることがあります。有効期限が切れていない場合は、どこにも表示されません。このオプションは、メッセージが失われたときにユーザーが実行する操作が指定されていないことを示します。

  • Reject : このオプションは、Windows Vista 専用です。アプリケーションがメッセージを受信できないという否定受信確認を送信側キュー マネージャーに返信するように、メッセージ キュー (MSMQ) に指示します。メッセージは、送信側キュー マネージャーの配信不能キューに置かれます。

  • Move : このオプションは、Windows Vista 専用です。有害メッセージを有害メッセージ キューに移動して、後で有害メッセージ処理アプリケーションで処理できるようにします。有害メッセージ キューは、アプリケーション キューのサブキューです。有害メッセージ処理アプリケーションを、有害キューからメッセージを読み取る WCF サービスにすることができます。有害キューはアプリケーション キューのサブキューであり、net.msmq://<machine-name>/applicationQueue;poison とアドレス指定できます。ここで machine-name はキューが存在するコンピューターの名前であり、applicationQueue はアプリケーション固有のキューの名前です。

メッセージに対して実行される配信の最大試行回数は、次のようになります。

  • Windows Vista の場合、((ReceiveRetryCount + 1) × (MaxRetryCycles + 1))。

  • Windows Server 2003 および Windows XP の場合、(ReceiveRetryCount + 1)。

ms789028.note(ja-jp,VS.100).gif注 :
正常に配信されたメッセージについては、再試行は行われません。

メッセージの読み取りが試行された回数を追跡するために、Windows Vista では、中止回数をカウントする永続的なメッセージ プロパティと、アプリケーション キューとサブキューの間でメッセージが移動された回数をカウントする移動回数プロパティを保持します。WCF チャネルはこれらを使用して、受信再試行回数と再試行サイクル数を計算します。Windows Server 2003 と Windows XP では、中止回数が WCF チャネルによってメモリに保持され、アプリケーションでエラーが発生した場合にリセットされます。また、WCF チャネルはいつでも、最大 256 のメッセージの中止回数をメモリに保持できます。257 番目のメッセージを読み取ると、最も古いメッセージの中止回数がリセットされます。

中止回数と移動回数のプロパティは、操作コンテキストを通じてサービス操作で使用できます。これらのプロパティにアクセスする方法を次のコード例に示します。

MsmqMessageProperty mqProp = OperationContext.Current.IncomingMessageProperties[MsmqMessageProperty.Name] as MsmqMessageProperty;
Console.WriteLine("Abort count: {0} ", mqProp.AbortCount);
Console.WriteLine("Move count: {0} ", mqProp.MoveCount);
// code to submit purchase order ...

WCF は、標準の、キューに置かれたバインディングを 2 つ提供します。

  • NetMsmqBinding。他の WCF エンドポイントとのキュー ベースの通信を実行するのに適した .NET Framework バインディング。

  • MsmqIntegrationBinding。既存のメッセージ キュー アプリケーションとの通信に適したバインディング。

ms789028.note(ja-jp,VS.100).gif注 :
これらのバインディングでは、WCF サービスの要件に基づいてプロパティを変更できます。有害メッセージ処理機構全体は、受信側アプリケーションに対してローカルです。このプロセスは、受信側アプリケーションが最終的に処理を停止し、送信側に否定受信確認を返信する場合を除き、送信元アプリケーションには認識されません。この場合、メッセージは、送信元の配信不能キューに送られます。

ベスト プラクティス : MsmqPoisonMessageException の処理

メッセージが有害であるとサービスが判断した場合、キューに置かれたトランスポートは MsmqPoisonMessageException をスローします。この例外には、有害メッセージの LookupId が含まれます。

受信側アプリケーションでは、必要な IErrorHandler インターフェイスを実装して、すべてのエラーを処理できます。詳細については、次のトピックを参照してください。、「エラー処理およびレポートに対する制御の拡張」を参照してください。

アプリケーションでは、有害メッセージを有害メッセージ キューに移動し、サービスがキュー内の残りのメッセージにアクセスできるようにする、なんらかの有害メッセージ自動処理を必要とする場合があります。エラー処理機構を使用して有害メッセージの例外をリッスンする唯一のシナリオは、ReceiveErrorHandling の設定を Fault に設定した場合です。メッセージ キュー 3.0 の有害メッセージ サンプルは、この動作を示しています。ベスト プラクティスを含め、有害メッセージを処理するために必要な手順の概要を以下に示します。

  1. 有害メッセージの設定がアプリケーションの要件を反映していることを確認します。設定を処理するときは、Windows Vista、Windows Server 2003、および Windows XP でのメッセージ キューの機能の相違点を理解してください。

  2. 必要に応じて IErrorHandler を実装し、有害メッセージのエラーを処理します。ReceiveErrorHandlingFault に設定する場合、有害メッセージをキューから取り除いたり、外部の従属的な問題を解決したりする手動の機構が必要となります。そのため、ReceiveErrorHandlingFault に設定するときは、次のコードに示すように IErrorHandler を実装するのが一般的です。

    class PoisonErrorHandler : IErrorHandler
    {
        public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
        {
            // No-op -We are not interested in this. This is only useful if you want to send back a fault on the wirenot applicable for queues [one-way].
        }
    
        public bool HandleError(Exception error)
        {
            if (error != null && error.GetType() == typeof(MsmqPoisonMessageException))
            {
                Console.WriteLine(" Poisoned message -message look up id = {0}", ((MsmqPoisonMessageException)error).MessageLookupId);
                return true;
            }
    
            return false;
        }
    }
    
  3. サービス動作が使用できる PoisonBehaviorAttribute を作成します。この動作により、IErrorHandler がディスパッチャーにインストールされます。このコード例を次に示します。

    public class PoisonErrorBehaviorAttribute : Attribute, IServiceBehavior
    {
        Type errorHandlerType;
    
        public PoisonErrorBehaviorAttribute(Type errorHandlerType)
        {
            this.errorHandlerType = errorHandlerType;
        }
    
        void IServiceBehavior.Validate(ServiceDescription description, ServiceHostBase serviceHostBase)
        {
        }
    
        void IServiceBehavior.AddBindingParameters(ServiceDescription description, ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, BindingParameterCollection parameters)
        {
        }
    
        void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription description, ServiceHostBase serviceHostBase)
        {
            IErrorHandler errorHandler;
    
            try
            {
                errorHandler = (IErrorHandler)Activator.CreateInstance(errorHandlerType);
            }
            catch (MissingMethodException e)
            {
                throw new ArgumentException("The errorHandlerType specified in the PoisonErrorBehaviorAttribute constructor must have a public empty constructor", e);
            }
            catch (InvalidCastException e)
            {
                throw new ArgumentException("The errorHandlerType specified in the PoisonErrorBehaviorAttribute constructor must implement System.ServiceModel.Dispatcher.IErrorHandler", e);
            }
    
            foreach (ChannelDispatcherBase channelDispatcherBase in serviceHostBase.ChannelDispatchers)
            {
                ChannelDispatcher channelDispatcher = channelDispatcherBase as ChannelDispatcher;
                channelDispatcher.ErrorHandlers.Add(errorHandler);
            }
        }
    }
    
  4. サービスに有害動作属性による注釈が付けられていることを確認します。

    <configuration>
      <appSettings>
        <!-- use appSetting to configure MSMQ queue name -->
        <add key="queueName" value=".\private$\ServiceModelSamplesPoison" />
        <add key="baseAddress" value="https://localhost:8000/orderProcessor/poisonSample"/>
      </appSettings>
      <system.serviceModel>
        <services>
          <service 
                  name="Microsoft.ServiceModel.Samples.OrderProcessorService">
            <!-- Define NetMsmqEndpoint -->
            <endpoint address="net.msmq://localhost/private/ServiceModelSamplesPoison"
                      binding="netMsmqBinding"
                      bindingConfiguration="PoisonBinding" 
                      contract="Microsoft.ServiceModel.Samples.IOrderProcessor" />
          </service>
        </services>
    
        <bindings>
          <netMsmqBinding>
            <binding name="PoisonBinding" 
                     receiveRetryCount="0"
                     maxRetryCycles="1"
                     retryCycleDelay="00:00:05"                      
                     receiveErrorHandling="Fault"
                            />
          </netMsmqBinding>
        </bindings>
      </system.serviceModel>
    </configuration>
    

また、ReceiveErrorHandlingFault に設定した場合は、ServiceHost が有害メッセージを検出するとエラーになります。この場合、faulted イベントにフックしてサービスを終了し、修正措置を講じた後に再起動できます。たとえば、IErrorHandler に伝達された MsmqPoisonMessageException に含まれる LookupId は記録しておくことができます。そのため、サービス ホストでエラーが発生したときに、System.Messaging API を使用して、この LookupId に基づいてキューからメッセージを受信し、そのメッセージをキューから削除して、外部ストアまたは別のキューに格納できます。これで ServiceHost を再起動して、通常の処理を再開できるようになります。Poison Message Handling in MSMQ 3.0 はこの動作を示しています。

トランザクション タイムアウトと有害メッセージ

キューに置かれたトランスポート チャネルとユーザー コードの間では、特定の部類のエラーが発生する可能性があります。これらのエラーは、メッセージ セキュリティ レイヤーやサービス ディスパッチ ロジックなど、中間のレイヤーで検出される場合があります。たとえば、SOAP セキュリティ レイヤーで X.509 証明書がないことが検出された場合やアクションがない場合は、メッセージがアプリケーションにディスパッチされます。この場合、サービス モデルはそのメッセージを破棄します。メッセージはトランザクションで読み取られますが、そのトランザクションの結果は提供されないため、トランザクションが最終的にタイムアウトになって中止され、メッセージはキューに戻されます。つまり、エラーの部類によっては、トランザクションがすぐに中止されるわけではなく、タイムアウトになるまで待機します。サービスのトランザクション タイムアウトは、ServiceBehaviorAttribute を使用して変更できます。

トランザクション タイムアウトをコンピューター全体で変更するには、machine.config ファイルを変更し、適切なトランザクション タイムアウトを設定します。トランザクションに設定されたタイムアウトに従って、トランザクションは最終的に中止され、キューに戻されることになり、中止回数がインクリメントされることに注意してください。最終的に、メッセージは有害メッセージになり、ユーザー設定に従って適切な処理が行われます。

セッションと有害メッセージ

セッションでは、単一のメッセージと同じ再試行および有害メッセージ処理手順が行われます。有害メッセージに対する上記のプロパティは、セッション全体に適用されます。つまり、セッション全体が再試行され、メッセージは最終有害メッセージ キューに送られます。または、メッセージが拒否された場合は、送信側の配信不能キューに送られます。

バッチ処理と有害メッセージ

バッチの一部であるメッセージが有害メッセージになった場合は、バッチ全体がロールバックされ、チャネルはメッセージを 1 つずつ読み取る処理に戻ります。バッチ処理詳細情報、「トランザクションに含まれるメッセージのバッチ処理」を参照してください。

有害キュー内のメッセージに対する有害メッセージ処理

有害メッセージ処理は、メッセージが有害メッセージ キューに配置された時点では終了しません。有害メッセージ キューに置かれたメッセージは、依然として読み取り、処理する必要があります。最終有害サブキューからメッセージを読み取るときは、有害メッセージ処理設定のサブセットを使用できます。適用できる設定は、ReceiveRetryCountReceiveErrorHandling です。ReceiveErrorHandling は、Drop、Reject、または Fault に設定できます。ReceiveErrorHandling が Move に設定されている場合、MaxRetryCycles が無視され、例外がスローされます。

Windows Vista、Windows Server 2003、および Windows XP の相違点

前述のように、Windows Server 2003 と Windows XP には、すべての有害メッセージ処理設定が適用されるわけではありません。Windows Server 2003、Windows XP、および Windows Vista のメッセージ キューには、有害メッセージの処理に関連する次のような重要な違いがあります。

  • Windows Vista のメッセージ キューはサブキューをサポートしていますが、Windows Server 2003 と Windows XP はサポートしていません。サブキューは、有害メッセージ処理で使用されます。再試行キューと有害キューは、有害メッセージ処理の設定に基づいて作成されるアプリケーション キューのサブキューです。作成する再試行サブキューの数は、MaxRetryCycles で指定します。したがって、Windows Server 2003 または Windows XP で実行している場合、MaxRetryCycles は無視されるため、ReceiveErrorHandling.Move は使用できません。

  • Windows Vista のメッセージ キューは否定受信確認をサポートしていますが、Windows Server 2003 と Windows XP はサポートしていません。受信側キュー マネージャーから否定受信確認を受け取ると、送信側キュー マネージャーは拒否されたメッセージを配信不能キューに入れます。そのため、ReceiveErrorHandling.Reject は、Windows Server 2003 と Windows XP では使用できません。

  • Windows Vista のメッセージ キューは、メッセージの配信試行回数を保持するメッセージ プロパティをサポートしています。この中止回数のプロパティは、Windows Server 2003 と Windows XP では使用できません。WCF は中止回数をメモリに保持するため、同じメッセージがファーム内の複数の WCF サービスによって読み取られた場合は、このプロパティに正確な値が格納されないことがあります。

参照

概念

キューの概要
Windows Vista、Windows Server 2003、および Windows XP におけるキュー機能の相違点
コントラクトおよびサービスのエラーの指定と処理