I want to consume 2 services and want to have different timeouts. So, I have overridden Spring's SimpleClientHttpRequestFactory
and using it from my HttpDaoImpl
.
Now everything works in one environment but exact same EAR doesn't work in other environment, and only difference in both the environments is that the service URL I am connecting is load balanced URL in one and non-LB in other. Issue is in load balanced service URL.
Problem is that everytime httpReadTimeout
is taking effect or being used but not XYZHttpReadTimeout
, even though I am conditionally setting the time out.
It is extremely strange issue because everything is working in one env. and not in other. My guess for probable root cause till now is the load balanced URL but still I am not able to see any technical reason on why load balanced URL could be issue because my machine will open a socket and close it after read timeout.
I have placed loggers in CustomClientHttpRequestFactory
and verified that correct values are being used but in the end somehow read timeout is not working as expected.
CustomClientHttpRequestFactory.java:
public class CustomClientHttpRequestFactory extends SimpleClientHttpRequestFactory{
private Log logger = LogFactory.getLog(CustomClientHttpRequestFactory.class);
@Value("${httpRequest.readTimeoutInMilliseconds}")
private Integer httpRequestTimeout;
@Value("${httpRequest.connectionTimeoutInMilliseconds}")
private Integer httpReadTimeout;
@Value("${XYZ.httpRequest.readTimeoutInMilliseconds}")
private Integer XYZHttpRequestTimeout;
@Value("${XYZ.httpRequest.connectionTimeoutInMilliseconds}")
private Integer XYZHttpReadTimeout;
@Value("${sgw.XYZHttp.service.url}")
private String XYZSystemUrl;
/**
* Overriding the default and setting a separate read timeout and HTTP connection timeout values for XYZ transactions.
*
* @param uri
*/
public void setTimeoutProperties(URI uri){
boolean isXYZTransaction = isXYZTransaction(uri);
Integer connectionTimeout = isXYZTransaction ? XYZHttpRequestTimeout : httpRequestTimeout;
Integer readTimeout = isXYZTransaction ? XYZHttpReadTimeout : httpReadTimeout;
logger.info("Setting " + connectionTimeout + " : " + readTimeout);
this.setReadTimeout(readTimeout);
this.setConnectTimeout(connectionTimeout);
}
private boolean isXYZTransaction(URI uri){
try {
URI XYZUrl = new URI(XYZSystemUrl);
logger.info("################################### XYZ Debug");
logger.info("XYZHttpRequestTimeout = " + XYZHttpRequestTimeout);
logger.info("XYZHttpReadTimeout = " + XYZHttpReadTimeout);
return XYZUrl.equals(uri);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return false;
}
}
HttpDaoImpl:
CustomClientHttpRequestFactory httpRequestFactory = (CustomClientHttpRequestFactory) myAppUtils.getApplicationContext().getBean("httpRequestFactory");
httpRequestFactory.setTimeoutProperties(lUri);
restTemplate.setRequestFactory(httpRequestFactory);
responseString = restTemplate.postForObject(lUri, requestString, String.class);
XML config:
<bean id="httpRequestFactory" class="com.abc.xyz.customComponents.CustomClientHttpRequestFactory" scope="prototype"/>
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name = "supportedMediaTypes">
<list>
<value>text/xml;charset=UTF-8</value>
</list>
</property>
</bean>
</list>
</property>
</bean>
Finally, found the issue and got it working.
Root cause:
Spring's RestTemplate
by default uses org.springframework.http.client.SimpleClientHttpRequestFactory
and it has issues in case of multi-threaded environment. This will pose issue when SimpleClientHttpRequestFactory
is used in multi-threaded environment and more than one set of timeout values are used, based on some rule of yours (in our case different timeouts based on URLs or service I am consuming).
A blog highlighting the same.
That's makes sense because another difference in 2 environments I have talked in my question was one environment (where everything was fine) had always one user which means only one thread and the other environment (where I had issues) had many concurrent users which means multiple threads.
Solution:
Use org.springframework.http.client.CommonsClientHttpRequestFactory
, which is another implementation of org.springframework.http.client.ClientHttpRequestFactory
available in Spring version 3.0.6 and works well in multi-threaded environment.
If Spring version is 3.0+ then use HttpComponentsClientHttpRequestFactory
.
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