New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Don't use soTimeout to actively timeout NIO connections [INT-4450] #8392
Comments
Michel Jung commented I just realized that Any way to reduce the TCP timeout when there's not ACK from the other side? |
Gary Russell commented NIO doesn't have a native I am not sure why it is not doing what you expect. With NIO, we use the Therein, if Now, it's an approximation; with a timeout of 10 seconds, it might take up to 19.999 seconds to timeout since the last read is likely to have occurred during the previous select so it might take 2 cycles to detect the timeout. On the client side, we look at the last send time, and explicitly wait for 2x the timeout since we might be expecting a reply; but I assume you are on the server side. If you are not seeing this behavior, a DEBUG log showing the unexpected behavior would be useful. We have a test case for an NIO read timeout here. |
Gary Russell commented I just wrote a quick app to verify... @SpringBootApplication
public class Int4450Application implements ApplicationListener<TcpConnectionCloseEvent> {
public static void main(String[] args) {
SpringApplication.run(Int4450Application.class, args).close();
}
@Override
public void onApplicationEvent(TcpConnectionCloseEvent event) {
System.out.println(event);
}
@Bean
public ApplicationRunner runner() {
return args -> {
Socket socket = SocketFactory.getDefault().createSocket("localhost", 1234);
System.out.println("sending foo");
socket.getOutputStream().write("foo\r\n".getBytes());
Thread.sleep(30_000);
socket.close();
};
}
@Bean
public IntegrationFlow flow(AbstractServerConnectionFactory connectionFactory) {
return IntegrationFlows.from(Tcp.inboundAdapter(connectionFactory))
.transform(Transformers.objectToString())
.handle(System.out::println)
.get();
}
@Bean
public AbstractServerConnectionFactory cf() {
TcpNioServerConnectionFactory cf = new TcpNioServerConnectionFactory(1234);
cf.setSoTimeout(10_000);
return cf;
}
} and
|
Gary Russell commented After re-reading your description, I think you don't want to timeout the socket after 10s but you want to detect that the peer has silently closed the socket (powered off) or a network element (router, firewall etc) has done so. The solution for that is See the HOW TO This keep alive time is set at the operating system/tcp stack level and is not configurable per socket. It is often quite a long time by default (2 hours on linux IIRC) but can be reduced. Socket keepalives are done at a lower layer in the stack and there is no application interaction, which solves your
comment. |
Gary Russell commented Closing due to inactivity; if SO_KEEPALIVE doesn't solve your problem, please comment and we'll reopen. |
Michel Jung opened INT-4450 and commented
Update: Save your time and read my comment first :)
I have an application with long-running TCP NIO connections.
Assume a client silently drops, e.g. because of loss of power. On the server side, the TCP connection will stay open for 15min until "Connection timed out".
Since this is too long for my use case, I tried setting
AbstractConnectionFactory.setSoTimeout(int)
to 10s. This value is then used forSocket.setSoTimeout
here:spring-integration/spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/connection/AbstractConnectionFactory.java
Lines 161 to 163 in 82e4679
The JavaDoc of
Socket.setSoTimeout
reads:So that's exactly what I want.
Next thing that happens is, my client gets dropped every 10s. How so? Well,
soTimeout
is also used to timeout active connections that haven't sent anything within that time:spring-integration/spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/connection/AbstractConnectionFactory.java
Lines 628 to 653 in 82e4679
To my understanding, these are two different cases that should be configured differently. With the current implementation, the only way I see to achieve what I need is to implement a PING-PONG mechanism in both, the client and the server. That seems totally unnecessary though since it's TCP.
Am I missing something or should this be fixed in spring-integration?
Affects: 5.0.4
The text was updated successfully, but these errors were encountered: