记录一次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)
*/
@Override
public 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 HttpClient
if (method instanceof HttpMethodBase) {
if (((HttpMethodBase)method).isAborted()) {
return false;
}
}
if (executionCount > this.retryCount) {
// Do not retry if over max retry count
return false;
}
if (exception instanceof NoHttpResponseException) {
// Retry if the server dropped connection on us
return true;
}
if (exception instanceof SocketTimeoutException) {
return true;
}
if (exception instanceof InterruptedIOException) {
// Timeout
return false;
}
if (exception instanceof UnknownHostException) {
// Unknown host
return false;
}
if (exception instanceof NoRouteToHostException) {
// Host unreachable
return false;
}
if (SSL_HANDSHAKE_EXCEPTION != null && SSL_HANDSHAKE_EXCEPTION.isInstance(exception)) {
// SSL handshake exception
return 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 sent
return true;
}
// otherwise do not retry
return 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;
}
}