Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring RestTemplate readtimeout property not working properly - strange issue

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>
like image 581
hagrawal Avatar asked Feb 09 '23 22:02

hagrawal


1 Answers

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.

like image 164
hagrawal Avatar answered Feb 11 '23 16:02

hagrawal