错误处理
正如本手册开头部分的 概述 中所述,Spring 集成等面向消息的框架背后的主要动机之一是促进组件之间的松耦合。消息通道在其中扮演着重要角色,因为生产者和消费者无需了解彼此。然而,这些优势也有一些缺点。在松耦合环境中,某些事情会变得更加复杂,其中一个例子就是错误处理。
当将消息发送到通道时,最终处理该消息的组件可能在与发送者相同的线程中运行,也可能不在。如果使用简单的默认 DirectChannel
(当 <channel>
元素没有 <queue>
子元素且没有 'task-executor' 属性时),消息处理将在发送初始消息的同一线程中进行。在这种情况下,如果抛出 Exception
,则可以由发送者捕获,或者如果它是一个未捕获的 RuntimeException
,则可能在发送者之后传播。这与正常 Java 调用堆栈中抛出异常的操作相同。
在调用者线程上运行的消息流可以通过消息网关(参见 消息网关)或 MessagingTemplate
(参见 MessagingTemplate
)调用。在这两种情况下,默认行为都是将任何异常抛出给调用者。对于消息网关,请参见 错误处理,了解有关如何抛出异常以及如何配置网关将错误路由到错误通道的详细信息。当使用 MessagingTemplate
或直接发送到 MessageChannel
时,异常始终会抛出给调用者。
当添加异步处理时,事情会变得更加复杂。例如,如果“channel”元素提供了一个“queue”子元素(在 Java 和注解配置中为QueueChannel
),则处理消息的组件将在与发送者不同的线程中运行。当使用ExecutorChannel
时也是如此。发送者可能已经将Message
放入通道并继续执行其他操作。无法通过使用标准的Exception
抛出技术将Exception
直接抛回该发送者。相反,异步进程的错误处理要求错误处理机制也是异步的。
Spring Integration 通过将错误发布到消息通道来支持其组件的错误处理。具体来说,Exception
成为 Spring Integration ErrorMessage
的有效负载。然后,该Message
被发送到一个消息通道,该通道的解析方式类似于“replyChannel”的解析。首先,如果在发生Exception
时正在处理的请求Message
包含一个“errorChannel”头(头名称在MessageHeaders.ERROR_CHANNEL
常量中定义),则ErrorMessage
将被发送到该通道。否则,错误处理程序将发送到一个名为errorChannel
的“全局”通道(这也定义为一个常量:IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME
)。
框架在内部创建了一个默认的errorChannel
bean。但是,如果您想控制设置,可以定义自己的。以下示例展示了如何在 XML 配置中定义一个由容量为500
的队列支持的错误通道。
-
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 完成的。建议对任何自定义taskScheduler 使用该MessagePublishingErrorHandler ,如果错误处理仍然需要使用标准的'errorChannel'集成流逻辑。在这种情况下,可以使用注册的integrationMessagePublishingErrorHandler bean。
|
要启用全局错误处理,请在该通道上注册一个处理程序。例如,您可以将 Spring Integration 的ErrorMessageExceptionTypeRouter
配置为订阅errorChannel
的端点的处理程序。然后,该路由器可以根据Exception
类型将错误消息传播到多个通道。
从 4.3.10 版本开始,Spring Integration 提供了ErrorMessagePublisher
和ErrorMessageStrategy
。您可以将它们用作发布ErrorMessage
实例的通用机制。您可以在任何错误处理场景中调用或扩展它们。ErrorMessageSendingRecoverer
扩展了此类作为RecoveryCallback
实现,可用于重试,例如RequestHandlerRetryAdvice
。ErrorMessageStrategy
用于基于提供的异常和AttributeAccessor
上下文构建ErrorMessage
。它可以注入到任何MessageProducerSupport
或MessagingGatewaySupport
中。requestMessage
存储在AttributeAccessor
上下文的ErrorMessageUtils.INPUT_MESSAGE_CONTEXT_KEY
下。ErrorMessageStrategy
可以使用该requestMessage
作为其创建的ErrorMessage
的originalMessage
属性。DefaultErrorMessageStrategy
正是这样做的。
从 5.2 版本开始,框架组件抛出的所有 `MessageHandlingException` 实例都包含一个组件 `BeanDefinition` 资源和源,以便从异常中确定配置点。对于 XML 配置,资源是 XML 文件路径,源是带有 `id` 属性的 XML 标签。对于 Java 和注解配置,资源是 `@Configuration` 类,源是 `@Bean` 方法。在大多数情况下,目标集成流解决方案基于开箱即用的组件及其配置选项。当运行时发生异常时,堆栈跟踪中不会涉及任何最终用户代码,因为执行针对的是 Bean,而不是它们的配置。包含 Bean 定义的资源和源有助于确定可能的配置错误,并提供更好的开发体验。
从 5.4.3 版本开始,默认错误通道配置了属性 `requireSubscribers = true`,以便在该通道上没有订阅者时(例如,当应用程序上下文停止时)不会静默忽略消息。在这种情况下,会抛出 `MessageDispatchingException`,该异常可能会在入站通道适配器的客户端回调中传递,以对源系统中的原始消息进行负确认(或回滚),以便重新传递或其他将来考虑。要恢复以前的行为(忽略未分发的错误消息),必须将全局集成属性 `spring.integration.channels.error.requireSubscribers` 设置为 `false`。有关更多信息,请参阅 全局属性 和 `PublishSubscribeChannel` 配置(如果您手动配置全局 `errorChannel`)。
有关更多信息,请参阅 错误处理示例。