简体   繁体   English

Spring集成流程中的错误处理实践

[英]Error handling practices in spring integration flow

I have a spring integration flow that involves async execution, returning value from gateway to controller, continuing integration flow after returning a value. 我有一个Spring集成流程,涉及异步执行,从网关返回值到控制器,返回值后继续集成流程。

Here is the gateway: 这是网关:

@MessagingGateway
public interface GW {

    @Gateway(requestChannel = "f.input")
    Task input(Collection<MessengerIncomingRequest> messages);

}

And here is the flow: 这是流程:

@Bean
IntegrationFlow jFlow() {
        return IntegrationFlows.from(
        MessageChannels.executor("f.input", executor()))
        .split()
        .channel(MessageChannels.executor(executor()))
        .transform(transformer)
        .channel(routerChannel())
        .get();
}

@Bean
ThreadPoolTaskExecutor executor() {
        ThreadPoolTaskExecutor pool = new ThreadPoolTaskExecutor();
        ...
        return pool;
}

@Bean
MessageChannel routerChannel() {
        return MessageChannels
        .publishSubscribe("routerChannel", executor())
        .get();
}

@Bean
IntegrationFlow routerChannelFlow() {
        return IntegrationFlows
        .from(routerChannel())
        .publishSubscribeChannel(s -> s
        .subscribe(f -> f.bridge(null))
        .subscribe(process()))
        .get();
}

