エラー処理

このマニュアルの最初の概要で説明したように、Spring Integration などのメッセージ指向フレームワークの背後にある主な動機の 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

  • XML

@Bean
QueueChannel errorChannel() {
    return new QueueChannel(500);
}
<int:channel id="errorChannel">
    <int:queue capacity="500"/>
</int:channel>
デフォルトのエラーチャネルは PublishSubscribeChannel です。デフォルトでは、サブスクライバーとして LoggingHandler があり、ロギングレベルが ERROR で、サブスクリプションの順序が Ordered.LOWEST_PRECEDENCE - 100 です。追加の消費エンドポイントをサブスクライブすると、例外がスローされる可能性があり、ロギングをプリエンプトしたくない場合は、追加のハンドラーの順序が高いことを確認してください。

ここで理解する最も重要なことは、メッセージングベースのエラー処理は、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 を手動で構成する場合) を参照してください。

詳細については、エラー処理のサンプル [GitHub] (英語) も参照してください。