We are using Http Client 4.5.x with Spring-Ws and make requests with the webServiceTemplate.marshalSendAndReceive(requestObject)
method. We would like to have a reliable connection timeout value, but are currently running into the issue described here in section 8 (DNS round robin) where multiple ip addresses are tried and so the timeout is unpredictable. Are there simple ways to set up a hard time-out after a certain time using just the Spring-ws and Http Client library, or is setting up some sort of custom timeout necessary?
Case: Connection timeout set to 1 second (actual timeout for the method is 4 seconds -- is it possible to set a method timeout of 1 second using the Spring/Http client library?)
Application logs (Http Client logs set to DEBUG
):
16:45:02 (org.apache.http.impl.execchain.MainClientExec) Opening connection {}->http://salesforce.com:448
16:45:02 (org.apache.http.impl.conn.DefaultHttpClientConnectionOperator) Connecting to salesforce.com/96.43.149.26:448
16:45:03 (org.apache.http.impl.conn.DefaultHttpClientConnectionOperator) Connect to salesforce.com/96.43.149.26:448 timed out. Connection will be retried using another IP address
16:45:03 (org.apache.http.impl.conn.DefaultHttpClientConnectionOperator) Connecting to salesforce.com/96.43.145.26:448
16:45:04 (org.apache.http.impl.conn.DefaultHttpClientConnectionOperator) Connect to salesforce.com/96.43.145.26:448 timed out. Connection will be retried using another IP address
16:45:04 (org.apache.http.impl.conn.DefaultHttpClientConnectionOperator) Connecting to salesforce.com/96.43.144.26:448
16:45:05 (org.apache.http.impl.conn.DefaultHttpClientConnectionOperator) Connect to salesforce.com/96.43.144.26:448 timed out. Connection will be retried using another IP address
16:45:05 (org.apache.http.impl.conn.DefaultHttpClientConnectionOperator) Connecting to salesforce.com/96.43.148.26:448
16:45:06 (org.apache.http.impl.conn.DefaultManagedHttpClientConnection) http-outgoing-0: Shutdown connection
Http client bean:
<bean id="httpClientBean" class="org.apache.http.client.HttpClient" factory-bean="httpClientFactory" factory-method="getHttpClient" />
Http Factory code (connection timeout value set via Spring dependency injection):
public class HttpFactory {
private int connectionTimeout;
public HttpFactory(int connectionTimeout, ...) {
this.connectionTimeout = connectionTimeout;
...
}
...
public HttpClient getHttpClient() {
HttpClientBuilder clientBuilder = HttpClientBuilder.create();
...
RequestConfig.Builder configBuilder = RequestConfig.custom();
configBuilder.setConnectTimeout(this.connectionTimeout);
clientBuilder.setDefaultRequestConfig(configBuilder.build());
...
return clientBuilder.build();
}
}
Web service template bean:
<bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
...
<property name="messageSender">
<bean class="org.springframework.ws.transport.http.HttpComponentsMessageSender">
<constructor-arg index="0">
<ref bean="httpClientBean" />
</constructor-arg>
</bean>
</property>
</bean>
Service code (we want this method call to take X seconds, not 2x or 3x seconds):
// we want this method call to take ~1 second, not ~4 seconds (i.e. similar to the connection timeout value, not a multiplier)
Object obj = webServiceTemplate.marshalSendAndReceive(requestDocument);
There are two options
Building a custom ClientConnectionOperator
Building a custom DnsResolver
. This option is much simpler.
CloseableHttpClient client = HttpClients.custom()
.setDnsResolver(host -> new InetAddress[] { InetAddress.getByName(host) })
.build();