How do I set MaxConnPerRoute, ConnectionRequestTimeout, keepAliveStrategy in Spring WebFlux WebClient - httpclient

we have the following custom connection pooling implemented for RestTemplate.
PoolingHttpClientConnectionManager poolingConnManager =
new PoolingHttpClientConnectionManager();
poolingConnManager.setDefaultMaxPerRoute(restClientprops.getRestClientMaxPerRoutePool());
poolingConnManager.setMaxTotal(restClientprops.getRestClientMaxTotalPool());
HttpClientBuilder httpClientBuilder = HttpClients.custom()
.setConnectionManager(poolingConnManager)
.setKeepAliveStrategy(DefaultConnectionKeepAliveStrategy.INSTANCE)
.setMaxConnPerRoute(restClientprops.getRestClientMaxPerRoutePool())
.setMaxConnTotal(restClientprops.getRestClientMaxTotalPool());
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setConnectTimeout(restClientprops.getConnectTimeout());
requestFactory.setReadTimeout(restClientprops.getReadTimeout());
requestFactory.setConnectionRequestTimeout(restClientprops.getConnectionRequestTimeout());
requestFactory.setHttpClient(httpClientBuilder.build());
this.restTemplate = new RestTemplate(requestFactory);
I am changing it to WebClient implementation, and this is what I could come up with.
HttpClient httpClient = HttpClient
.create(ConnectionProvider.create("webclient-pool", restClientprops.getRestClientMaxTotalPool()))
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, restClientprops.getConnectTimeout())
.responseTimeout(Duration.ofMillis(restClientprops.getConnectionRequestTimeout()))
.doOnConnected(conn -> conn.addHandler(new ReadTimeoutHandler(restClientprops.getReadTimeout(), TimeUnit.MILLISECONDS)))
.keepAlive(true);
Per this URL https://github.com/reactor/reactor-netty/issues/1159
from what I understood connection request timeout is renamed to responseTimeOut in webclient httpclient. Is that accurate?
How should I set MaxConnPerRoute in webclient that is in the RestTemplate implementation?
Is keepAlive(true) accurate translation of setKeepAliveStrategy(DefaultConnectionKeepAliveStrategy.INSTANCE)
Appreciate your help.

Per this URL https://github.com/reactor/reactor-netty/issues/1159 from what I understood connection request timeout is renamed to responseTimeOut in webclient httpclient. Is that accurate?
Yes that's true. More about all timeouts that can be configured for the HttpClient you can find in the Reference Documentation.
How should I set MaxConnPerRoute in webclient that is in the RestTemplate implementation?
You can provide connection pool configuration per remote address (if that's what you mean with MaxConnPerRoute), see javadoc for forRemoteHost.
ConnectionProvider.builder("test")
.maxConnections(2) // default max connections
.forRemoteHost(<socket-address>,spec -> spec.maxConnections(1)) // max connections only for this socket address
.build();
Is keepAlive(true) accurate translation of setKeepAliveStrategy(DefaultConnectionKeepAliveStrategy.INSTANCE)
If you mean specifying whether the connection is persistent or not then YES this configuration has to be used. By default the connection IS persistent. If you mean SO_KEEPALIVE then NO and you have to use .option() configuration. You can find more in the Reference Documentation.
HttpClient.create()
.option(ChannelOption.SO_KEEPALIVE, true)
This configuration can be removed is you use the timeout settings provided by Reactor Netty:
.doOnConnected(conn -> conn.addHandler(new ReadTimeoutHandler(restClientprops.getReadTimeout(), TimeUnit.MILLISECONDS)))

Related

How to get the number of connections used (and free) to the MongoDB (from a client perspective)?

I'm posting the question here just to be sure I'm not barking on the wrong tree.
How to get the number of connections used (and free) to the MongoDB, but from a client perspective (eg. Java client), using the 4.x driver?
There are posts regarding using the serverStatus(Get the number of open connections in mongoDB using java), but it presumes having 'admin' access to the MongoDB. Using a 'regular user'(an db user with lower privileges (e.g access to only one database)) cannot run the serverStatus(). But this provides only a view from the server-side (there are N connections from IP x).
Other posts mentioned how to setup the connection pool size (eg. using the MongoClients.create​(MongoClientSettings settings) (see the 4.x API reference (https://mongodb.github.io/mongo-java-driver/4.0/apidocs/mongodb-driver-sync/com/mongodb/client/MongoClients.html)):
MongoCredential credential = MongoCredential.createCredential(
username,
"admin",
password.toCharArray());
MongoClient mongoClient = MongoClients.create(MongoClientSettings.builder()
.applyToClusterSettings(
builder -> builder.hosts(Arrays.asList(new ServerAddress(hostname, portNumber))))
.credential(credential)
.applyToConnectionPoolSettings(builder -> builder
.minSize(connectionPoolMinimumSize)
.maxSize(connectionPoolMaximumSize))
.readConcern(readConcern)
.readPreference(readPreference)
.writeConcern(writeConcern)
.build());
But none provided means to get the used and available connections the connection pool.
As mentioned by Oleg, using the ConnectionPoolListener would be a way, but that is available only in the 3.x drivers. The ConnectionPoolListener methods are marked as deprecated on 4.x (although it is still mentioned in the JMX Monitoring section (http://mongodb.github.io/mongo-java-driver/4.0/driver-reactive/reference/monitoring/).
You can use connection pool monitoring which is described here to keep track of connection states, and deduce the counts you are looking for.
I don't know if Java driver exposes the counters you are looking for as public APIs; many drivers don't.
Finally got this working:
created a custom connection pool listener, implementing the com.mongodb.event.ConnectionPoolListener...
public class CustomConnectionPoolListener implements ConnectionPoolListener {
...
}
... and having the stats counters updated on a store (accessible later)
#Override
public void connectionCreated(ConnectionCreatedEvent event) {
ConnectionPoolStatsPOJO cps = mongoConnectionPoolList.get(connectionPoolAlias);
cps.incrementConnectionsCreated();
mongoConnectionPoolList.put(connectionPoolAlias, cps);
}
attached this custom connection pool listener to the MongoClient connection:
ConnectionPoolListener customConnPoolListener = new CustomConnectionPoolListener(...); /* added some references in the */
...
MongoClientSettings mongoClientSettings = MongoClientSettings.builder()
.applicationName(applicationName)
.applyConnectionString(connURI)
.credential(credential)
.readConcern(readConcern)
.readPreference(readPreference)
.writeConcern(writeConcern)
.applyToConnectionPoolSettings(builder -> builder
.minSize(connectionPoolMinimumSize)
.maxSize(connectionPoolMaximumSize)
.addConnectionPoolListener(customConnPoolListener)
)
.retryWrites(true)
.retryReads(true)
.build();
...
MongoClient mongoClient = MongoClients.create(mongoClientSettings);
....
finally, to access the connection pool stats, just have to query out the store:
ConnectionPoolStatsPOJO connectionPoolStats = MongoDB_ConnectionPool_Repository.getInstance().getMongoConnectionPoolList().get(connectionPoolAlias);
Therefore, thanks to "#D. SM" for pointing to the right direction.

Issue with HttpClient in AEM

I have an HttpClient code written that is from org.apache.commons.httpclient package.
In that I am setting connection time and socket time out this way.
final HttpClient http = new HttpClient(this.connectionManager);
http.getParams().setParameter("http.connection.timeout", this.connectionTimeout);
http.getParams().setParameter("http.socket.timeout", this.socketTimeout);
Now the Adobe Cloud has raised issue that timeout is not being set(which is not true).
They suggested to set timeouts using
#Reference
private HttpClientBuilderFactory httpClientBuilderFactory;
public void doThis() {
HttpClientBuilder builder = httpClientBuilderFactory.newBuilder();
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(5000)
.setSocketTimeout(5000)
.build();
builder.setDefaultRequestConfig(requestConfig);
HttpClient httpClient = builder.build();
// do something with the client
}
Refer Link
But HttpClientBuilderFactory does not belong to **org.apache.commons.httpclient it belongs to org.apache.http.client**
And always returns Closable Http client.
How do I resolve this security issue? Can I add an annotation for exception? Or will I have to rewrite all my code?
This issue is with Adobe Experience Manager 6.5 instance.
Is it probably because you are not setting the right timeout parameter?
You are setting the property http.connection.timeout which is not available in the class org.apache.commons.httpclient.params.HttpClientParams.
http.getParams() returns an instance of HttpClientParams which has the socket timeout and connection manager timeout but not a connection timeout. You could probably use the constant HttpClientParams.CONNECTION_MANAGER_TIMEOUT to set a timeout for the connection manager?
On the other hand, the property http.connection.timeout is available for the class HttpConnectionParams.
Constant field values reference
The problem is Adobe has two versions of HttpClient the old 3.x that has package structure org.apache.commons.httpclient.HttpClient and the one that HttpClientBuilderFactory gives out that is 4.x org.apache.http.Httpclient.
I was breaking my head around this. Finally we were left with two options...
1) Rewrite all our commons http api(3.x) to the newer version of apache.http (4.x) that has the methods setTimeout and setConnectionTimeout
OR
2)#SuppressWarnings("CQRules:ConnectionTimeoutMechanism")
We chose Option number 2 as the effort arround this was huge and we are planning to go live soon.

Will setDefaultRequestConfig method override system properties - CloseableHttpClient

I am using the following code to make a Http request.
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(10000)
.setConnectionRequestTimeout(10000)
.setSocketTimeout(300000)
.build();
CloseableHttpClient httpClient = HttpClientBuilder.create().useSystemProperties().setDefaultRequestConfig(requestConfig).build();
My simple question, will the method setDefaultRequestConfig remove all the system properties and will keep only the properties given above OR will it override only the given properties and keep the other system properties while making the HTTP request.
System properties that HttpClientBuilder can optionally take into consideration are as follows
ssl.TrustManagerFactory.algorithm
javax.net.ssl.trustStoreType
javax.net.ssl.trustStore
javax.net.ssl.trustStoreProvider
javax.net.ssl.trustStorePassword
ssl.KeyManagerFactory.algorithm
javax.net.ssl.keyStoreType
javax.net.ssl.keyStore
javax.net.ssl.keyStoreProvider
javax.net.ssl.keyStorePassword
https.protocols
https.cipherSuites
http.proxyHost
http.proxyPort
http.nonProxyHosts
http.keepAlive
http.maxConnections
http.agent
Request level configuration have no effect on any of those settings with an exception of proxy host and port. Proxy setting at the request level will override those at the system level.

Zuul connection pool size

I have a question about configuring the size of the connection pool when using Zuul by itself, and not using Ribbon or other Netflix components.
We have a system that uses Zuul to proxy requests to a Mule server. We are only using Zuul and not Ribbon. We have defined 4 routes that call the Mule services. One of these services is long running, probably around 3 seconds per call.
When we load the system with 40 simultaneous users we get this error
org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection from pool
at org.apache.http.impl.conn.tsccm.ConnPoolByRoute.getEntryBlocking(ConnPoolByRoute.java:412)
at org.apache.http.impl.conn.tsccm.ConnPoolByRoute$1.getPoolEntry(ConnPoolByRoute.java:298)
at org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager$1.getConnection(ThreadSafeClientConnManager.java:238)
at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:423)
at org.apache.http.impl.client.AbstractHttpClient.doExecute(AbstractHttpClient.java:863)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:115)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:57)
at org.springframework.cloud.netflix.zuul.filters.route.SimpleHostRoutingFilter.forwardRequest(SimpleHostRoutingFilter.java:262)
at org.springframework.cloud.netflix.zuul.filters.route.SimpleHostRoutingFilter.forward(SimpleHostRoutingFilter.java:225)
at org.springframework.cloud.netflix.zuul.filters.route.SimpleHostRoutingFilter.run(SimpleHostRoutingFilter.java:177)
at com.netflix.zuul.ZuulFilter.runFilter(ZuulFilter.java:112)
When I looked through the code to figure out how to change the size of the connection pool and found this code
private static ClientConnectionManager newConnectionManager() throws Exception {
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);
SSLSocketFactory sf = new MySSLSocketFactory(trustStore);
sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
registry.register(new Scheme("https", sf, 443));
ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager(registry);
cm.setMaxTotal(Integer.parseInt(System.getProperty("zuul.max.host.connections", "200")));
cm.setDefaultMaxPerRoute(Integer.parseInt(System.getProperty("zuul.max.host.connections", "20")));
return cm;
}
At first I thought all I have to do is increase the value for zuul.max.host.connections and that would increase the size of per route max, but then I noticed that the same system property is used to set the total max number of connections.
Is setting the value of this system parameter the correct way to control the pool sizes? Or should we be using another component such as Ribbon to better manage these connections?
you can config
zuul.host.maxTotalConnections=1000
zuul.host.maxPerRouteConnections=100
If you still looking for solution, i took the following approach.
1- Disable ( SimpleHostRoutingFilter ) by passing
( -Dzuul.SimpleHostRoutingFilter.route.disable=true ) as system property.
2- Write your own customized Routing filter, in my case i copied ( SimpleHostRoutingFilter ), and make some modification to be able to set these properties .