@Bean
IntegrationFlow process() {
        return f ->
        f.route(p -> p.getKind().name(),
        m -> m.suffix("Channel")
        .channelMapping(TaskKind.CREATE.name(), "create")
        ....
}

@Bean
IntegrationFlow createFlow() {
        return IntegrationFlows.from(
        MessageChannels.direct("createChannel"))
        .handle(routerService)
        .get();
}

How can I define an error handler for the whole flow? 如何为整个流程定义错误处理程序? What are the best practices? 什么是最佳做法? I know I can put a try/catch block for the gateway method call, but it will only catch exceptions that occur in jFlow flow for everything that comes before channel(routerChannel()) . 我知道我可以为网关方法调用设置一个try / catch块,但是它只会捕获jFlow流中出现的异常,这些异常发生在channel(routerChannel())

How can I handle errors for the rest of the flow? 如何处理其余流程的错误? Or for the entire flow? 还是整个流程?

UPDATE UPDATE

I added error handler for publishSubscribeChannel 我为publishSubscribeChannel添加了错误处理程序

@Bean
IntegrationFlow routerChannelFlow() {
    return IntegrationFlows
            .from(routerChannel())
            .publishSubscribeChannel(s -> s
                    .subscribe(f -> f.bridge(null))
                    .subscribe(process())
                    .errorHandler(errorHandler))
            .get();
}

but it does not seem to help, because in case of exception I get the following error: 但它似乎没有帮助,因为如果出现异常,我会收到以下错误:

cMessagingTemplate$TemporaryReplyChannel : Reply message received but the receiving thread has already received a reply:ErrorMessage [payload=org.springframework.messaging.MessageHandlingException:

and my error handler does not get called. 并且我的错误处理程序不会被调用。

UPDATE UPDATE

According to Gary's answer I tried this code: 根据Gary的回答,我尝试了这段代码:

@Bean
IntegrationFlow jFLow() {
    return IntegrationFlows.from(
            MessageChannels.executor("f.input", executor()))
            .split()
            .channel(MessageChannels.executor(executor()))
            .transform(transformer)
            .channel(routerChannel())
            .get();
}

@Bean
IntegrationFlow exceptionOrErrorFlow() {
    return IntegrationFlows.from(
            MessageChannels.direct("exceptionChannel"))
            .handle(errorHandler, "handleError")
            .get();
}

    @Bean
MessageChannel exceptionChannel() {
    return MessageChannels.direct("exceptionChannel")
            .get();
}

@Bean
IntegrationFlow process() {
        return f ->
        f.enrichHeaders((spec) ->
                    spec.header("errorChannel", "exceptionChannel", true))
        f.route(p -> p.getKind().name(),
        m -> m.suffix("Channel")
        .channelMapping(TaskKind.CREATE.name(), "create")
        ....
}

@MessagingGateway(errorChannel = "exceptionChannel")

After another edit I added exceptionChannel to the gateway, and moved enriching header to the second leg (async) of my flow. 在另一次编辑之后,我将exceptionChannel添加到网关,并将富集标题移动到我的流的第二段(异步)。 Still controller gets blocked if exception is throw in the synchronous part of the flow. 如果在流的同步部分抛出异常,则控制器被阻塞。

First of all, let me explain how the gateway works - it should help with understanding the solution below. 首先,让我解释网关的工作原理 - 它应该有助于理解下面的解决方案。

The request message gets a unique temporary reply channel which is added as the replyChannel header. 请求消息获得唯一的临时回复通道,该通道被添加为replyChannel标头。 Even if the gateway has an explicit replyChannel , that is simply bridged to the request's replyChannel - that's how the gateway correlates the reply to the request. 即使网关具有显式的replyChannel ,它也只是桥接到请求的replyChannel - 这就是网关如何将回复与请求相关联。

Now, the gateway also sets the request's errorChannel header to the same reply channel. 现在,网关还将请求的errorChannel标头设置为相同的回复通道。 That way, even if the flow is asynchronous, an exception can be routed back to the gateway and either thrown to the caller or routed to the gateway's error channel (if specified). 这样,即使流是异步的,也可以将异常路由回网关,并将其抛出到调用者或路由到网关的错误通道(如果指定)。 This routing is performed by a MessagePublishingErrorHandler which is wired into a ErrorHandlingTaskExecutor , which wraps your executor. 此路由由MessagePublishingErrorHandler执行,该MessagePublishingErrorHandler连接到ErrorHandlingTaskExecutor ,它包装您的执行程序。

Since you are returning a result to the gateway and then continuing; 因为您将结果返回到网关然后继续; that gateway interaction is "spent" and nothing will ever receive a message (including an exception) sent to the replyChannel header. 网关交互是“花费”的,没有任何东西会收到发送到replyChannel标头的消息(包括异常)。 Hence the log message you are seeing. 因此,您看到的日志消息。

So, one solution is to fix up the errorChannel header on the message sent to the independent flow. 因此,一种解决方案是在发送到独立流的消息上修复errorChannel头。 Use .enrichHeaders to replace (be sure to set overwrite to true) the errorChannel header that was set up by the gateway. 使用.enrichHeaders替换(确保将覆盖设置为true)网关设置的errorChannel标头。 This should be done as soon as possible in the flow so any exceptions will be routed to that channel (and then you can subscribe your error handler there). 这应该在流程中尽快完成,因此任何异常都将路由到该通道(然后您可以在那里订阅您的错误处理程序)。

An alternative solution is to wire up your own error handling executor, explicitly setting a defaultErrorChannel on its MessagePublishingErrorHandler and remove the errorChannel header. 另一种解决方案是连接您自己的错误处理执行程序,在其MessagePublishingErrorHandler上显式设置defaultErrorChannel并删除errorChannel标头。

The async error routing first looks for a header; 异步错误路由首先查找标头; if present, the error message is routed there; 如果存在,则错误消息在那里路由; if there's no header and the MPEH has no default error channel; 如果没有标题,MPEH没有默认的错误通道; the message will be routed to the default errorChannel to which (normally) there is a LoggingChannelAdapter subscribed. 消息将被路由到默认的errorChannel ,其中(通常)订阅了LoggingChannelAdapter The default errorChannel is a pub/sub channel so you can subscribe other endpoints to it. 默认的errorChannel是一个发布/订阅者通道,因此您可以为其订阅其他端点。

EDIT 编辑

You are changing the channel before the pub/sub. 您正在更改pub / sub之前的频道。

It's important to get at least one response to the gateway; 获得至少一个网关响应非常重要; you should leave the error channel alone on one leg of the pub/sub and update it on the second leg. 您应该将错误通道单独留在pub / sub的一条腿上,并在第二条腿上更新它。 That way, an exception on the first leg will be thrown to the caller (you can add an errorChannel to the gateway if you want to take some action there, such as routing to your exception handler). 这样,第一条腿上的异常将被抛给调用者(如果你想在那里采取一些动作,你可以向网关添加一个errorChannel ,例如路由到你的异常处理程序)。 You must only update the header on the second leg so that its exceptions go straight to your error handler. 您必须只更新第二个分支上的标题,以便它的异常直接发送到您的错误处理程序。

If you set the errorChannel on the gateway to your exceptionChannel then exceptions on both legs will go there. 如果您将网关上的errorChannel设置为exceptionChannel那么两条腿上的异常将会出现在那里。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM