问题
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