设置httpClient的超时时间及关闭自动重发

文章详细介绍了HttpClient的重试机制,包括默认的重试条件和如何自定义重试策略。同时,讲解了CloseableHttpClient的创建方式,以及如何禁用超时重试和设置超时时间。重点在于理解HttpClient的retryHandler以及CloseableHttpClient的配置选项。

关于httpClient的重发策略

HttpClient默认是有重试机制的,其重试策略是:
1.只有发生IOExecetion时才会发生重试
2.InterruptedIOException、UnknownHostException、ConnectException、SSLException,发生这4中异常不重试
3.get方法可以重试3次,post方法在socket对应的输出流没有被write并flush成功时可以重试3次。
4.读/写超时不进行重试
5.socket传输中被重置或关闭会进行重试

一、HttpClient的创建方式:

	//设置超时时间
    httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(5000);
    httpClient.getHttpConnectionManager().getParams().setSoTimeout(5000);
    //构造PostMethod的实例  url:请求地址
    PostMethod postMethod = new PostMethod(url);
    //关闭重发策略
    postMethod .getParams().setParameter(HttpMethodParams.RETRY_HANDLER,new DefaultHttpMethodRetryHandler(0,false));
    //设置编码格式
    postMethod .getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET,"UTF-8");
    //设置超时时间
     postMethod .getParams().setParameter(HttpMethodParams.SO_TIMEOUT,5000);
     try{
           httpClient.executeMethod(postMethod);
        }catch (Exception e){
            //TODO 省略异常处理部分
        }finally {
            postMethod.releaseConnection();
        }

重试方法调用:
在 org.apache.commons.httpclient.HttpMethodDirector类中,如果发生IOException,会判断是否需要重试
package org.apache.commons.httpclient.HttpMethodDirector
具体源码如下:

private void executeWithRetry(HttpMethod method) throws IOException, HttpException {
        int execCount = 0;

        try {
            while(true) {
                ++execCount;

                try {
                    if (LOG.isTraceEnabled()) {
                        LOG.trace("Attempt number " + execCount + " to process request");
                    }

                    if (this.conn.getParams().isStaleCheckingEnabled()) {
                        this.conn.closeIfStale();
                    }

                    if (!this.conn.isOpen()) {
                        this.conn.open();
                        if (this.conn.isProxied() && this.conn.isSecure() && !(method instanceof ConnectMethod) && !this.executeConnect()) {
                            return;
                        }
                    }

                    this.applyConnectionParams(method);
                    method.execute(this.state, this.conn);
                    return;
                } catch (HttpException var5) {
                    throw var5;
                } catch (IOException var6) {
                    LOG.debug("Closing the connection.");
                    this.conn.close();
                    if (method instanceof HttpMethodBase) {
                        MethodRetryHandler handler = ((HttpMethodBase)method).getMethodRetryHandler();
                        if (handler != null && !handler.retryMethod(method, this.conn, new HttpRecoverableException(var6.getMessage()), execCount, method.isRequestSent())) {
                            LOG.debug("Method retry handler returned false. Automatic recovery will not be attempted");
                            throw var6;
                        }
                    }

                    HttpMethodRetryHandler handler = (HttpMethodRetryHandler)method.getParams().getParameter("http.method.retry-handler");
                    if (handler == null) {
                        handler = new DefaultHttpMethodRetryHandler();
                    }

                    if (!((HttpMethodRetryHandler)handler).retryMethod(method, var6, execCount)) {
                        LOG.debug("Method retry handler returned false. Automatic recovery will not be attempted");
                        throw var6;
                    }

                    if (LOG.isInfoEnabled()) {
                        LOG.info("I/O exception (" + var6.getClass().getName() + ") caught when processing request: " + var6.getMessage());
                    }

                    if (LOG.isDebugEnabled()) {
                        LOG.debug(var6.getMessage(), var6);
                    }

                    LOG.info("Retrying request");
                }
            }
        } catch (IOException var7) {
            if (this.conn.isOpen()) {
                LOG.debug("Closing the connection.");
                this.conn.close();
            }

            this.releaseConnection = true;
            throw var7;
        } catch (RuntimeException var8) {
            if (this.conn.isOpen()) {
                LOG.debug("Closing the connection.");
                this.conn.close();
            }

            this.releaseConnection = true;
            throw var8;
        }
    }

HttpMethodRetryHandler:
new DefaultHttpMethodRetryHandler(0,false)解读:
DefaultHttpMethodRetryHandler(0,false),
允许重试次数为0
调用如下方法:

public DefaultHttpMethodRetryHandler(int retryCount, boolean requestSentRetryEnabled) {
        this.retryCount = retryCount;
        this.requestSentRetryEnabled = requestSentRetryEnabled;
    }

默认的重试方法:DefaultHttpMethodRetryHandler()
允许重试次数为3
具体处理如下“retryMethod”方法
若实际发送次数大于等于3次,不进行重试;
若实际发送次数小于3次,请求未成功发送,进行重试;
若实际发送次数小于3次,请求成功发送,不进行重试;

public class DefaultHttpMethodRetryHandler implements HttpMethodRetryHandler {
    private static Class SSL_HANDSHAKE_EXCEPTION = null;
    private int retryCount;
    private boolean requestSentRetryEnabled;

    public DefaultHttpMethodRetryHandler(int retryCount, boolean requestSentRetryEnabled) {
        this.retryCount = retryCount;
        this.requestSentRetryEnabled = requestSentRetryEnabled;
    }

    public DefaultHttpMethodRetryHandler() {
        this(3, false);
    }

    public boolean retryMethod(HttpMethod method, IOException exception, int executionCount) {
        if (method == null) {
            throw new IllegalArgumentException("HTTP method may not be null");
        } else if (exception == null) {
            throw new IllegalArgumentException("Exception parameter may not be null");
        } else if (method instanceof HttpMethodBase && ((HttpMethodBase)method).isAborted()) {
            return false;
        } else if (executionCount > this.retryCount) {
            return false;
        } else if (exception instanceof NoHttpResponseException) {
            return true;
        } else if (exception instanceof InterruptedIOException) {
            return false;
        } else if (exception instanceof UnknownHostException) {
            return false;
        } else if (exception instanceof NoRouteToHostException) {
            return false;
        } else if (SSL_HANDSHAKE_EXCEPTION != null && SSL_HANDSHAKE_EXCEPTION.isInstance(exception)) {
            return false;
        } else {
            return !method.isRequestSent() || this.requestSentRetryEnabled;
        }
    }
}

MethodRetryHandler目前在commons-httpclient-3.1.jar中不推荐使用
在这里插入图片描述

public class DefaultMethodRetryHandler implements MethodRetryHandler {
    private int retryCount = 3;
    private boolean requestSentRetryEnabled = false;

    public DefaultMethodRetryHandler() {
    }

    public boolean retryMethod(HttpMethod method, HttpConnection connection, HttpRecoverableException recoverableException, int executionCount, boolean requestSent) {
        return (!requestSent || this.requestSentRetryEnabled) && executionCount <= this.retryCount;
    }
}    

二、CloseableHttpClient的创建方式:

1.HttpClients.custom().setXXX().build();
2.HttpClients.createDefault();
第一种方法用来定制一些HttpClient的属性,比如https证书,代理服务器,http过滤器,连接池管理器等自定义的用法。
第二种方法用来获得一个默认的HttpClient实例,获取到的CloseableHttpClient是默认重试策略的。
这两种方法获得都是CloseableHttpClient实例,且都是通过HttpClientBuilder的build()构建的。

HttpClients.custom();源码

 public static HttpClientBuilder custom() {
        return HttpClientBuilder.create();
    }

HttpClients.createDefault();源码

public static CloseableHttpClient createDefault() {
        return HttpClientBuilder.create().build();
    }

如果要禁止CloseableHttpClient访问超时时进行重试,则需要用第一种方式创建CloseableHttpClient,设置重试参数automaticRetriesDisabled为true;

CloseableHttpClient build1 = HttpClients.custom().disableAutomaticRetries().build()

其中disableAutomaticRetries()的源码为:

public final HttpClientBuilder disableAutomaticRetries() {
        this.automaticRetriesDisabled = true;
        return this;
    }

这里将参数设置为true,在执行build()方法的时候会进行判断:

if (!this.automaticRetriesDisabled) {
            routePlannerCopy = this.retryHandler;
            if (routePlannerCopy == null) {
                routePlannerCopy = DefaultHttpRequestRetryHandler.INSTANCE;
            }

            execChain = new RetryExec((ClientExecChain)execChain, (HttpRequestRetryHandler)routePlannerCopy);
        }

如果automaticRetriesDisabled为false就会进入if分支,使用RetryExec执行器创建执行链使用用默认的重发策略DefaultHttpRequestRetryHandler。因此automaticRetriesDisabled设置为true时就可以禁止重发。

为HttpClient设置连接超时时间和获取数据超时时间

使用HttpClient,一般都需要设置连接超时时间和获取数据超时时间。这两个参数很重要,目的是为了防止访问其他http时,由于超时导致自己的应用受影响。
4.5版本中,这两个参数的设置都抽象到了RequestConfig中,由相应的Builder构建。

setConnectTimeout:设置连接超时时间,单位毫秒。
setConnectionRequestTimeout:设置从connect Manager获取Connection 超时时间,单位毫秒。这个属性是新加的属性,因为目前版本是可以共享连接池的。
setSocketTimeout:请求获取数据的超时时间,单位毫秒。 如果访问一个接口,多少时间内无法返回数据,就直接放弃此次调用。

HttpPost httpPost = new HttpPost();
        RequestConfig requestConfig = RequestConfig.custom().
                setConnectTimeout(5000).
                setConnectionRequestTimeout(5000).
                setSocketTimeout(5000).
                build();
        httpPost.setConfig(requestConfig);

整体例子

		/**
         * 创建CloseableHttpClient,禁止超时重试,并设置超时时间
         */
        CloseableHttpClient closeableHttpClient = HttpClients.custom().disableAutomaticRetries().build();
        HttpPost httpPost = new HttpPost();
        RequestConfig requestConfig = RequestConfig.custom(). 
                setConnectTimeout(5000).
                setConnectionRequestTimeout(5000).
                setSocketTimeout(5000).
                build();
        httpPost.setConfig(requestConfig);
        try {
            closeableHttpClient.execute(httpPost);
        } catch (IOException e) {
            //异常处理部分省略.....
            e.printStackTrace();
        }
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值