记录一次HttpClient 连接超时重试问题
原代码如下:
public static void main(String[] args) throws UnsupportedEncodingException {HttpClient httpClient = new HttpClient();httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(5000);String url="XXXXXX你的请求接口";GetMethod getMethod = new GetMethod(url);//设置请求超时为5秒getMethod.getParams().setParameter(HttpMethodParams.SO_TIMEOUT, 5000);//设置请求重试处理,用的是默认的重试处理:请求三次getMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler());String response = "";try {int statusCode = httpClient.executeMethod(getMethod);if (statusCode != HttpStatus.SC_OK){System.err.println("请求出错:" + getMethod.getStatusLine());}byte[] responseBody = getMethod.getResponseBody();response = new String(responseBody, "utf-8");System.out.println("-----------response:" + response);} catch (HttpException e) {System.out.println("请检查输入的URL!");e.printStackTrace();} catch (IOException e) {System.out.println("发生网络异常!");} finally {getMethod.releaseConnection();}}
getMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler()); =====》这里设置了默认的请求重试处理类,默认会重试3次。
实测过程中发现在连接超时后直接报错SocketTimeoutException,并不会重试。
为什么呢?这里我们看下DefaultHttpMethodRetryHandler、SocketTimeoutException这两个类的源码。
1、发现SocketTimeoutException继承InterruptedIOException
2、在DefaultHttpMethodRetryHandler请求处理中retryMethod这个方法会判断是否进行重试,第109行发现在判断异常类型为InterruptedIOException时,返回false
真相大白,在httpclient的默认重试处理类(DefaultHttpMethodRetryHandler)中,连接超时报错会并不会重试。
这里我们可以自定义重试处理类(复制粘贴DefaultHttpMethodRetryHandler的类,将这里return true,或者直接在上面加一个判断SocketTimeoutException
异常的代码)就可以了,然后将这里换成我们自己的处理类。
自定义处理类代码如下MyHttpMethodRetryHandler:
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.HttpMethodBase;
import org.apache.commons.httpclient.HttpMethodRetryHandler;
import org.apache.commons.httpclient.NoHttpResponseException;import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.NoRouteToHostException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;public class MyHttpMethodRetryHandler implements HttpMethodRetryHandler {private static Class SSL_HANDSHAKE_EXCEPTION = null;static {try {SSL_HANDSHAKE_EXCEPTION = Class.forName("javax.net.ssl.SSLHandshakeException");} catch (ClassNotFoundException ignore) {}}/** the number of times a method will be retried */private int retryCount;/** Whether or not methods that have successfully sent their request will be retried */private boolean requestSentRetryEnabled;/*** Creates a new DefaultHttpMethodRetryHandler.* @param retryCount the number of times a method will be retried* @param requestSentRetryEnabled if true, methods that have successfully sent their request will be retried*/public MyHttpMethodRetryHandler(int retryCount, boolean requestSentRetryEnabled) {super();this.retryCount = retryCount;this.requestSentRetryEnabled = requestSentRetryEnabled;}/*** Creates a new DefaultHttpMethodRetryHandler that retries up to 3 times* but does not retry methods that have successfully sent their requests.*/public MyHttpMethodRetryHandler() {this(3, false);}/*** Used <code>retryCount</code> and <code>requestSentRetryEnabled</code> to determine* if the given method should be retried.** @see HttpMethodRetryHandler#retryMethod(HttpMethod, IOException, int)*/@Overridepublic boolean retryMethod(final HttpMethod method,final IOException exception,int executionCount) {if (method == null) {throw new IllegalArgumentException("HTTP method may not be null");}if (exception == null) {throw new IllegalArgumentException("Exception parameter may not be null");}// HttpMethod interface is the WORST thing ever done to HttpClientif (method instanceof HttpMethodBase) {if (((HttpMethodBase)method).isAborted()) {return false;}}if (executionCount > this.retryCount) {// Do not retry if over max retry countreturn false;}if (exception instanceof NoHttpResponseException) {// Retry if the server dropped connection on usreturn true;}if (exception instanceof SocketTimeoutException) {return true;}if (exception instanceof InterruptedIOException) {// Timeoutreturn false;}if (exception instanceof UnknownHostException) {// Unknown hostreturn false;}if (exception instanceof NoRouteToHostException) {// Host unreachablereturn false;}if (SSL_HANDSHAKE_EXCEPTION != null && SSL_HANDSHAKE_EXCEPTION.isInstance(exception)) {// SSL handshake exceptionreturn false;}if (!method.isRequestSent() || this.requestSentRetryEnabled) {// Retry if the request has not been sent fully or// if it's OK to retry methods that have been sentreturn true;}// otherwise do not retryreturn false;}/*** @return <code>true</code> if this handler will retry methods that have* successfully sent their request, <code>false</code> otherwise*/public boolean isRequestSentRetryEnabled() {return requestSentRetryEnabled;}/*** @return the maximum number of times a method will be retried*/public int getRetryCount() {return retryCount;}
}