My main goal is for pooling a httpclient in silent or somehow, and i'd like to call a method to give me a httpclient...because i think its too resource usage for every rest call add a new httpclient instance and set the things and so on...is there best practice for it?
If you refer to Apache HTTP client then you may follow the steps below:
Initiate Apache HTTP client only once, since it is thread safe you can safely reuse it. If you use Spring then it should be safe to store it in the Spring Context as a Bean. See following link for the thread-safety.
Despite the fact that HTTP client instance itself is not pooled (since you gonna use single instance of it) what you can do to increase the performance is configuring pooled connection manager on the HTTP client. See following link for the details.
(search for the 'Pooling connection manager' on that page) . The actual code should be something similar to the snippet bellow :
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
// Increase max total connection to 200
cm.setMaxTotal(200);
// Increase default max connection per route to 20
cm.setDefaultMaxPerRoute(20);
// Increase max connections for localhost:80 to 50
HttpHost localhost = new HttpHost("localhost", 80);
cm.setMaxPerRoute(new HttpRoute(localhost), 50);
CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(cm)
.build();
Hope this helps .
Related
Currently I'm replacing existing org.apache.http.* http client library with JDK-11's new Http library. There are many exciting new features, however I've not found anything on "how to set RetryRequestHandler in new HTTPClient". Code snippet of previous Apache HttpClient builder:
...
...
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create()
.setDefaultRequestConfig(config)
.setConnectionManager(connectionManager)
if(retryCount > 0) {
httpClientBuilder.setRetryHandler(new RetryRequestHandler(retryCount, url));
}
if (proxyHost) {
HttpHost proxy = new HttpHost(proxyHost, proxyPort, "http");
httpClientBuilder.setProxy(proxy);
}
...
...
Here RetryRequestHandler is an extension of DefaultHttpRequestRetryHandler
public class RetryRequestHandler extends DefaultHttpRequestRetryHandler{...}
There is no option to set retry in java-11's new HttpClient. Is there any workaround to do so?
Spring has broken retry out of Spring Batch into a separate, standalone library that you can use (albeit inside a Spring project). It will allow you to add a retry policy to a method calling the new HTTP client. See docs below:
https://github.com/spring-projects/spring-retry
That's the closest thing I know for this situation. If you want to roll your own, you could also accomplish the same thing with aspects. I think the Spring library is cleaner because you can let the library handle the details of retry. They also have a powerful set of APIs for different retry policies, including exponential backoff, etc.
The java.net.http HttpClient will retry idempotent requests (GET/HEAD) once by default. This is typically useful on HTTP/1.1 long live connections where the server side might arbitrarily decide that the connection has remained idle for too long, and closes it at the same time that the client takes it out of the pool and starts sending a new request.
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
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
Having the client wait until it has a complete upload to perform before starting the POST.
Splitting the uploads into smaller, more frequent uploads.
Use a protocol other than HTTP.
The approach above with Socket worked beautifully with a reset of tcp_keepalive_intvl value below the AWS Network Load Balancer timeout. Using both, reset the NLB tcp idle timeout that allowed java hour+ connections.
Sometimes, if the configuration is overwritten, the configuration does not take effect.My initial modification of setDefaultSocketConfig in buildClient didn't take effect.Because it is overwritten by getConnectionManager()
public CloseableHttpClient buildClient() throws Exception {
HttpClientBuilder builder = HttpClientBuilder.create()
.setDefaultSocketConfig(SocketConfig.custom().setSoKeepAlive(true).build()) // did not work
.setConnectionManager(getConnectionManager())
.setRetryHandler(getRequestRetryHandler())
.setConnectionReuseStrategy(getConnectionReuseStrategy())
.setDefaultConnectionConfig(getConnectionConfig())
.setDefaultRequestConfig(getRequestConfig())
.setDefaultHeaders(getDefaultHeaders())
.setDefaultCredentialsProvider(getDefaultCredentialsProvider())
.disableContentCompression() // gzip is not needed. Use lz4 when compress=1
.setDefaultCookieStore(cookieStoreProvider.getCookieStore(properties))
.disableRedirectHandling();
String clientName = properties != null ? properties.getClientName() : null;
if (!Utils.isNullOrEmptyString(clientName)) {
builder.setUserAgent(clientName);
}
return builder.build();
And then I move the config to getConnectionManager(),and it work.
private PoolingHttpClientConnectionManager getConnectionManager()
throws CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException, IOException {
RegistryBuilder<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory());
if (properties.getSsl()) {
HostnameVerifier verifier = "strict".equals(properties.getSslMode()) ? SSLConnectionSocketFactory.getDefaultHostnameVerifier() : NoopHostnameVerifier.INSTANCE;
registry.register("https", new SSLConnectionSocketFactory(getSSLContext(), verifier));
}
//noinspection resource
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(
registry.build(),
null,
null,
new IpVersionPriorityResolver(),
properties.getTimeToLiveMillis(),
TimeUnit.MILLISECONDS
);
connectionManager.setDefaultMaxPerRoute(properties.getDefaultMaxPerRoute());
connectionManager.setMaxTotal(properties.getMaxTotal());
connectionManager.setDefaultConnectionConfig(getConnectionConfig());
connectionManager.setDefaultSocketConfig(SocketConfig.custom().setSoKeepAlive(true).build());
return connectionManager;
}
I have a Java program which use Axis2 for consuming a web service. Now I want to set connection timeout and socket timeout for that client.
I have searched and found that there are two possible ways to do that.
1st method
https://singztechmusings.wordpress.com/2011/05/07/how-to-configure-timeout-duration-at-client-side-for-axis2-web-services/
stub._getServiceClient().getOptions().setProperty(HTTPConstants.SO_TIMEOUT, new Integer(timeOutInMilliSeconds));
stub._getServiceClient().getOptions().setProperty(HTTPConstants.CONNECTION_TIMEOUT, new Integer(timeOutInMilliSeconds));
2nd method
https://community.emc.com/docs/DOC-44893
MultiThreadedHttpConnectionManager multiThreadedHttpConnectionManager = new MultiThreadedHttpConnectionManager();
HttpConnectionManagerParams params = new HttpConnectionManagerParams();
params.setSoTimeout(timeOutInMilliSeconds);
params.setConnectionTimeout(timeOutInMilliSeconds);
multiThreadedHttpConnectionManager.setParams(params);
HttpClient httpClient = new HttpClient(multiThreadedHttpConnectionManager);
stub._getServiceClient().getServiceContext().getConfigurationContext().setProperty(HTTPConstants.CACHED_HTTP_CLIENT, httpClient);
Does anyone know what is the difference between those two methods?
Edit :
Also I have found the following method which is used to set timeout. Confusion at it's best. :(
stub._getServiceClient().getOptions().setTimeOutInMilliSeconds(180000);
I use PoolingHttpClientConnectionManager for sending multiple GET/POST requests in parallel to following services:
(1) http://localhost:8080/submit
(2) http://localhost:8080/query
Both services are heavily used but the first service (1) has a higher priority.
I need to set setMaxPerRoute for service (1) so that it will consume 80% of available connections.
The rest 20% limit will be allocated for the rest requests with longer timeouts (including service (2)). Here is my code:
...
PoolingHttpClientConnectionManager httpClientManager =
new PoolingHttpClientConnectionManager();
httpClientManager.setMaxTotal(10);
httpClientManager.setDefaultMaxPerRoute(2);
HttpHost httpHost = new HttpHost("http://localhost/submit",8080);
HttpRoute submitRoute = new HttpRoute(httpHost);
httpClientManager.setMaxPerRoute(submitRoute, 8);
...
The problem is that HttpHost apparently cannot be the same to differentiate among routes. In fact, two URL-s have the same host (http://localhost:8080), but different request pages. In the result, both services are used the same resources.
Is there any way to implement such a limitation for the same host?
Thanks for help.
After suggestions from my co-workers I've found the solution.
We need to control the maximum number of connections so that when requesting URL (1) we have the pool with max 20 connections, whereas the rest type of requests including request (2) we have the pool with max 2 connections.
It can be solved by creating two different HttpClient objects each one has its own PoolingHttpClientConnectionManager. The first manager is set with setMaxTotal=20, while the second with setMaxTotal=2.
Now each pool is has different limitations for the same domain.
I am using Apache RequestConfig to configure some timeouts on my HttpClient.
RequestConfig config = RequestConfig.custom()
.setConnectTimeout(timeout)
.setSocketTimeout(timeout)
.setConnectionRequestTimeout(timeout) // Can I leave this out..
.build();
CloseableHttpClient httpClient = HttpClients.custom()
//.setConnectionManager(connectionManager) // ..if I don't use this
.setDefaultRequestConfig(config)
.build();
Does it make any sense to call setConnectionRequestTimeout(timeout) even I don't have a custom Connection Manager / Pool set up?
As far as I understand, setConnectionRequestTimeout(timeout) is used to set the time to wait for a connection from the connection manager/pool.
Note that I am not setting a Connection Manager on the httpClient (see commented line).
connectionRequestTimeout happens when you have a pool of connections and they are all busy, not allowing the connection manager to give you a connection to make the request.
So, The answer to your question of:
Does it make any sense to call setConnectionRequestTimeout(timeout)
even I don't have a custom Connection Manager / Pool set up?
is YES.
This is because the default implementation has an internal connection pool.
So, yes it makes sense to specify a connection request timeout. Actually it is a good, safe practice.
Isuru's answer is mostly correct. The default connection manager is a PoolingHttpClientConnectionManager.
However, by default it will only have one connection in it's pool.
If you are using your HttpClient synchronously from the same thread you should never encounter a situation where the ConnectionRequestTimeout will take effect.
If you are using the HttpClient from multiple threads then you might want to set it, but you would probably also want to increase the pool size, among other things.
For single-threaded httpclient use it is safe to leave it out.