Error Handling

如本手册最开始 overview 所述,Spring 集成等面向消息的框架背后的一个主要动机是促进组件之间的松散耦合。消息信道扮演着重要角色,因为生产者和消费者不必互相了解。但是,这些优点也有一些缺点。在松散耦合的环境中,某些事情会变得更加复杂,错误处理就是其中一个示例。

As described in the overview at the very beginning of this manual, one of the main motivations behind a message-oriented framework such as Spring Integration is to promote loose coupling between components. The message channel plays an important role, in that producers and consumers do not have to know about each other. However, the advantages also have some drawbacks. Some things become more complicated in a loosely coupled environment, and one example is error handling.

向通道发送消息时,最终处理该消息的组件可能与发送者在同一线程内运行,也可能不在同一线程内运行。如果使用简单的默认 DirectChannel(其中 <channel> 元素没有 <queue> 子元素,也没有 task-executor 属性),则消息处理发生在发送初始消息的同一线程中。在这种情况下,如果抛出一个 Exception,它可以被发送者捕获,或者如果它是一个未捕获的 RuntimeException,则它可能传播到发送者之外。这与正常 Java 调用堆栈中抛出异常的操作相同。

When sending a message to a channel, the component that ultimately handles that message may or may not be operating within the same thread as the sender. If using a simple default DirectChannel (when the <channel> element that has no <queue> child element and no 'task-executor' attribute), the message handling occurs in the same thread that sends the initial message. In that case, if an Exception is thrown, it can be caught by the sender, or it may propagate past the sender if it is an uncaught RuntimeException. This is the same behavior as an exception-throwing operation in a normal Java call stack.

通过消息网关(参见 Messaging Gateways)或 MessagingTemplate(参见 MessagingTemplate)可以调用在调用方线程中运行的消息流。在任一情况下,默认行为是向调用方抛出任何异常。对于消息网关,请参阅 Error Handling,了解有关如何抛出异常以及如何配置该网关以将错误路由到错误信道的信息。在使用 MessagingTemplate 或直接发送到 MessageChannel 时,异常始终会抛给调用方。

A message flow that runs on a caller thread might be invoked through a messaging gateway (see Messaging Gateways) or a MessagingTemplate (see MessagingTemplate). In either case, the default behavior is to throw any exceptions to the caller. For the messaging gateway, see Error Handling for details about how the exception is thrown and how to configure the gateway to route the errors to an error channel instead. When using a MessagingTemplate or sending to a MessageChannel directly, exceptions are always thrown to the caller.

添加异步处理后,事情变得更加复杂。例如,如果 channel 元素确实提供了一个 queue 子元素(Java 和注释配置中的 QueueChannel),那么处理消息的组件在与发送器不同的线程中运行。使用 ExecutorChannel 时也是如此。发送者可能已将 Message 放入通道并转到其他事情。Exception 无法使用标准 Exception 抛出技术直接抛回该发送者。相反,处理异步进程的错误要求错误处理机制也是异步的。

When adding asynchronous processing, things become rather more complicated. For instance, if the 'channel' element does provide a 'queue' child element (QueueChannel in Java & Annotations Configuration), the component that handles the message operates in a different thread than the sender. The same is true when an ExecutorChannel is used. The sender may have dropped the Message into the channel and moved on to other things. There is no way for the Exception to be thrown directly back to that sender by using standard Exception throwing techniques. Instead, handling errors for asynchronous processes requires that the error-handling mechanism also be asynchronous.

Spring Integration 通过将错误发布到消息通道来支持对其组件的错误处理。具体来说,Exception 成为 Spring Integration ErrorMessage 的有效负载。然后将 Message 发送到以类似于 replyChannel 解析的方式解析的消息通道。首先,如果在发生 Exception 时正在处理的请求 Message 具有 errorChannel 标头(标头名称在 MessageHeaders.ERROR_CHANNEL 常量中定义),则 ErrorMessage 将发送到该通道。否则,错误处理程序将发送到 bean 名称为 errorChannel 的“全局”通道(这也定义为一个常量:IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME)。

