# 错误处理

# 错误处理

正如本手册开头的overview中所描述的, Spring 集成之类的面向消息的框架背后的主要动机之一是促进组件之间的松耦合。消息渠道起着重要的作用,因为生产者和消费者不必相互了解。然而,这些优点也有一些缺点。在松散耦合的环境中,有些事情会变得更加复杂,其中一个例子就是错误处理。

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

在调用者线程上运行的消息流可以通过消息网关(参见消息传递网关)或MessagingTemplate(参见[MessagingTemplate](./channel.html#channel-template))调用。在这两种情况下,默认的行为都是向调用方抛出任何异常。有关消息传递网关,请参见错误处理,以了解如何引发异常以及如何配置网关将错误路由到错误通道的详细信息。当使用MessagingTemplate或直接发送到MessageChannel时,异常总是被抛给调用方。

当添加异步处理时,事情会变得相当复杂。例如,如果“channel”元素确实提供了一个“queue”子元素(在 Java&Annotations 配置中是QueueChannel),那么处理消息的组件在与发送方不同的线程中进行操作。当使用ExecutorChannel时也是如此。发送者可能已经将Message放入通道中,然后转移到其他东西上。通过使用标准的Exception抛出技术,无法将Exception直接抛回给发送方。相反,处理异步进程的错误要求错误处理机制也是异步的。

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

默认的errorChannel Bean 是由框架在内部创建的。但是,如果你想要控制设置,则可以定义自己的设置。下面的示例展示了如何在 XML 配置中定义错误通道,该配置由容量500的队列支持:

Java

@Bean
QueueChannel errorChannel() {
    return new QueueChannel(500);
}

XML

<int:channel id="errorChannel">
    <int:queue capacity="500"/>
</int:channel>
默认的错误通道是PublishSubscribeChannel

这里需要理解的最重要的一点是,基于消息传递的错误处理仅适用于在TaskExecutor中执行的 Spring 集成任务引发的异常。这不适用于在与发送方相同的线程中操作的处理程序引发的异常(例如,通过DirectChannel,如本节前面所述)。

当计划的 Poller 任务执行中出现异常时,这些异常被包装在ErrorMessage实例中,并发送到“errorchannel”。
这是通过将MessagePublishingErrorHandler注入全局taskScheduler Bean 中来完成的。
对于任何自定义MessagePublishingErrorHandler,都建议使用MessagePublishingErrorHandler。如果错误处理仍然必须使用标准的“errorchannel”集成流逻辑来完成。
在这种情况下可以使用已注册的integrationMessagePublishingErrorHandler Bean。

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

从版本 4.3.10 开始, Spring 集成提供了ErrorMessagePublisherErrorMessageStrategy。你可以将它们用作发布ErrorMessage实例的通用机制。你可以在任何错误处理场景中调用或扩展它们。ErrorMessageSendingRecoverer将这个类扩展为RecoveryCallback实现,该实现可用于重试,例如[RequestHandlerRetryAdvice](./handler-advice.html#retry-advice)。ErrorMessageStrategy用于基于提供的异常和AttributeAccessor上下文构建ErrorMessage。它可以被注入任何MessageProducerSupportMessagingGatewaySupportrequestMessage存储在ErrorMessageUtils.INPUT_MESSAGE_CONTEXT_KEY上下文中。ErrorMessageStrategy可以使用requestMessage作为它创建的ErrorMessage属性的originalMessageDefaultErrorMessageStrategy就是这么做的。

从版本 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配置](./channel.html#channel-configuration-pubsubchannel)(如果你手动配置全局errorChannel)。