My Java application which resides in AWS private subnet connects to an http server via AWS Nat gateway. I am calling a POST request via HttpClient
to the HTTP server. That request will take more than 10 minutes to complete. I have configured a socket time out and connection timeout of 1 hour as this this a background task . But the intermediate AWS NAT gateway will send back a RST packet after 300 secs [5 mins] and cause the connection to get resetted , there is no way i can increase the NAT gateway timeout. So i need to handle the problem from my application side.
My strategy is to use a TCP keep alive time which will send a packet say every 240 secs to keep the connection active. I have configured this as below
CloseableHttpClient httpClient = HttpClients.createDefault()
HttpParams params = httpClient.getParams();
HttpConnectionParams.setConnectionTimeout(params, 3600000); //connection Timeout
HttpConnectionParams.setSoTimeout(params, 3600000); // Socket Time out
HttpConnectionParams.setSoKeepalive(params, true); //Enable Socket level keep alive time
and then call the post request via execute method
HttpPost post = new HttpPost("http://url");
HttpResponse response = httpClient.execute(post);
Since I am using a Linux system I have configured the server with following sysctl values:
sysctl -w net.ipv4.tcp_keepalive_time=240
sysctl -w net.ipv4.tcp_keepalive_intvl=240
sysctl -w net.ipv4.tcp_keepalive_probes=10
But while executing the program the keep alive is not enabled and connection fails as previous.
I have checked this with netstat -o option and as shown below keep alive is off
tcp 0 0 192.168.1.141:43770 public_ip:80 ESTABLISHED 18134/java off (0.00/0/0)
Is there any way i can set TCP keep alive from java code using httpclient . Also I can see HttpConnectionParams
are deprecated. But I couldn't find any new class which can set keep alive
The keepalive concept is very simple: when you set up a TCP connection, you associate a set of timers. Some of these timers deal with the keepalive procedure. When the keepalive timer reaches zero, you send your peer a keepalive probe packet with no data in it and the ACK flag turned on.
The Keep-Alive general header allows the sender to hint about how the connection may be used to set a timeout and a maximum amount of requests. Note: Set the Connection header to "keep-alive" for this header to have any effect.
The Keep-Alive duration determines whether or not the connection is idle, in fact - if the Keep-alive strategy says to keep connections alive for 10 seconds, and we receive responses from the server every 2 seconds, the connection will be kept alive for 10 seconds after the last successful response.
Keep-Alive is Optional If keep-alives are included, the application MUST be able to turn them on or off for each TCP connection, and they MUST default to off. It is extremely important to remember that ACK segments that contain no data are not reliably transmitted by TCP.
I have found a solution to the problem . Curious case is there is no way i can use some builder class in httpclient to pass socket keep alive . One method as i specified in the question is using HttpConnectionParams as below but this is not working and this class is now deprecated.
HttpParams params = httpClient.getParams();
HttpConnectionParams.setSoKeepalive(params, true);
So while checking apache http docs I can see that now connection parameters are passed to httpclient via RequestConfig class . Builders of this class provide solution to set connection_time_out and socket_time_out. But checking the socurce code of this I couldnt see an option to enable SocketKeepAlive which is what we want. So the only solution is directly creating a Socket using SocketBuilder class and pass that to the HttpClientBuilder.
Following is the working code
SocketConfig socketConfig = SocketConfig.custom().setSoKeepAlive(true).setSoTimeout(3600000).build(); //We need to set socket keep alive
RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(3600000).build();
CloseableHttpClient httpClient = HttpClientBuilder.create().setDefaultRequestConfig(requestConfig).
setDefaultSocketConfig(socketConfig).build();
HttpPost post = new HttpPost(url.toString());
HttpResponse response = httpClient.execute(post);
While executing above i can see that keep alive is properly set in the socket based on the sysctl values i set in linux kernel
tcp 0 0 localip:48314 public_ip:443 ESTABLISHED 14863/java keepalive (234.11/0/0)
If some one has a better solution to enable Socket Keep alive from Requestconfig class or any other high level builder class i am open to suggestions.
Keeping an HTTP connection open but inactive for a long period is a bad design choice. HTTP is a request-response protocol, with the implication that requests and responses are quick.
Holding a connection open holds resources. From the perspective of the server (and network firewalls and routers) a client that opens a connection and begins a request (A POST in your case) but does not send any bytes for a long period is indistinguishable from a client that will never send any more data, because it is faulty or malicious (conducting a DOS attack). The server (and network hardware) is right to conclude that the right thing to do is to shutdown the connection and reclaim the resources used for it. You are trying to fight against correct behaviour that occurs for good reasons. Even if you manage to workaround the TCP shutdown you will find other problems, such as HTTP server timeouts and database timeouts.
You should instead be reconsidered the design of communication between the two components. That is, this looks like an XY Problem. You might consider
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With