Spring Integration supports error handling for its components by publishing errors to a message channel. Specifically, the Exception becomes the payload of a Spring Integration ErrorMessage. That Message is then sent to a message channel that is resolved in a way that is similar to the 'replyChannel' resolution. First, if the request Message being handled at the time the Exception occurred contains an 'errorChannel' header (the header name is defined in the MessageHeaders.ERROR_CHANNEL constant), the ErrorMessage is sent to that channel. Otherwise, the error handler sends to a “global” channel whose bean name is errorChannel (this is also defined as a constant: IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME).

框架在内部创建一个默认的 errorChannel bean。然而,如果你希望控制设置,你可以定义你自己的 errorChannel。以下示例显示如何在由具有容量为 500 的队列做后盾的 XML 配置中定义错误通道:

A default errorChannel bean is created internally by the Framework. However, you can define your own if you want to control the settings. The following example shows how to define an error channel in XML configuration backed by a queue with a capacity of 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 订阅顺序。如果您订阅其他消耗端点,则可能会抛出异常,并且您不想抢占日志记录,因此请确保其他处理器的顺序更高。

The default error channel is a PublishSubscribeChannel. By default, it has a LoggingHandler as a subscriber with an ERROR logging level and subscription order as Ordered.LOWEST_PRECEDENCE - 100. If you subscribe additional consuming endpoints, that might throw an exception, and you don’t want to preempt the logging, ensure that the additional handlers have a higher order.

这里要理解的最重要的事情是,基于消息的错误处理仅适用于由在 TaskExecutor 内执行的 Spring Integration 任务抛出的异常。这并不适用于在与发送者同一线程内操作的处理程序抛出的异常(例如,如本节前面所述,通过 DirectChannel)。

The most important thing to understand here is that the messaging-based error handling applies only to exceptions that are thrown by a Spring Integration task that is executing within a TaskExecutor. This does not apply to exceptions thrown by a handler that operates within the same thread as the sender (for example, through a DirectChannel as described earlier in this section).

当计划的轮询任务执行中发生异常时,这些异常会封装在 ErrorMessage 实例中,并发送到“errorChannel”。这是通过注入到全局 taskScheduler bean 的 MessagePublishingErrorHandler 完成的。如果仍然需要使用标准“errorChannel”集成流逻辑进行错误处理,则建议对任何自定义 taskScheduler 使用该 MessagePublishingErrorHandler。在这种情况下,可以用注册的 integrationMessagePublishingErrorHandler bean。

When exceptions occur in a scheduled poller task’s execution, those exceptions are wrapped in ErrorMessage instances and sent to the 'errorChannel' as well. This is done via a MessagePublishingErrorHandler injected into the global taskScheduler bean. It is recommended to use that MessagePublishingErrorHandler for any custom taskScheduler if the error handling still has to be done using standard 'errorChannel' integration flow logic. A registered integrationMessagePublishingErrorHandler bean can be used in this case.

要启用全局错误处理,请在该通道上注册一个处理程序。例如,你可以将 Spring Integration 的 ErrorMessageExceptionTypeRouter 配置为订阅 errorChannel 的端点的处理程序。然后,根据 Exception 类型,该路由器可以将错误消息分散到多个通道。

To enable global error handling, register a handler on that channel. For example, you can configure Spring Integration’s ErrorMessageExceptionTypeRouter as the handler of an endpoint that is subscribed to the errorChannel. That router can then spread the error messages across multiple channels, based on the Exception type.

