Best practice for handling low-level socket error in spring integration?

落花浮王杯 提交于 2019-12-08 07:26:44

问题


Hi I'm here again asking for spring integration's tcp-ip related question.

Suppose I have a dynamically configured tcp-ip connection factory:

<int-ip:tcp-connection-factory id="chatRoomTcpIpClientFactory"
    type="client"
    host="${host}"
    port="${port}"
    single-use="false"
    using-nio="false"
    so-keep-alive="false"
    so-timeout="${timeout}"
    serializer="${seri-deseri}"
    deserializer="${seri-deseri}"/>

Here, the host & port are both unknown at the beginning. Sometimes the program cannot grantee that the host & port are valid. Say there is an ip/port 8.8.8.8:12345 which is not open on the destination server. Now the spring framework throws:

2015-08-13 22:07:52.906 ERROR 24479 --- [nio-8080-exec-1] o.s.i.ip.tcp.TcpSendingMessageHandler    : Error creating connection
java.net.ConnectException: Connection refused

Suppose that my business logic relies heavily on the tcp-ip connection and the program wants to do some error-handling to this specific socket connection error. (e.g. write the error to redis cache, re-connect using another ip/port, etc...)

I've looked for solutions and now I know that the "errorChannel" is not triggered on such low-level exception. The same applies to the tcp-connection-event-inbound-channel-adapter.

After some googling, it seems that the "advice" inside the factory bean or TcpConnectionInterceptorSupport with the chainFactory is the way to go... But I'm still rather confused. It's very common for the tcp-connection to fail. Is there any simpler method to handle connection exceptions?


Edit: I delved into the source code, and now I added a try-catch around my method. It works now. As for the tcp-connection-event-inbound-channel-adapter, it still does not work. It seems that the socket creation exception is not published...

The exception is picked up by TcpSendingMessageHandler when the service tries to send a message through the socket...


Edit 2: Following the stack trace:

2015-08-14 10:31:53.694 ERROR 2531 --- [nio-8080-exec-1] o.s.i.ip.tcp.TcpSendingMessageHandler    : Error creating connection

java.net.ConnectException: Connection refused
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:345)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
at java.net.Socket.connect(Socket.java:589)
at java.net.Socket.connect(Socket.java:538)
at java.net.Socket.<init>(Socket.java:434)
at java.net.Socket.<init>(Socket.java:211)
at javax.net.DefaultSocketFactory.createSocket(SocketFactory.java:271)
at org.springframework.integration.ip.tcp.connection.TcpNetClientConnectionFactory.createSocket(TcpNetClientConnectionFactory.java:76)
at org.springframework.integration.ip.tcp.connection.TcpNetClientConnectionFactory.buildNewConnection(TcpNetClientConnectionFactory.java:49)
at org.springframework.integration.ip.tcp.connection.AbstractClientConnectionFactory.obtainNewConnection(AbstractClientConnectionFactory.java:114)
at org.springframework.integration.ip.tcp.connection.AbstractClientConnectionFactory.obtainConnection(AbstractClientConnectionFactory.java:77)
at org.springframework.integration.ip.tcp.connection.AbstractClientConnectionFactory.getConnection(AbstractClientConnectionFactory.java:67)
at org.springframework.integration.ip.tcp.connection.AbstractClientConnectionFactory.getConnection(AbstractClientConnectionFactory.java:31)
at org.springframework.integration.ip.tcp.TcpSendingMessageHandler.obtainConnection(TcpSendingMessageHandler.java:72)
at org.springframework.integration.ip.tcp.TcpSendingMessageHandler.doWrite(TcpSendingMessageHandler.java:144)
at org.springframework.integration.ip.tcp.TcpSendingMessageHandler.handleMessageInternal(TcpSendingMessageHandler.java:120)
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:78)
at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:116)
at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:101)
at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:97)
at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:77)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:287)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:245)
at (My service call)

Here, in TcpNetClientConnectionFactory.buildNewConnecton(),

it is written as:

protected TcpConnectionSupport buildNewConnection() throws IOException, SocketException, Exception {
    Socket socket = createSocket(this.getHost(), this.getPort());  //<--- Where exception is thrown
    setSocketAttributes(socket);
    TcpConnectionSupport connection = new TcpNetConnection(socket, false, this.isLookupHost(),
            this.getApplicationEventPublisher(), this.getComponentName());
    connection = wrapConnection(connection);
    initializeConnection(connection, socket);
    this.getTaskExecutor().execute(connection);
    this.harvestClosedConnections();
    return connection;
}

Hence, the TcpConnectionSupport is not even reached. The event-driven adapter cannot deal with the exception occurred during socket creation. This is why I struggled so long for an answer.

In TcpSendingMessageHandler,

protected TcpConnection obtainConnection(Message<?> message) {
    TcpConnection connection = null;
    Assert.notNull(this.clientConnectionFactory, "'clientConnectionFactory' cannot be null");
    try {
        connection = this.clientConnectionFactory.getConnection();
    }
    catch (Exception e) {
        logger.error("Error creating connection", e);
        throw new MessageHandlingException(message, "Failed to obtain a connection", e);
    }
    return connection;
}

Here the error is simply logged and a new exception is thrown again.


回答1:


Please, take a look to the TcpConnectionEvent hierarchy, especially TcpConnectionExceptionEvent. And investigate the places where it is emitted by the TcpConnection implementations.

Please, find more information on the matter in the Reference Manual.




回答2:


When the socket is closed, the event TcpConnectionFailedEvent is triggered (it extends org.springframework.integration.ip.tcp.connection.IpIntegrationEvent).

Here is how to use it:

  @EventListener
  public void handleEvent(TcpConnectionFailedEvent event) {
    // handle the event
  }


来源:https://stackoverflow.com/questions/31992821/best-practice-for-handling-low-level-socket-error-in-spring-integration

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!