异步网关中的异常处理

时间:2015-10-23 09:56:53

标签: spring-integration

我有一个网关实现如下。我在网关中定义了一个错误通道,以捕获整个SI流中的任何异常。 outputChannel将响应发送回网关。如您所见,使用拆分器和路由器创建的系统中有3个并行流。

在ErrorHandlingService中,我设置了相应的错误代码并将消息发送回aggregateDSLResponseChannel,以便整个流程顺利完成。我需要一个实现,其中整个流程在DSLFlowEndService中结束,即使存在异常,然后通过设置适当的响应对象将响应返回给调用者。原因是如果有连接请求说WLDP和IRIS并且说WLDP流失败但是IRIS成功了,我不想发送完整的响应作为失败。相反,我会向呼叫者发回适当的消息,提及部分成功(有失败和成功的详细信息)

为了测试,我在WLDPFlowEndService中引入了一个异常。异常来自错误通道,然后消息也会返回到aggregateDSLResponseChannel,然后返回到DSLFlowEndService。但是,响应不会发送回网关外部的呼叫者。基本上Spring集成流程永远不会完成,并且在配置后30秒后超时。以下是日志中的警告:

15:13:23.271 [dslParallelExecutor-2] WARN  org.springframework.integration.core.MessagingTemplate$TemporaryReplyChannel {} - Reply message being sent, but the receiving thread has already received a reply:[Payload=DSLResponseVO 
15:13:23.271 [dslParallelExecutor-2] DEBUG org.springframework.integration.channel.DirectChannel {} - postSend (sent=true) on channel 'outputChannel', message: [Payload=DSLResponseVO

**更新配置 - 一个有效(超级感谢Gary)**:

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/integration
       http://www.springframework.org/schema/integration/spring-integration.xsd
        http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">

<int:channel id="inputChannel"></int:channel>

<int:channel id="outputChannel"></int:channel>

<int:channel id="dslFlowInitiatorOutputChannel"/>

<int:channel id="routingChannel"/>

<int:channel id="wldpInputChannel">
    <int:dispatcher task-executor="wldpParallelExecutor" />
</int:channel>

<int:channel id="lcInputChannel">
    <int:dispatcher task-executor="lcParallelExecutor" />
</int:channel>

<int:channel id="irisInputChannel">
    <int:dispatcher task-executor="irisParallelExecutor" />
</int:channel>

<int:publish-subscribe-channel id="aggregateDSLResponseChannel"></int:publish-subscribe-channel>

<int:channel id="aggregateDSLOutputChannel">
    <int:dispatcher task-executor="dslParallelExecutor" />
</int:channel>

<!-- all thread pools to execute tasks in parallel -->
<task:executor id="dslParallelExecutor" pool-size="50" />
<task:executor id="wldpParallelExecutor" pool-size="25" />
<task:executor id="lcParallelExecutor" pool-size="25" />
<task:executor id="irisParallelExecutor" pool-size="25" />

<int:gateway id="dslServiceFacade" service-interface="com.mycompany.tss.ls.dsl.gateway.IDSLServiceFacade"  
    default-request-channel="inputChannel" default-reply-channel="outputChannel" error-channel="gatewayErrorChannel" async-executor="dslParallelExecutor">
    <int:method name="invoke" request-channel="inputChannel" request-timeout="5000" reply-timeout="3000000"/>
</int:gateway>

<int:service-activator input-channel="inputChannel"
    output-channel="dslFlowInitiatorOutputChannel" ref="dslFlowInitiatorService"
    method="invoke" id="dslFlowInitiator" />

<bean id="dslFlowInitiatorService" class="com.mycompany.tss.ls.dsl.service.DSLFlowInitiatorService" />

<int:splitter id="systemSplitter" input-channel="dslFlowInitiatorOutputChannel" method="split"
                output-channel="routingChannel" ref="systemMessageSplitter">
</int:splitter>

<bean id="systemMessageSplitter" class="com.mycompany.tss.ls.dsl.splitter.SystemMessageSplitter"/>

<int:router id="systemRouter" input-channel="routingChannel" default-output-channel="nullChannel" 
    expression="headers.get('systemId')">
    <int:mapping value="WLDP" channel="wldpInputChannel" />
    <int:mapping value="LC" channel="lcInputChannel" />
    <int:mapping value="IRIS" channel="irisInputChannel" />
</int:router>

<int:aggregator id="dslResponseAggregator" input-channel="aggregateDSLResponseChannel" output-channel="aggregateDSLOutputChannel"
    message-store="dslResponseMessageStore" correlation-strategy-expression="headers['requestId']" 
    send-partial-result-on-expiry="true">
</int:aggregator>

<int:service-activator input-channel="aggregateDSLOutputChannel"
    output-channel="outputChannel" ref="dslFlowEndService"
    method="invoke" id="dslFlowEndActivator" />

<bean id="dslFlowEndService" class="com.mycompany.tss.ls.dsl.service.DSLFlowEndService" />

<bean id="dslResponseMessageStore" class="org.springframework.integration.store.SimpleMessageStore" />

<bean id="dslResponseMessageStoreReaper" class="org.springframework.integration.store.MessageGroupStoreReaper">
    <property name="messageGroupStore" ref="dslResponseMessageStore" />
    <property name="timeout" value="4000" />
</bean>

<task:scheduled-tasks>
    <task:scheduled ref="dslResponseMessageStoreReaper" method="run" fixed-rate="10000" />
</task:scheduled-tasks>

<int:wire-tap pattern="*Channel" order="3" channel="wiretap"/>
<int:message-history/>

<int:logging-channel-adapter id="wiretap" level="DEBUG"/>


<!-- Error handling Service Activator to log exceptions and send the response -->
<int:service-activator id="errorHandlingServiceActivator" input-channel="gatewayErrorChannel"
    method="invoke" output-channel="aggregateDSLResponseChannel" ref="errorHandlingService">
</int:service-activator>

<!-- Error Handling Service class -->
<bean id="errorHandlingService" class="com.mycompany.tss.ls.dsl.service.ErrorHandlingService" />
<bean id="wldpErrorHandlingService" class="com.mycompany.tss.ls.dsl.service.ErrorHandlingService" />
<bean id="irisErrorHandlingService" class="com.mycompany.tss.ls.dsl.service.ErrorHandlingService" />

<int:channel id="wldpStartChannel"></int:channel>
<int:channel id="irisStartChannel"></int:channel>
<int:channel id="wldpErrorChannel"></int:channel>
<int:channel id="irisErrorChannel"></int:channel>
<int:channel id="wldpOutputChannel"></int:channel>
<int:channel id="irisOutputChannel"></int:channel>

<int:service-activator id="midFlowWLDPActivator" input-channel="wldpInputChannel" 
    ref="midFlowWLDPGateway" output-channel="aggregateDSLResponseChannel"/>

<int:service-activator id="midFlowIRISActivator" input-channel="irisInputChannel" 
    ref="midFlowIRISGateway" output-channel="aggregateDSLResponseChannel"/>

<int:gateway id="midFlowWLDPGateway" default-request-channel="wldpStartChannel" 
    service-interface="com.mycompany.tss.ls.dsl.gateway.IDSLWLDPFacade" error-channel="wldpErrorChannel" 
/>


<int:gateway id="midFlowIRISGateway" default-request-channel="irisStartChannel" 
    service-interface="com.mycompany.tss.ls.dsl.gateway.IDSLIRISFacade" error-channel="irisErrorChannel" 
/>


<int:service-activator id="errorHandlingWLDPServiceActivator" input-channel="wldpErrorChannel"
    method="invoke" ref="wldpErrorHandlingService">
</int:service-activator>

<int:service-activator id="errorHandlingIRISServiceActivator" input-channel="irisErrorChannel"
    method="invoke" ref="irisErrorHandlingService">
</int:service-activator>

<!-- Responsible for closing all the activities related to WLDP before sending response -->
<int:service-activator id="wldpFlowEndServiceActivator" input-channel="wldpStartChannel"
    method="invoke" ref="wldpFlowEndService">
</int:service-activator>

<bean id="wldpFlowEndService" class="com.mycompany.tss.ls.dsl.service.wldp.WLDPFlowEndService" />

<!-- Responsible for closing all the activities related to IRIS before sending response -->
<int:service-activator id="irisFlowEndServiceActivator" input-channel="irisStartChannel"
    method="invoke">
</int:service-activator>


<bean id="irisFlowEndService" class="com.mycompany.tss.ls.dsl.service.iris.IrisFlowEndService" />

ErrorHandlingService:

公共类ErrorHandlingService {

Logger logger = LoggerFactory.getLogger(ErrorHandlingService.class);

public Message<DSLResponseVO> invoke(Message<?> requestMessage) {

    Object exception = requestMessage.getPayload();
    if(exception instanceof MessagingException){
        DSLResponseVO responseVO = new DSLResponseVO();
        logger.error("Exception has occurred: {}", ((MessagingException)exception).getMessage());
        logger.error("Exception Stacktrace is: ", exception);
        responseVO.setRequestId((String)((MessagingException)exception).getFailedMessage().getHeaders().get("requestId"));
        responseVO.setSystemId((String)((MessagingException)exception).getFailedMessage().getHeaders().get("systemId"));
        responseVO.setErrorCode(DSLErrorConstants.FAILURE_CODE);
        responseVO.setErrorMessage(DSLErrorConstants.FAILURE_DESC);

        Message<DSLResponseVO> failedMessage = MessageBuilder.withPayload(responseVO)
                //.copyHeadersIfAbsent(((MessagingException)exception).getFailedMessage().getHeaders())
                .setHeaderIfAbsent("requestId", (String)((MessagingException)exception).getFailedMessage().getHeaders().get("requestId"))
                .setHeaderIfAbsent("systemId", (String)((MessagingException)exception).getFailedMessage().getHeaders().get("systemId"))
                .setHeaderIfAbsent(MessageHeaders.SEQUENCE_NUMBER, (Integer)((MessagingException)exception).getFailedMessage().getHeaders().get(MessageHeaders.SEQUENCE_NUMBER))
                .setHeaderIfAbsent(MessageHeaders.SEQUENCE_SIZE, (Integer)((MessagingException)exception).getFailedMessage().getHeaders().get(MessageHeaders.SEQUENCE_SIZE))
                .setHeaderIfAbsent(MessageHeaders.REPLY_CHANNEL, ((MessagingException)exception).getFailedMessage().getHeaders().get(MessageHeaders.REPLY_CHANNEL))
                .build();

        return failedMessage;
    }

    return null;
}

}

1 个答案:

答案 0 :(得分:0)

您无法向网关发送多个回复;您需要将结果(或失败)聚合到单个回复消息中。

每个子流都需要将结果或错误发送到聚合器。

每个子流都需要一个错误流。如果使用队列通道而不是执行程序通道,则可以在轮询器上放置错误通道。

使用执行程序通道时,您需要一个中流网关来添加错误流(引用<gateway/>的服务激活器)。