有害メッセージ処理
有害メッセージとは、アプリケーションへの配信試行の回数が最大値を超えたメッセージのことです。 この状況は、キュー ベースのアプリケーションがエラーによってメッセージを処理できないときに発生する可能性があります。 信頼性に対する要求を満たすために、キューに置かれたアプリケーションはトランザクションの下でメッセージを受信します。 キューに置かれたメッセージを受信したトランザクションを中止すると、メッセージはそのままキューに残り、新しいトランザクションの下で再試行されます。 トランザクションを中止させた問題が解決されなければ、受信側のアプリケーションは、配信試行回数の最大値を超えるまで同じメッセージの受信と中止を繰り返す悪循環に陥る可能性があり、その結果、有害メッセージが生じることになります。
メッセージは、さまざまな理由で有害メッセージになる可能性があります。 最も一般的なのは、アプリケーション固有の理由です。 たとえば、アプリケーションがキューからメッセージを読み取り、なんらかのデータベース処理を実行する場合は、アプリケーションがデータベースをロックできないことによって、トランザクションが中止されることが考えられます。 データベース トランザクションが中止されたために、メッセージはキューに残ります。これにより、アプリケーションではメッセージを再度読み取り、データベースのロックの取得を再試行します。 メッセージに無効な情報が含まれている場合にも、有害メッセージになる可能性があります。 たとえば、発注書に無効な顧客番号が含まれている場合があります。 このような場合、アプリケーションは自発的にトランザクションを中止し、メッセージを強制的に有害メッセージにすることがあります。
まれに、メッセージをアプリケーションにディスパッチできないことがあります。 メッセージに不適切なフレームがあったり、無効なメッセージ資格情報が割り当てられていたり、無効なアクション ヘッダーが含まれていたりする場合などは、Windows Communication Foundation (WCF) レイヤーがメッセージの問題を検出することがあります。 このような場合、アプリケーションはメッセージを受信しません。ただし、メッセージは有害メッセージとなるため、手動で処理できます。
有害メッセージの処理
WCF の有害メッセージ処理は、アプリケーションにディスパッチできないメッセージや、アプリケーションにディスパッチされても、アプリケーション固有の理由によって処理できないメッセージを、受信側アプリケーションで処理する機構を提供します。 有害メッセージ処理は、キューに置かれた使用可能な各バインディングの次のプロパティで構成します。
ReceiveRetryCount
。 アプリケーション キューからアプリケーションへのメッセージの配信を再試行する最大回数を示す整数値。 既定値は 5 です。 データベースの一時的なデッドロックなどの問題を即時再試行で解決する場合は、この値で十分です。MaxRetryCycles
. 再試行サイクルの最大数を示す整数値。 再試行サイクルは、アプリケーション キューから再試行サブキューにメッセージを転送し、構成可能な遅延の後に再試行サブキューからアプリケーション キューにメッセージを転送して配信を再試行するプロセスで構成されます。 既定値は 2 です。 Windows Vista では、メッセージが最大で (ReceiveRetryCount
+ 1) × (MaxRetryCycles
+ 1) 回、試行されます。 Windows Server 2003 および Windows XP では、MaxRetryCycles
が無視されます。RetryCycleDelay
. 再試行サイクル間の遅延時間。 既定値は 30 分です。MaxRetryCycles
とRetryCycleDelay
の組み合わせにより、問題に対処する機構が提供されます。この場合、定期的な遅延後に再試行が行われ、問題が解決されます。 たとえば、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 はアプリケーション固有のキューの名前です。
メッセージに対して実行される配信の最大試行回数は、次のようになります。
((ReceiveRetryCount + 1) × (MaxRetryCycles + 1)) (Windows Vista の場合)。
(ReceiveRetryCount + 1) (Windows Server 2003 および Windows XP の場合)。
Note
正常に配信されたメッセージについては、再試行は行われません。
メッセージの読み取りが試行された回数を追跡するために、Windows Vista では、中止回数をカウントする永続的なメッセージ プロパティと、アプリケーション キューとサブキューの間でメッセージが移動された回数をカウントする移動回数プロパティが保持されます。 WCF チャネルはこれらを使用して、受信再試行回数と再試行サイクル数を計算します。 Windows Server 2003 および Windows XP では、abort カウントが 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. 既存のメッセージ キュー アプリケーションとの通信に適したバインディング。
Note
これらのバインディングでは、WCF サービスの要件に基づいてプロパティを変更できます。 有害メッセージ処理機構全体は、受信側アプリケーションに対してローカルです。 このプロセスは、受信側アプリケーションが最終的に処理を停止し、送信側に否定受信確認を返信する場合を除き、送信元アプリケーションには認識されません。 この場合、メッセージは、送信元の配信不能キューに送られます。
ベスト プラクティス : MsmqPoisonMessageException の処理
メッセージが有害であるとサービスが判断した場合、キューに置かれたトランスポートは MsmqPoisonMessageException をスローします。この例外には、有害メッセージの LookupId
が含まれます。
受信側アプリケーションでは、必要な IErrorHandler インターフェイスを実装して、すべてのエラーを処理できます。 詳細については、「エラー処理およびレポートに対する制御の拡張」を参照してください。
アプリケーションでは、有害メッセージを有害メッセージ キューに移動し、サービスがキュー内の残りのメッセージにアクセスできるようにする、なんらかの有害メッセージ自動処理を必要とする場合があります。 エラー処理機構を使用して有害メッセージの例外をリッスンする唯一のシナリオは、ReceiveErrorHandling の設定を Fault に設定した場合です。 メッセージ キュー 3.0 の有害メッセージ サンプルは、この動作を示しています。 ベスト プラクティスを含め、有害メッセージを処理するために必要な手順の概要を以下に示します。
有害メッセージの設定がアプリケーションの要件を反映していることを確認します。 設定を処理するときは、Windows Vista、Windows Server 2003、および Windows XP でのメッセージ キューの機能の相違点を理解してください。
必要に応じて、
IErrorHandler
を実装して有害メッセージのエラーを処理します。ReceiveErrorHandling
をFault
に設定する場合、有害メッセージをキューから取り除いたり、外部の従属的な問題を解決したりする手動の機構が必要となります。そのため、IErrorHandler
をReceiveErrorHandling
に設定するときは、次のコードに示すように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 = {0}", ((MsmqPoisonMessageException)error).MessageLookupId); return true; } return false; } }
サービス動作が使用できる
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); } } }
サービスに有害動作属性による注釈が付けられていることを確認します。
また、ReceiveErrorHandling
を Fault
に設定した場合は、ServiceHost
が有害メッセージを検出するとエラーになります。 この場合、faulted イベントにフックしてサービスを終了し、是正措置を講じた後に再起動できます。 たとえば、LookupId
に伝達された MsmqPoisonMessageException に含まれる IErrorHandler
は記録しておくことができます。そのため、サービス ホストでエラーが発生したときに、System.Messaging
API を使用して、この LookupId
に基づいてキューからメッセージを受信し、そのメッセージをキューから削除して、外部ストアまたは別のキューに格納できます。 これで ServiceHost
を再起動して、通常の処理を再開できるようになります。 この動作については、「MSMQ 4.0 での有害メッセージ処理」で説明しています。
トランザクション タイムアウトと有害メッセージ
キューに置かれたトランスポート チャネルとユーザー コードの間では、特定の部類のエラーが発生する可能性があります。 これらのエラーは、メッセージ セキュリティ レイヤーやサービス ディスパッチ ロジックなど、中間のレイヤーで検出される場合があります。 たとえば、SOAP セキュリティ レイヤーで X.509 証明書がないことが検出された場合やアクションがない場合は、メッセージがアプリケーションにディスパッチされます。 この場合、サービス モデルはそのメッセージを破棄します。 メッセージはトランザクションで読み取られますが、そのトランザクションの結果は提供されないため、トランザクションが最終的にタイムアウトになって中止され、メッセージはキューに戻されます。 つまり、エラーの部類によっては、トランザクションがすぐに中止されるわけではなく、タイムアウトになるまで待機します。サービスのトランザクション タイムアウトは、ServiceBehaviorAttribute を使用して変更できます。
トランザクション タイムアウトをコンピューター全体で変更するには、machine.config ファイルを変更し、適切なトランザクション タイムアウトを設定します。トランザクションに設定されたタイムアウトに従って、トランザクションは最終的に中止され、キューに戻されることに注意してください。また、中止回数はインクリメントされます。 最終的に、メッセージは有害メッセージになり、ユーザー設定に従って適切な処理が行われます。
セッションと有害メッセージ
セッションでは、単一のメッセージと同じ再試行および有害メッセージ処理手順が行われます。 有害メッセージに対する上記のプロパティは、セッション全体に適用されます。 つまり、セッション全体が再試行され、メッセージは最終有害メッセージ キューに送られます。または、メッセージが拒否された場合は、送信側の配信不能キューに送られます。
バッチ処理と有害メッセージ
バッチの一部であるメッセージが有害メッセージになった場合は、バッチ全体がロールバックされ、チャネルはメッセージを 1 つずつ読み取る処理に戻ります。 バッチ処理の詳細については、「トランザクションに含まれるメッセージのバッチ処理」を参照してください。
有害キュー内のメッセージに対する有害メッセージ処理
有害メッセージ処理は、メッセージが有害メッセージ キューに配置された時点では終了しません。 有害メッセージ キューに置かれたメッセージは、依然として読み取り、処理する必要があります。 最終有害サブキューからメッセージを読み取るときは、有害メッセージ処理設定のサブセットを使用できます。 適用できる設定は、ReceiveRetryCount
と ReceiveErrorHandling
です。 ReceiveErrorHandling
は Drop、Rreject、Fault のいずれかに設定できます。 MaxRetryCycles
が Move に設定されている場合は、ReceiveErrorHandling
が無視され、例外がスローされます。
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 サービスによって読み取られた場合、このプロパティは、正確な値を格納できない可能性があります。