HttpClient timeout

springcorn picture springcorn · Jul 12, 2013 · Viewed 8.9k times · Source

I have a client server app using spring HttpInvokers. I am struggling with the timeouts. I see other threads regarding this but no clear answers.

As far as I know the readTimeout is supposed to control the length of a transaction after it has already connected. I have made this very long because some processes such as report requests take time to run. This has worked fine for a long period of time now.

Now the problem is that sometimes the internet connection fails or blips out at the moment a request is made and the connection is never made. Instead of erroring out I have instituted a retry procedure which intercepts a failed connection and retries it. I want these failures to happen quickly so I set the HttpClient parameters soTimeout and connectionManagerTimeout to 2 seconds.

This seemed to work for the most part. Occasionally it takes longer than 2 seconds to catch the problem but that is ok. The issue is that the soTimeout seems to apply to or override the readTimout as well. So now all my longer running requests get the retry function even though they have connected just fine and are simply waiting for the reply to complete. The connectionManagerTimeout setting seems to do nothing at all. Nothing seems to affect the time it takes to actually acquire the initial connection.

Is there another timeout parameter I should be setting?

 <bean id="myServiceInterceptor"
          class="org.springframework.remoting.httpinvoker.HttpInvokerClientInterceptor">
        <property name="serviceUrl">
            <value>https://myhost/myservices/myMgrService</value>
        </property>
        <property name="remoteInvocationFactory" ref="remoteInvocationFactory"/>
        <property name="httpInvokerRequestExecutor">
 <bean class="org.springframework.remoting.httpinvoker.CommonsHttpInvokerRequestExecutor">
                <!--read time out is how long we will wait for a reply.  This is large in case we are waiting on a long server process-->
                <property name="readTimeout" value="2000000"/>
                <!--                we manually set this so we can control the parameter timeout values-->
                <property name="httpClient">
                    <bean class="org.apache.commons.httpclient.HttpClient">
                        <constructor-arg index="0">
                            <!--we want multi threaded if we want to ever connect in background threads, this is default if not set anyways in CommonsHttpInvokerRequestExecutor but since we are overriding we have to manually set it or we get the simple one-->
                            <bean class="org.apache.commons.httpclient.MultiThreadedHttpConnectionManager"></bean>
                        </constructor-arg>
                        <property name="params">
                            <bean class="org.apache.commons.httpclient.params.HttpClientParams">
                                <!--Here we set the socket time out and connection manager time out which is how long we wait for the socket to connect before showing bad feedback.
             The default is 60 seconds which makes the client just hang.  Now we get immediate response from our RetryConnection-->
                                <property name="soTimeout" value="2000"/>
                                <property name="connectionManagerTimeout" value="2000"/>
                            </bean>
                        </property>
                    </bean>
                </property>
            </bean>        </property>
    </bean>
    <bean id="myServiceMgr" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="interceptorNames">
            <list>

                <value>clientExceptionAdvisor</value>
                <value>retryConnection</value>
                <value>myServiceInterceptor</value>
            </list>
        </property>
        <property name="proxyInterfaces">
            <value>ServiceIF</value>
        </property>
    </bean>

**UPDATE SOLUTION Looks like soTimout is read timeout also. There is a way to set connection timeout it is just a pain. Here is the spring xml i used:

<bean class="org.springframework.remoting.httpinvoker.CommonsHttpInvokerRequestExecutor">
                <!--read time out is how long we will wait for a reply.  This is large in case we are waiting on a long server process-->
                <property name="readTimeout" value="2000000"/>
                <!--                we manually set this so we can control the parameter timeout values-->
                <property name="httpClient">
                    <bean class="org.apache.commons.httpclient.HttpClient">
                        <constructor-arg index="0">
                            <bean class="org.apache.commons.httpclient.params.HttpClientParams">
                                <!--Here we set the connection manager time out which is supposed to be how long we wait for the established connection to be returned from the connection manager. This shortness allows us to catch it and enable the retry function.-->
                                <property name="connectionManagerTimeout" value="2500"/>
                            </bean>
                        </constructor-arg>
                        <constructor-arg index="1">
                            <!--we want multi threaded if we want to ever connect in background threads, this is default if not set anyways in CommonsHttpInvokerRequestExecutor but since we are overriding we have to manually set it or we get the simple one-->
                            <bean class="org.apache.commons.httpclient.MultiThreadedHttpConnectionManager">
                                <property name="params">
                                    <bean class="org.apache.commons.httpclient.params.HttpConnectionManagerParams">
                                        <!--Here we set the socket time out which is essentially the same thing as the readTimeout.  It is the time we wait for a response-->
                                        <property name="soTimeout" value="2000000"/>
                                        <!--Here we set the connection time out which is supposed to be how long we wait for the connect to be established. This shortness allows us to catch it and enable the retry function.  The default is 60 seconds which makes the client just hang.  -->
                                        **<property name="connectionTimeout" value="2500"/>**
                                    </bean>
                                </property>
                            </bean>
                        </constructor-arg>
                        <property name="params">
                            <bean class="org.apache.commons.httpclient.params.HttpClientParams">
                                <!--Here we set the socket time out which is essentially the same thing as the readTimeout.  It is the time we wait for a response-->
                                <property name="soTimeout" value="2000000"/>
                                <!--Here we set the connection manager time out which is how long we wait to get an established connection returned from the connection manager.
             The default is 60 seconds which makes the client just hang.  Now we get more immediate response from our RetryConnection.  But it isn't totally reliable cause of underlying tcp/ip-->
                                **<property name="connectionManagerTimeout" value="2500"/>**
                            </bean>
                        </property>
                    </bean>
                </property>
            </bean>

Answer

Rob Adams picture Rob Adams · Jul 12, 2013

You cannot set a maximum request duration. This has some helpful info.

Generally with http connections, you should have the same timeout regardless of connection type. It makes no sense to have multiple ticks counting off the same thing at different points.

You should set up your API to either have the reports either pre-run, or run on a different thread that's doesn't lock up your invoker. This way you can get back a status object that lets you know if you need to check again for a report.