エラー処理

このマニュアルの冒頭の概要で説明したように、Spring Integration などのメッセージ指向フレームワークの背後にある主な動機の 1 つは、コンポーネント間の疎結合を促進することです。メッセージチャネルは、プロデューサーとコンシューマーがお互いを知る必要がないという点で重要なロールを果たします。ただし、この利点にはいくつかの欠点もあります。疎結合環境では、いくつかのことがより複雑になります。1 つの例はエラー処理です。

メッセージをチャネルに送信するとき、最終的にそのメッセージを処理するコンポーネントは、送信者と同じスレッド内で動作している場合と動作していない場合があります。単純なデフォルト DirectChannel を使用する場合(<queue> 子要素および 'task-executor' 属性を持たない <channel> 要素の場合)、メッセージ処理は、初期メッセージを送信するのと同じスレッドで行われます。その場合、Exception がスローされた場合、送信者によってキャッチされる可能性があります(または、キャッチされていない RuntimeException の場合、送信者を越えて伝播する可能性があります)。これは、通常の Java コールスタックでの例外をスローする操作と同じ動作です。

呼び出し元スレッドで実行されるメッセージフローは、メッセージングゲートウェイ(メッセージングゲートウェイを参照)または MessagingTemplate (MessagingTemplate を参照)を介して呼び出される場合があります。どちらの場合でも、デフォルトの動作は、呼び出し元に例外をスローすることです。メッセージングゲートウェイの場合、例外がスローされる方法と、エラーをエラーチャネルにルーティングするようにゲートウェイを構成する方法の詳細については、エラー処理を参照してください。MessagingTemplate を使用する場合、または MessageChannel に直接送信する場合、例外は常に呼び出し元にスローされます。

非同期処理を追加すると、状況はかなり複雑になります。たとえば、'channel' 要素が 'queue' 子要素(Java および Annotations 構成の QueueChannel)を提供する場合、メッセージを処理するコンポーネントは送信者とは異なるスレッドで動作します。ExecutorChannel を使用する場合も同様です。送信者が Message をチャネルにドロップし、他のものに移動した可能性があります。標準の Exception スローテクニックを使用して Exception をその送信者に直接スローする方法はありません。代わりに、非同期プロセスのエラーを処理するには、エラー処理メカニズムも非同期である必要があります。

Spring Integration は、エラーをメッセージチャネルに発行することで、そのコンポーネントのエラー処理をサポートします。具体的には、Exception は Spring Integration ErrorMessage のペイロードになります。その Message は、'replyChannel' 解決と同様の方法で解決されるメッセージチャネルに送信されます。まず、Exception が発生したときに処理されていたリクエスト Message に 'errorChannel' ヘッダー (ヘッダー名は MessageHeaders.ERROR_CHANNEL 定数で定義されています) が含まれている場合、ErrorMessage がそのチャネルに送信されます。それ以外の場合、エラーハンドラーは、Bean 名が errorChannel (これも定数として定義されています: IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME) である「グローバル」チャネルに送信します。

デフォルトの errorChannel Bean は、フレームワークによって内部的に作成されます。ただし、設定を制御する場合は、独自に定義できます。次の例は、容量が 500 のキューによってサポートされる XML 構成でエラーチャネルを定義する方法を示しています。

Java
@Bean
QueueChannel errorChannel() {
    return new QueueChannel(500);
}
XML
<int:channel id="errorChannel">
    <int:queue capacity="500"/>
</int:channel>
デフォルトのエラーチャネルは PublishSubscribeChannel です。

ここで理解する最も重要なことは、メッセージングベースのエラー処理は、TaskExecutor 内で実行されている Spring Integration タスクによってスローされる例外にのみ適用されるということです。これは、送信者と同じスレッド内で動作するハンドラーによってスローされる例外には適用されません(たとえば、このセクションで前述した DirectChannel を使用)。

スケジュールされたポーラータスクの実行中に例外が発生すると、それらの例外は ErrorMessage インスタンスにラップされ、'errorChannel' にも送信されます。これは、グローバル taskScheduler Bean に挿入された MessagePublishingErrorHandler を介して行われます。標準の 'errorChannel' 統合フローロジックを使用してエラー処理を実行する必要がある場合は、カスタム taskScheduler にその MessagePublishingErrorHandler を使用することをお勧めします。この場合、登録済みの integrationMessagePublishingErrorHandler Bean を使用できます。

グローバルエラー処理を有効にするには、そのチャネルにハンドラーを登録します。例: Spring Integration の ErrorMessageExceptionTypeRouter を、"errorChannel" にサブスクライブされているエンドポイントのハンドラーとして設定できます。そのルータは、Exception 型に基づいて、エラーメッセージを複数のチャネルに分散できます。

バージョン 4.3.10 以降、Spring Integration は ErrorMessagePublisher および ErrorMessageStrategy を提供します。ErrorMessage インスタンスを公開するための一般的なメカニズムとして使用できます。エラー処理シナリオで呼び出したり、拡張したりできます。ErrorMessageSendingRecoverer は、このクラスを、RequestHandlerRetryAdvice などの再試行で使用できる RecoveryCallback 実装として拡張します。ErrorMessageStrategy は、提供された例外と AttributeAccessor コンテキストに基づいて ErrorMessage を構築するために使用されます。MessageProducerSupport または MessagingGatewaySupport に注入できます。requestMessage は、AttributeAccessor コンテキストの ErrorMessageUtils.INPUT_MESSAGE_CONTEXT_KEY に保管されます。ErrorMessageStrategy は、作成した ErrorMessage の originalMessage プロパティとして、その requestMessage を使用できます。DefaultErrorMessageStrategy はまさにそれを行います。

バージョン 5.2 以降、フレームワークコンポーネントによってスローされるすべての MessageHandlingException インスタンスには、コンポーネント BeanDefinition リソースとソースが含まれ、例外からの構成ポイントを決定します。XML 構成の場合、リソースは XML ファイルパスであり、id 属性を持つ XML タグをソースとします。Java&Annotation 構成では、リソースは @Configuration クラスで、ソースは @Bean メソッドです。ほとんどの場合、ターゲット統合フローソリューションは、すぐに使用できるコンポーネントとその構成オプションに基づいています。実行時に例外が発生した場合、実行は構成ではなく Bean に対するものであるため、スタックトレースに関与するエンドユーザーコードはありません。Bean 定義のリソースとソースを含めると、構成ミスの可能性を判断し、開発者エクスペリエンスを向上させることができます。

バージョン 5.4.3 以降、デフォルトのエラーチャネルはプロパティ requireSubscribers = true で構成され、このチャネルにサブスクライバーがない場合(アプリケーションコンテキストが停止している場合など)にメッセージをサイレントに無視しないようになっています。この場合、MessageDispatchingException がスローされ、受信チャネルアダプターのクライアントコールバックを利用して、再配信またはその他の将来の検討のために、ソースシステム内の元のメッセージを否定的に確認(またはロールバック)することができます。以前の動作を復元するには(ディスパッチされていないエラーメッセージを無視する)、グローバル統合プロパティ spring.integration.channels.error.requireSubscribers を false に設定する必要があります。詳細については、グローバルプロパティおよび PublishSubscribeChannel 構成(グローバル errorChannel を手動で構成する場合)を参照してください。