Play Framework: How can I use a given certifcate for an HTTP WS.url call?

I have a certificate from a REST services provider, and I need to tell an given HTTP get call to use it when contacting a given provider. The codebase I'm working on used to use a third party library to achieve this, but I want leverage the timeout features and niceties of the WS.url call.
I've had a look at the API docs and the docs on the website, and I only unearthed a few clues, and those clues have so far not lead me to any solutions.
You should be able to approach it by adapting the SSLContext of the AsyncHttpClient.
The idea is to provide the client certificate with the underlying client. I assume you have a pkcs12 flying around.
It seems like that the AsyncHttpClientConfig cannot be updated as it is auto initialised. Therefore a new client should be instantiated and used.
The following should send off the request as stated with the SSLContext containing the pkcs12 file.
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory
.getDefaultAlgorithm());
KeyStore clientCert = KeyStore.getInstance("pkcs12");
InputStream is = new FileInputStream("/conf/brazo.p12");
clientCert.load(is, "brazo".toCharArray());
kmf.init(clientCert, "brazo".toCharArray());
SSLContext sc = SSLContext.getInstance("TLSv1");
sc.init(kmf.getKeyManagers(), null,
new SecureRandom());
AsyncHttpClientConfig config = new AsyncHttpClientConfig.Builder().setSSLContext(sc).build();
AsyncHttpClient client = new AsyncHttpClient(config);
String url = "https://your-url";
Request req = new RequestBuilder().setUrl(url).setMethod("GET").build();
client.executeRequest(req);
Sadly this goes away from Play! standard WS library.