Spring Integration tcp-outbound-channel-adapter,用于检测连接丢失和自动重新连接

时间:2015-09-16 16:51:18

标签: spring-integration

我试图与此处完成的事情完全相反:

How do I create a tcp-inbound-gateway which detects connection loss and auto-reconnects?

我有一组从示例应用程序中获取的协作客户端tcp适配器。通常,应用程序将通过共享连接向第三方发送大量客户端请求,但有时候根本没有请求通过。

因此,第三方要求我每隔2分钟发送一次ping消息。如果在预定义的时间段内没有收到对ping的响应(可能比其他呼叫的超时短,我应该终止连接并重新连接。

我的第一个想法是创建一个每2分钟发送一次ping的计划任务,但我不确定如何终止连接并从任务内部重新建立连接。我正在考虑的另一个选择是使用连接拦截器并为请求和响应计时,但这看起来并不正确。我是SI的新手,所以任何正确方向的推动都会有所帮助。

我刚才的另一个是将tcp-outbound-channel-adapter自动装入作业并调用retryConnection()

<converter>
    <beans:bean class="org.springframework.integration.samples.tcpclientserver.ByteArrayToStringConverter" />
</converter>

<!-- Given we are looking for performance, let's use
     the most performant wire protocol. -->

<beans:bean id="fastestWireFormatSerializer" class="org.springframework.integration.ip.tcp.serializer.ByteArrayLengthHeaderSerializer">
    <beans:constructor-arg value="1" />
</beans:bean>

<!-- Client side -->

<gateway id="gw"
    service-interface="org.springframework.integration.samples.tcpclientserver.SimpleGateway"
    default-request-channel="input" />

<ip:tcp-connection-factory id="client"
    type="client"
    host="localhost"
    port="${availableServerSocket}"
    single-use="false"
    serializer="fastestWireFormatSerializer"
    deserializer="fastestWireFormatSerializer"
    so-timeout="10000" />

<publish-subscribe-channel id="input" />

<ip:tcp-outbound-channel-adapter id="outAdapter.client"
    order="2"
    channel="input"
    connection-factory="client" /> <!-- Collaborator -->

<!-- Also send a copy to the custom aggregator for correlation and
     so this message's replyChannel will be transferred to the
     aggregated message.
     The order ensures this gets to the aggregator first -->
<bridge input-channel="input" output-channel="toAggregator.client"
        order="1"/>

<!-- Asynch receive reply -->
<ip:tcp-inbound-channel-adapter id="inAdapter.client"
    channel="toAggregator.client"
    connection-factory="client" /> <!-- Collaborator -->

<!-- dataType attribute invokes the conversion service, if necessary -->
<channel id="toAggregator.client" datatype="java.lang.String" />

<aggregator input-channel="toAggregator.client"
    output-channel="toTransformer.client"
    correlation-strategy-expression="payload.substring(0,3)"
    release-strategy-expression="size() == 2" />

<transformer input-channel="toTransformer.client"
    expression="payload.get(1)"/> <!-- The response is always second -->

更新

我正在使用SI 3.0.7.RELEASE

我在日志中看到的唯一例外是:

22:02:38.956 ERROR [pool-1-thread-1] [org.springframework.integration.ip.tcp.connection.TcpNetConnection]读取异常localhost:4607:39129:464bd042-dd9c-4639-8d1d-cdda61dc988a SocketTimeoutException:读取超时

这是当clientFactory上的so-timeout设置为10秒时,我强制服务器休眠15秒。没有任何东西返回到SimpleGateway调用。它只是坐在那里等待永远。

使用上面列出的配置的示例代码:

String input = "ping";
                String result = null;
                System.out.println("Sending message: " + input);
                try{
                    result = gateway.send(input);
                }catch(Exception e){
                    System.out.println("There was an exception sending the message");
                }
                System.out.println("response: " + result);

输出:

发送消息:测试 22:12:17.093 ERROR [pool-1-thread-1] [org.springframework.integration.ip.tcp.connection.TcpNetConnection]读取异常localhost:4607:39232:fe90d394-edc3-440a-ab68-34e7162db6ec SocketTimeoutException:Read超时

网关呼叫永远不会返回。

1 个答案:

答案 0 :(得分:0)

new component in 4.2 called Thread Barrier

这允许您等待一些(可配置的)时间,直到发生异步事件。

有关示例,请参阅the barrier sample

ping回复将会释放&#39;等待的线索;在超时的情况下,您可以在处理程序上使用请求处理程序建议来捕获异常并采取您需要的操作(您可以从工厂获取打开的连接列表并通过id关闭它们。)

BTW,我们可能会重写该样本以使用屏障而不是聚合器。 实际上没有必要重新打开连接(除非您预期新连接上的未经请求的响应);它将在下一次发送时打开。

修改

我需要查看更多日志;我刚试了一下,得到了以下内容:

2015-09-28 09:05:53,995 [pool-2-thread-1] ERROR: org.springframework.integration.ip.tcp.connection.TcpNetConnection - Read exception localhost:5678:51323:f3537a39-feb4-4237-9508-7e67c1b79654
java.net.SocketTimeoutException: Read timed out
...
2015-09-28 09:05:53,996 [pool-2-thread-1] TRACE: org.springframework.integration.ip.tcp.TcpOutboundGateway - onMessage: localhost:5678:51323:f3537a39-feb4-4237-9508-7e67c1b79654([Payload=java.net.SocketTimeoutException: Read timed out][Headers={timestamp=1443445553996, id=0b3fae63-f431-c09c-ac4e-26ce4b012b6b, ip_connectionId=localhost:5678:51323:f3537a39-feb4-4237-9508-7e67c1b79654}])
2015-09-28 09:05:53,996 [main] DEBUG: org.springframework.integration.ip.tcp.TcpOutboundGateway - second chance
2015-09-28 09:05:55,998 [main] ERROR: org.springframework.integration.ip.tcp.TcpOutboundGateway - Tcp Gateway exception
org.springframework.integration.MessagingException: Exception while awaiting reply
...
Caused by: java.net.SocketTimeoutException: Read timed out
...

然而,它在4.2(和一次性套接字)中被打破,所以我打开了JIRA Issue。但你的不是一次性的,所以我希望得到与我相同的结果。

编辑2

Skipping a socket timeout because we have a recent send localhost:4607:26489:926b1c1f-49cc-4f44-b20d-78f2e94e82d1

我之前应该提到这一点。

因为连接工厂支持完全异步消息传递,所以我们可能必须等待2次超时才能关闭套接字并将异常传播到网关线程。

因此推理(使用你的10秒so-timeout):

  1. t+0创建的连接;读取线程启动并在套接字中被阻塞(不可中断)。
  2. t+9发送的消息。
  3. t+10读取时间。
  4. 使用此超时为时尚早,所以我们忽略它,因为在最后10秒内发送了一次。
  5. t+20再次阅读时间。
  6. 异常传播给调用者。
  7. 平均而言,网关线程将在so-timeout * 1.5处获得超时,但它将在so-timeoutso-timeout * 2的范围内。

    编辑3

    我无法从日志中告诉您为什么没有在您的案例中传播。

    我刚刚将this branch上的tcp-client-server示例恢复为3.0.7,并更改了测试以强制超时(跳过一次),这对我来说都很好。

    我们需要弄清楚你和我的案子有什么不同。

    您可以在the last commit on that branch中看到我对示例所做的更改。

    如果您能够提出类似的测试用例来重现问题,我可以帮助您进行调试。