从版本 4.3.10 开始,Spring 集成提供 ErrorMessagePublisherErrorMessageStrategy。您可以将它们用作发布 ErrorMessage 实例的一般机制。您可以在任何错误处理场景中调用或扩展它们。ErrorMessageSendingRecoverer 将此类扩展为可用于重试的 RecoveryCallback 实现,例如 RequestHandlerRetryAdviceErrorMessageStrategy 用于根据提供的异常和 AttributeAccessor 上下文构建 ErrorMessage。它可以注入到任何 MessageProducerSupportMessagingGatewaySupport 中。requestMessage 储存在 AttributeAccessor 上下文中的 ErrorMessageUtils.INPUT_MESSAGE_CONTEXT_KEY 下。ErrorMessageStrategy 可以使用该 requestMessage 作为其创建的 ErrorMessageoriginalMessage 属性。DefaultErrorMessageStrategy 正好就是这样做的。

Starting with version 4.3.10, Spring Integration provides the ErrorMessagePublisher and the ErrorMessageStrategy. You can use them as a general mechanism for publishing ErrorMessage instances. You can call or extend them in any error handling scenarios. The ErrorMessageSendingRecoverer extends this class as a RecoveryCallback implementation that can be used with retry, such as the RequestHandlerRetryAdvice. The ErrorMessageStrategy is used to build an ErrorMessage based on the provided exception and an AttributeAccessor context. It can be injected into any MessageProducerSupport or MessagingGatewaySupport. The requestMessage is stored under ErrorMessageUtils.INPUT_MESSAGE_CONTEXT_KEY in the AttributeAccessor context. The ErrorMessageStrategy can use that requestMessage as the originalMessage property of the ErrorMessage it creates. The DefaultErrorMessageStrategy does exactly that.

从 5.2 版开始,框架组件抛出的所有 MessageHandlingException 实例都包含一个组件 BeanDefinition 资源和源来确定配置点以形成异常。在 XML 配置中,资源是 XML 文件路径和带其 id 属性的 XML 标记。在使用 Java 和注释的配置中,资源是一个 @Configuration 类和源是一个 @Bean 方法。在大多数情况下,目标集成流程解决方案基于开箱即用的组件及其配置选项。当在运行时发生异常时,由于执行针对 bean,而不是 bean 的配置,因此堆栈跟踪中不包含任何最终用户代码。包含 bean 定义的资源和源有助于确定可能的配置错误,并提供更好的开发人员体验。

Starting with version 5.2, all the MessageHandlingException instances thrown by the framework components, includes a component BeanDefinition resource and source to determine a configuration point form the exception. In case of XML configuration, a resource is an XML file path and source an XML tag with its id attribute. With Java & Annotation configuration, a resource is a @Configuration class and source is a @Bean method. In most case the target integration flow solution is based on the out-of-the-box components and their configuration options. When an exception happens at runtime, there is no any end-user code involved in stack trace because an execution is against beans, not their configuration. Including a resource and source of the bean definition helps to determine possible configuration mistakes and provides better developer experience.

从版本 5.4.3 开始,默认错误信道配置了属性 requireSubscribers = true,以便在该信道上没有订阅者时不静默忽略消息(例如,当应用程序上下文已停止时)。在这种情况下,将抛出一个 MessageDispatchingException,该异常可能会借用入站信道适配器的客户端回调函数,以否定确认(或回滚)源系统中的原始消息,以便重新传递或将来考虑。要恢复之前的行为(忽略未分派错误消息),则必须将全局集成属性 spring.integration.channels.error.requireSubscribers 设置为 false。有关详细信息,请参见 Global PropertiesPublishSubscribeChannel Configuration(如果您手动配置全局 errorChannel)。

Starting with version 5.4.3, the default error channel is configured with the property requireSubscribers = true to not silently ignore messages when there are no subscribers on this channel (e.g. when application context is stopped). In this case a MessageDispatchingException is thrown which may lend on the client callback of the inbound channel adapter to negatively acknowledge (or roll back) an original message in the source system for redelivery or other future consideration. To restore the previous behavior (ignore non-dispatched error messages), the global integration property spring.integration.channels.error.requireSubscribers must be set to false. See Global Properties and PublishSubscribeChannel Configuration (if you configure a global errorChannel manually) for more information.

另请参阅 Error Handling Sample 了解更多信息。

See also Error Handling Sample for more information.