アダプターのエラーを処理する方法
アダプターでは通常、処理できないメッセージを中断します。 たとえば、受信アダプターで送信エラーが発生した場合は通常、メッセージを中断します。ただし、この判断はアダプターの目的によって変わることがあります。 エラーを処理するときには、セキュリティの問題も考慮する必要があります。 たとえば、失敗したメッセージをすべてアダプターで自動的に中断すると、そのアダプターはサービス拒否攻撃に対して脆弱になり、BizTalk Server の保留キューがいっぱいになる可能性があります。 HTTP アダプターなど一部のアダプターでは、要求が拒否されたことを示すエラー コードをクライアントに返すことができます。 こうした種類のアダプターでは、多くの場合、メッセージを中断するよりもエラー コードを返す方が有効です。 送信アダプターでは通常、プライマリ トランスポートとセカンダリ トランスポートの両方で、再試行回数が設定値を超えた後ではじめてメッセージを中断します。
操作を含むバッチではなく、個々の操作にエラー処理を関連付ける
アダプターでのメッセージのバッチ処理は、アダプターのユーザーに見えないところで実行し、 バッチ内の操作の 1 つが失敗しても他の操作に影響が生じないようにする必要があります。 しかし、バッチはアトミックであるため、1 つのメッセージでエラーが発生するとバッチのエラーとなり、操作は処理されなくなります。
したがって、エラーが発生したときに処理するコードを記述し、正常なメッセージを再送信して失敗したメッセージを中断するよう指定する必要があります。 BizTalk Server では詳細なエラー構造が提供されるので、アダプターでは失敗した操作を特定できます。 これを基に、正常な操作を再実行し失敗した操作を中断するバッチをさらに構築できます。
アダプター内のバッチ処理によって、操作の最終的な状態が影響を受けないようにしてください。
SetErrorInfo を使用して BizTalk Server にエラーを報告する
メッセージを中断する場合は、その前のメッセージ コンテキストから BizTalk Server にエラー情報を送信する必要があります。 BizTalk Serverは、IBaseMessage インターフェイスと ITransportProxy インターフェイスの両方で SetErrorInfo メソッドを使用してエラー報告機能を提供します。 エラーを報告するには次の方法を使用できます。
メッセージの処理中にエラーが発生した場合は、メッセージ (IBaseMessage) で SetErrorInfo(Exception e) を使用して例外を中断するように設定します。 こうすると、エンジンではメッセージと共にエラーが保持され、後の診断に使用できます。また、イベント ログにエラーが記録され、管理者はエラーを認識できます。
初期化中にエラーが発生した場合、または内部簿記 (メッセージ処理中ではない) が発生した場合は、初期化中に渡された ITransportProxy ポインターで SetErrorInfo(Exception e) を呼び出す必要があります。 アダプターが BaseAdapter 実装を基にしている場合は、このポインターに常にアクセスできるようにしておく必要があります。 そうでない場合は、ポインターをキャッシュしておくようにします。
どちらの方法でも、エラーが報告されると、イベント ログにエラー メッセージが書き込まれます。 エラーとそのメッセージを関連付けることができる場合は、必ず関連付けを行ってください。
データベースがオフラインになった場合の処理
BizTalk Server データベースの 1 つがオフラインになった場合、BizTalk サービスは自身を再利用します。 メッセージング エンジンは、サービスが再利用される前にすべての受信場所のシャットダウンをあらゆる方法で試行しますが、 この処理が 60 秒以上かかるとサービスは終了します。 エンジンのトランザクションは行われるので、データの損失は起こりません。
タイムアウトはレジストリで次のキーを使用して設定できます。
DWORD
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\BTSSvc{Host Guid}\MessagingDBFailoverShutdownTimeLimit
分離アダプターの場合はプロセスの所有者が BizTalk Server ではないため、BizTalk Server データベースの 1 つがオフラインになると受信場所は無効になります。 これらの受信場所は、データベースがオンラインに戻った後で再度有効になります。
イベント ログへの書き込み
アダプターは、例外を渡す IBTTransportProxy インターフェイスを使用してイベント ログ エントリを書き込むことができます。 ネイティブ コードで開発されたアダプターは、IErrorInfo インターフェイス IBTTransportProxy.SetErrorInfo( Exceptione
)を渡す必要があります。
送信エラー後にアダプターでメッセージの送信を再試行したり、アダプターのバックアップ トランスポートにメッセージを移動したり、メッセージを中断するときなどは、メッセージング エンジンがアダプターに代わってイベント ログへの書き込みを行います。 このような場合、アダプターで必要になる操作は、API の呼び出し前にメッセージに例外を設定することだけです。 次のコードに、このことを示します。
IBaseMessage msg;
...
// Set exception on msg to indicate why transmission failed...
msg.SetErrorInfo(
new ApplicationException(
"The TCP connection was closed by the destination"));
受信に固有のバッチ エラーの処理
受信に失敗した場合の処理
アダプターから BizTalk Server に操作または操作のバッチを送信するときに失敗した場合は、さまざまな原因が考えられます。 最も大きな原因は次の 2 つです。
受信パイプラインが失敗した。
メッセージを公開するときにルーティング エラーが発生した。
受信パイプラインが失敗した場合、メッセージング エンジンは自動的にメッセージの中断を試行します。 この中断操作は常に正常に完了するとは限りません。 たとえば、メッセージの公開中にメッセージング エンジンでルーティング エラーが発生すると、エンジンではメッセージの中断を試行することもできません。
メッセージの失敗は常に発生する可能性があります。 このような状況では、アダプターは MoveToSuspendQ API を明示的に呼び出す必要があり、メッセージの中断を試みる必要があります。 アダプターでメッセージの中断を試行する場合は、次のいずれかの状況が該当します。
アダプターが送信 (推奨) したメッセージ オブジェクトと同じオブジェクトを中断する必要がある。
アダプターで新しいメッセージを作成する必要がある場合は、新しいメッセージのメッセージ コンテキストに、最初に送信されたメッセージのメッセージ コンテキストへのポインターを設定する必要がある。 この理由は、メッセージのメッセージ コンテキストに、メッセージとエラーに関する有用な情報が多く含まれるためです。 この情報は、失敗したメッセージをデバッグするときに必要になります。
Note
アダプターで新しいメッセージ オブジェクトを作成して中断する場合、アダプターでは、古いメッセージ オブジェクトから新しいメッセージ オブジェクトにエラー情報をコピーする必要があります。
BizTalk Server 付属の HTTP アダプターなど、一部のアダプターではメッセージを中断する必要はありません。 これらのアダプターでは、エラーをクライアントに返すことができます。
エラーの原因
エラーの単純な原因は、バッチの構築時、または IBTTransportBatch::D one が呼び出されたときに発生する可能性があるエラーです。
発信に失敗しました。 Submit 呼び出しは、限られた数の理由で失敗する可能性があり、それらのすべてが致命的です。 これらの理由には以下のものが含まれます。
BizTalk Server のプロセス領域で発生するメモリ不足エラー。
スキーマ アセンブリが展開先から削除されている。 この場合、 送信 は不可解なエラーで失敗します。 MQSeries アダプターの場合は、BizTalk Server からの一般的なエラー例外がキャッチされ、システム イベント ログに拡張エラー メッセージが書き込まれます。 このメッセージでは、考えられるエラー原因の 1 つとして、スキーマ アセンブリが展開先から削除されていることが示されます。
一般に、 送信 に失敗した場合は、同じトランザクションを使用してメッセージを中断する必要があります。
IBTTransportBatch::Done の失敗 IBTTransportBatch::D one 呼び出しは、いくつかの理由のいずれかで失敗する可能性があります。 通常は、中断操作を 1 回試行してから、それが失敗したときのみトランザクションを終了してください。 IBTTransportBatch::D one のエラーから受け取る可能性のあるエラー コードの 1 つは、BizTalk Serverがシャットダウンしようとしているということです。 この場合は、 Terminate 呼び出しが同時に行われている可能性があるため、トランザクションを終了してそのままにしておく必要があります。 その他のシナリオは、バッチを正常に構築し、 IBTTransportBatch::D one を正常に実行したときに発生します。 このような場合、 エラーは BatchComplete で返され、アダプターはそれらの処理を決定する必要があります。 以下では、この場合について検討します。
BatchComplete エラーの処理
BatchComplete は、バッチ操作の完了状態を示すために、BizTalk Serverによって呼び出されるアダプターによって提供されるコールバックです。
BatchComplete に渡される最も重要なパラメーターは、バッチの状態 hResult
です。 このパラメーターはバッチの成功または失敗を示します。 バッチの失敗は、バッチのいずれの操作も成功しなかったことを意味します。 アダプターはバッチ状態構造を通過し、失敗したメッセージを決定します (これは バッチのフィルター処理と呼ばれます)。
非トランザクション BatchComplete エラー
非トランザクション アダプターの場合、 SubmitMessage/SubmitRequestMessage または SubmitResponseMessage 操作でエラーが発生した場合は、応答を選択する必要があります。 通常、アダプターは MoveToSuspendQ を呼び出してメッセージを中断します。
次の操作は常に渡されることが想定されています: DeleteMessage、 MoveToSuspendQ、 ResubmitMessage。 これらの操作が失敗した場合は、通常、アダプターにバグがあることを示しています。 この場合は、エラーを処理するコードを記述する必要はありません。 ただし、別の操作の失敗が原因でバッチが失敗した場合は、新しいバッチでこれらの操作を再実行する必要があります。
アダプターが MovetoBackupTransport を 呼び出し、それが失敗した場合 (バックアップ トランスポートがないため)、アダプターは MoveToSuspendQ を呼び出してメッセージを中断する必要があります。
トランザクション BatchComplete エラー
アダプターによって作成されたトランザクションを使用して BizTalk Server にバッチを送信するときには、次のいずれかの方法を使用する必要があります。
単一メッセージのバッチを使用する 。単一メッセージのバッチを BizTalk Server に送信します。 この単一メッセージが失敗した場合は、同じトランザクションで BizTalk Server に 2 つ目のバッチを送信できますが、問題のあるメッセージは再送信せず保留キューへ移動する必要があります。 失敗したメッセージを削除して 2 つ目のバッチを送信し、 BizTalk Server で 2 つ目のバッチが成功したことが確認されたら、トランザクションをコミットできます。 2 つ目のバッチが失敗した場合、アダプターではトランザクションを中止するか、メッセージの格納先を見つける必要があります。 この場合、トランザクションのロールバック処理によって、パフォーマンスが急激に低下します。
アダプターのパフォーマンスを改善するにはいくつかの方法があります。 たとえば MQSeries アダプターでは、実行時に動的に処理方法が調整されます。 このアダプターは 100 メッセージのバッチを処理できます。 エラーが発生した場合はバッチを終了する必要がありますが、このとき単一メッセージのバッチに切り替えて問題のあるメッセージに対処し、 その後、100 メッセージのバッチに戻ります。 再度エラーが発生した場合は、再び処理が遅くなります。
プリエンプティブ中断を使用する 。複数メッセージのバッチを構築し、その中で、エラーがあるメッセージをプリエンプティブに中断します。 バッチには 、Submit 操作と MoveToSuspendQ 操作の組み合わせが含まれており、トランザクションの最初の唯一のバッチです。 問題のあるデータはプリエンプティブに中断され、処理が続行されます。BizTalk Server からの確認を受信したら、トランザクションをコミットできます。
この処理には予測が必要なように思えますが、この方法は MSMQ アダプターで以前から使用されています。 この方法を使用する場合は、メッセージ ID がそれぞれ確実に一意であることが必要です。 このアダプターは複数メッセージを含む単一バッチを構築し、 エラーが発生した場合は、トランザクションとバッチをロールバックします。このとき、アダプターは一時データ構造にメッセージ ID を記録します (この構造が無期限に増加するのを防ぐために、その中の項目は一定の時間遅延の後に削除されます)。各バッチが送信される前に、アダプターは不適切なメッセージ ID の一覧を確認します。 問題のあるメッセージ ID があった場合、該当のメッセージは以前に一度失敗しているので、アダプターで失敗を予測できます。したがって、メッセージを送信せずに、プリエンプティブに中断できます。
すべてのアダプターで、メッセージ ID が確実に一意であるとは限りません。トランザクション ストアではさらに可能性が低くなります。 このため、多くのトランザクション アダプターは、単一メッセージ バッチの送信のみに制限されています。
その他のエラーの処理
メッセージ中断中のエラーなど、その他のエラーが発生した場合はすべて、アダプターでトランザクションを終了する必要があります。 その他の方法ではメッセージが重複するか削除されます。
バッチが失敗した場合、アダプターでは可能な限りトランザクションを中止する必要があります。 ただし、アダプターでトランザクションを中止できない場合もあり、 このような場合は、同じトランザクションを使用するメッセージを中断する必要があります。
トランザクション受信でのエラーの処理
トランザクション処理では、エラーが発生したときにトランザクションを終了するのが一般的なパターンです。 この場合、すべての操作が以前の状態に戻され、データが失われることはありません。 ただし、トランザクションで取得したデータを使用している場合は、これでは十分ではない可能性があります。この例としては、データベースの段階テーブルから一度に 1 行を取得している場合や、MQSeries、MSMQ などのキューイング製品から一度に 1 つのメッセージを取得している場合などがあります。 単純にトランザクションを終了して元の状態に戻し、また同じデータを取得すると、同じエラーが発生する可能性が高く、システムはエラーのある状況から抜け出せません。
以前のバージョンの BizTalk Server に付属の SQL アダプターには、この動作が設定されていました。 しかし、リリース後すぐにこのアダプターの動作は変更され、失敗したメッセージの中断とトランザクションのコミットを試行するようになっています。 同じトランザクションで保留キューにメッセージを移動し、トランザクションをコミットすると、データ損失を防止でき、アダプターでは問題があるデータに対処できるようになります。
アダプターの受信部分が 送信 メッセージ操作に応答してエラー メッセージを渡されると、アダプターはそのエラーを処理し、メッセージを中断キューに移動する必要があります。
アダプターでトランザクション オブジェクトを作成し、そのトランザクションでメッセージを送信するトランザクション バッチの場合、アダプターではエラーが発生したときと同じトランザクションでメッセージを保留キューに移動する必要があります。 トランザクションでは、エラーの原因となったデータも含めすべてのデータが削除されないようにします。
サブスクリプションが存在しない場合のメッセージの処理
BizTalk Server では、メッセージを受け入れるサブスクリプションが定義されていない場合、メッセージ ボックス データベースでのメッセージの公開は許可されません。 サブスクリプションはオーケストレーションまたは送信ポートで登録されます。 複数のサブスクリプションの定義が可能で、この場合メッセージは複数の送信先に送信されます。 サブスクリプションが存在しない場合、BizTalk Server はメッセージを拒否し、メッセージの中断を試行しません。 アダプターでこのエラーを処理せず、明示的にメッセージを中断しない場合は、メッセージは削除され、メッセージのデータも失われる可能性があります。 トランザクション アダプターの場合は、トランザクションを終了してメッセージを送信先に返すものもあります。
受信ストリームのシークのサポート
受信側ストリームは、パイプライン障害時にメッセージを中断できるようにするために、BizTalk Serverの Seek メソッドをサポートする必要があります。 メッセージ ストリームが seekable でない場合、BizTalk Serverは Seek を実行しようとしたときにエラーを生成します。
多くの場合、 Seek のサポートは簡単ではありません。 たとえば、ネットワークからデータをストリーミングするときには、ネットワーク リソースに戻って再度データを要求することは困難です。
BizTalk Server に付属のいくつかのアダプターは、BizTalk Server でメッセージ データが読み取られると同時に、データをディスク上のファイルにスプールします。 これにより、アダプターは、(メッセージ データのパイプライン処理など) エラーが発生した場合に、そのファイルで Seek を使用できます。 内部的には、アダプターは ReadOnlySeekableStream クラスを使用します。このクラスは、受信したシーク不可能なストリームをラップし、構成可能なサイズのしきい値に達するとディスクにオーバーフローします。 しきい値のサイズよりも小さいメッセージはディスクに保存されません。
ユーザーが構成できるエラー処理オプションについて検討する
エラーへの適切な対応が 1 つでない場合があります。 この場合は、ユーザーが構成できるオプションを検討し、動作を選択できるようにする必要があります。 MQSeries アダプターはこのオプションに対応しています。
エラーが発生したときにアダプターでメッセージが中断される問題は、BizTalk Serverの Suspended キューが "ブラック ホール" であるということです。メッセージをキューに取り込むのは比較的簡単ですが、メッセージを再び取り出すのが難しくなります。
アダプターの使用において、保留キュー内のアイテムをユーザーがまったく必要としない場合もあります。 たとえば MQSeries アダプターを使用する場合、ユーザーは構成オプションで次のいずれかの動作を実行できます。
アダプターでエラーが発生したとき、現在のトランザクションを終了し、アダプター自身を無効にする。
失敗したメッセージを中断し、トランザクションをコミットする。 BizTalk Server でメッセージが正常に中断された場合でも、アダプターではこの操作を実行します。 この操作では、イベント ログが厳密には正確ではなくなりますが、顧客の要件を満たすことができます。
1 つのスレッドを使用して BatchComplete を待機することで受信を順序付ける
BizTalk Server のインターフェイスは、コンカレンシーをサポートすることでパフォーマンスと拡張性を向上するよう設計されています。 しかし、MQSeries または MSMQ などのメッセージ キュー製品からメッセージを受信するときなどは、しばしばメッセージの受信を厳密に順序付ける必要が生じます。このような場合は、アダプターに対して追加の作業を行い、コンカレンシーの一部を無効にする必要があります。 これは、次の 2 つの手順で行うことができます。
アダプターでのすべてのデータ処理に単一スレッドを使用します。
BizTalk Server でそれぞれのバッチが完全に処理されるまで待機します。 この要件は重要であり、.NET スレッド同期プリミティブを使用して実現できます。 たとえば、 AutoResetEvent を使用すると、次のようになります。
メインワーカー スレッドと BatchComplete コールバック オブジェクトの両方からアクセスできるイベント オブジェクトを宣言します。
メインワーカー スレッドで、通常どおりにメッセージをバッチに送信しますが、バッチ IBTTransportBatch::D one の呼び出しの直前に、イベント オブジェクトで AutoResetEvent.Reset を呼び出します。
この同じスレッドからイベント オブジェクトに対して AutoResetEvent.WaitOne を呼び出します。 これによって、メイン ワーカー スレッドがブロックされます。 BizTalk Serverからの BatchComplete コールバックで、同じイベント オブジェクトで AutoResetEvent.Set を呼び出してワーカー スレッドのブロックを解除し、別のメッセージを処理する準備が整います。
このような 受信順序 は、パフォーマンスの大幅な低下を引き起こすので、構成可能にすることを強くお勧めします。 ほとんどではないにせよ、多くの場合、ユーザーはメッセージの順序付けを必要としません。 メッセージを中断すると、順序も崩れる可能性があり、 この場合の対応はアプリケーションによって異なります。このため、アダプターを構成するポイントを用意し、ユーザーにオプションを提供するのが最良の方法です。
順序付けを使用する場合で、順序を破棄するのではなくアダプターを無効にし、処理を停止することを顧客が希望する場合もあります。 受信の順序付けがサポートされている MQSeries アダプターを使用すれば、このオプションをユーザーに提供できます。
送信に固有のバッチ エラーの処理
送信再試行とバッチの処理
送信側バッチ処理の一般的な例としては、次のような処理があります。
BizTalk Server がアダプターにメッセージのバッチを送信します。
アダプターは、メッセージが送信先に正常に送信されたことを確認した後、BizTalk Server に送信の完了を示す削除メッセージを送信します。 通常はパフォーマンス向上のため、複数の削除メッセージが任意にまとめられます。
送信側アダプターでメッセージの処理に失敗した場合、アダプターはそのメッセージに対して次のいずれかの処理を実行します。
BizTalk Server にメッセージの再試行を要求する。 BizTalk Server はメッセージの再試行を自動的に実行しませんが、 再試行のカウントを保持しており、このカウントはメッセージのコンテキストで参照できます。
メッセージを処理できないと判断し、 メッセージを次のトランスポートに移動する。 アダプターは、Batch オブジェクトの MoveToNextTransport 呼び出しでこれを行います。
メッセージを保留キューに移動する。
アダプターではメッセージの処理が決定されますが、 アダプターの動作を一貫したものにするようお勧めします。こうすることで、BizTalk Server のサポートが容易になります。
アダプターは次のような動作に設定することを強くお勧めします。 BizTalk Server に付属のアダプターにはこの動作が設定されています。
バッチでの送信エラー処理に関する推奨動作
送信アダプターはいくつかのメッセージを受信し、BizTalk Server に送信します。
アダプターでは成功したメッセージごとに、BizTalk Server にメッセージの削除を要求する必要があります。 BizTalk Server への通信はすべてバッチで行います。削除もバッチにまとめることができます。 これらのバッチは、BizTalk Server によってアダプター上に作成されるバッチと同じである必要はありません。 SolicitResponse シナリオなどで応答メッセージがある場合は、これらのメッセージも削除メッセージと共に (SubmitResponse で) BizTalk Server に返送する必要があります。
アダプターでメッセージ処理が失敗した場合は、再試行カウントを確認する。
再試行カウントが設定値を超えていない場合は、BizTalk Server にメッセージを再送信する。その際、メッセージの再試行回数を設定する。 アダプターで使用する再試行カウントと再試行間隔は、メッセージ コンテキストで確認できます。
再試行回数を超えた場合、アダプターは MoveToNextTransport を使用してメッセージの移動を試みる必要があります。 再送信メッセージと MoveToNextTransport メッセージは、同じバッチ内の削除と混合して、BizTalk Serverに戻すことができます。 これは必須ではありませんが、有効な方法です。
再送信と MoveToNextTransport は、アダプターがエラーに対処する方法です。 この処理中にエラーが発生することもあります。 この場合、(BatchComplete メソッドで) BizTalk Serverからの応答を処理する場合、アダプターは、そのエラーの対処方法を示すために、BizTalk Serverに対して別のバッチを作成する必要があります。
失敗への対処中に起こったエラーを処理するには、次の手順に従います。
再送信が失敗した場合は、 MoveToNextTransport を使用します。
MoveToNextTransport が失敗した場合は、MoveToSuspendQ を使用します。
BizTalk Server で操作の成功を受信するまで、BizTalk Server に対してバッチの作成を続ける必要があります。
メッセージ コンテキスト プロパティのシリアル化
メッセージ コンテキスト プロパティに割り当てるオブジェクトはすべてシリアル化できる必要があります。 それ以外の場合、メッセージング エンジンは E_NOINTERFACE 型の例外をスローします。 この戻り値は、メッセージ コンテキスト プロパティに対してシリアル化できないオブジェクトの割り当てが試行されたことを暗黙的に示すものです。