次の方法で共有


有害メッセージ処理

有害メッセージは、アプリケーションへの配信試行の最大数を超えたメッセージです。 この状況は、キューベースのアプリケーションがエラーのためにメッセージを処理できない場合に発生する可能性があります。 信頼性の要求を満たすために、キューに登録されたアプリケーションはトランザクションの下でメッセージを受信します。 キューに登録されたメッセージを受信したトランザクションを中止すると、メッセージはキューに残り、新しいトランザクションでメッセージが再試行されます。 トランザクションの中止の原因となった問題が修正されない場合、受信アプリケーションは、配信試行の最大数を超えて有害メッセージが発生するまで、同じメッセージを受信および中止するループでスタックする可能性があります。

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

まれに、メッセージがアプリケーションにディスパッチされない場合があります。 Windows Communication Foundation (WCF) レイヤーでは、メッセージに間違ったフレームがある場合、無効なメッセージ資格情報が添付されている場合、または無効なアクション ヘッダーなど、メッセージに問題が見つかる場合があります。 このような場合、アプリケーションはメッセージを受信しません。ただし、メッセージは引き続き有害メッセージになり、手動で処理される可能性があります。

有害メッセージの処理

WCF では、有害メッセージ処理は、アプリケーションにディスパッチできないメッセージ、またはアプリケーションにディスパッチされるが、アプリケーション固有の理由で処理できないメッセージを受信アプリケーションが処理するためのメカニズムを提供します。 使用可能なキューに登録された各バインディングで、次のプロパティを使用して有害メッセージ処理を構成します。

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

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

  • RetryCycleDelay。 再試行サイクル間の遅延時間。 既定値は 30 分です。 MaxRetryCycles RetryCycleDelayを組み合わせることで、定期的な遅延後の再試行によって問題が修正される問題に対処するメカニズムが提供されます。 たとえば、SQL Server の保留中のトランザクション コミットでロックされた行セットを処理します。

  • ReceiveErrorHandling。 再試行の最大数が試行された後に配信に失敗したメッセージに対して実行するアクションを示す列挙体。 値には、Fault、Drop、Reject、Move を指定できます。 既定のオプションは Fault です。

  • 過ち。 このオプションは、 ServiceHost がエラーの原因となったエラーをリスナーに送信します。 アプリケーションがキューからのメッセージを処理し続ける前に、何らかの外部メカニズムによってメッセージをアプリケーション キューから削除する必要があります。

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

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

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

メッセージに対して行われた配信試行の最大数を次に示します。

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

  • (ReceiveRetryCount + 1) は Windows Server 2003 および Windows XP で使用されます。

正常に配信されたメッセージに対して再試行は行われません。

メッセージの読み取りが試行された回数を追跡するために、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: {mqProp.AbortCount} ");
Console.WriteLine($"Move count: {mqProp.MoveCount} ");
// code to submit purchase order ...

WCF には、次の 2 つの標準のキュー バインディングが用意されています。

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

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

WCF サービスの要件に基づいて、これらのバインドのプロパティを変更できます。 有害メッセージ処理メカニズム全体は、受信側アプリケーションに対してローカルです。 受信側アプリケーションが最終的に停止し、否定受信確認を送信者に送信しない限り、プロセスは送信側アプリケーションからは見えません。 その場合、メッセージは送信者のデッドレターキューに移動されます。

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

メッセージが有害であるとサービスが判断すると、キューに登録されたトランスポートは、有害メッセージのMsmqPoisonMessageExceptionを含むLookupIdをスローします。

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

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

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

  2. 必要に応じて、有害メッセージ エラーを処理する IErrorHandler を実装します。 ReceiveErrorHandlingFaultを設定するには、有害メッセージをキューから移動したり、外部依存の問題を修正したりするための手動メカニズムが必要であるため、次のコードに示すように、IErrorHandlerReceiveErrorHandlingに設定されている場合にFaultを実装するのが一般的です。

    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 wire…not 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 = {((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. サービスに有害動作属性で注釈が付けられていることを確認します。

さらに、 ReceiveErrorHandlingFaultに設定されている場合、有害メッセージが発生したときに ServiceHost エラーが発生します。 障害が発生したイベントに接続し、サービスをシャットダウンし、是正措置を実行して、再起動することができます。 たとえば、LookupId内のMsmqPoisonMessageExceptionIErrorHandlerに伝達され、サービス ホストに障害が発生したときに、System.Messaging API を使用して、LookupIdを使用してキューからメッセージを受信し、メッセージをキューから削除し、メッセージを外部ストアまたは別のキューに格納できます。 その後、 ServiceHost を再起動して、通常の処理を再開できます。 MSMQ 4.0 の有害メッセージ処理は、この動作を示しています。

トランザクション Time-Out と有害メッセージ

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

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

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

セッションでは、単一のメッセージと同じ再試行および有害メッセージ処理手順が行われます。 有害メッセージに関して以前に一覧表示されたプロパティは、セッション全体に適用されます。 つまり、セッション全体が再試行され、メッセージが拒否された場合は最終的な有害メッセージ キューまたは送信者の配信不能キューに送られます。

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

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

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

有害メッセージ処理は、メッセージが有害メッセージ キューに配置されるときに終了しません。 有害メッセージ キュー内のメッセージは、引き続き読み取って処理する必要があります。 有害メッセージ処理設定のサブセットは、最終的な有害サブキューからメッセージを読み取るときに使用できます。 該当する設定は、 ReceiveRetryCountReceiveErrorHandlingです。 ReceiveErrorHandlingを Drop、Reject、または Fault に設定できます。 MaxRetryCycles は無視され、ReceiveErrorHandling が Move に設定されている場合は例外が発生します。

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 ではサポートされません。 受信側キュー・マネージャーからの否定受信確認により、送信側キュー・マネージャーは、拒否されたメッセージを配信不能キューに配置します。 そのため、Windows Server 2003 および Windows XP では、 ReceiveErrorHandling.Reject は許可されません。

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

こちらもご覧ください