httpclient-重试

1,976 阅读4分钟

默认有重试吗?

有。

但是,不是所有的情况,都会重试。

所以,如果要对所有的情况,都进行重试,请自定义实现。


那什么时候重试?什么时候不重试呢?
1.不重复的情况
httpClient默认重试策略DefaultHttpRequestRetryHandler,针对连接超时和获取数据超时并不会重试,需要自定义重试策略。

2.重试的情况
具体来说,什么样的异常会重试呢?客户端写的时候。

为什么只有写的时候,才需要重试呢?因为建立连接的时候,异常,可能是连接不上,那就不连接了。而读的时候,异常,说明服务器已经处理了客户端的请求。只有写的时候,异常,但是呢,服务器连接是正常的,而且服务器没有处理当前请求,那么就继续重试。

源码-校验是否需要重试

//默认handler实现
/**
     * Used <code>retryCount</code> and <code>requestSentRetryEnabled</code> to determine
     * if the given method should be retried.
     */
    public boolean retryRequest(
            final IOException exception,
            int executionCount,
            final HttpContext context) {
        if (exception == null) {
            throw new IllegalArgumentException("Exception parameter may not be null");
        }
        if (context == null) {
            throw new IllegalArgumentException("HTTP context may not be null");
        }
        
        //重试次数超过,不再重试
        if (executionCount > this.retryCount) {
            // Do not retry if over max retry count
            return false;
        }
        
        //以下几种异常,都不重试
        if (exception instanceof InterruptedIOException) { //线程中断异常
            // Timeout
            return false;
        }
        if (exception instanceof UnknownHostException) {
            // Unknown host
            return false;
        }
        if (exception instanceof ConnectException) { //建立连接超时异常,读超时异常,都是套接字超时异常。而且,属于线程中断异常。
            // Connection refused
            return false;
        }
        if (exception instanceof SSLException) {
            // SSL handshake exception
            return false;
        }

        HttpRequest request = (HttpRequest)
            context.getAttribute(ExecutionContext.HTTP_REQUEST);

        if(requestIsAborted(request)){
            return false;
        }

        if (handleAsIdempotent(request)) {
            // Retry if the request is considered idempotent
            return true;
        }

        Boolean b = (Boolean)
            context.getAttribute(ExecutionContext.HTTP_REQ_SENT);
        boolean sent = (b != null && b.booleanValue());

        if (!sent || 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;
    }

为什么是根据异常来校验是否需要重试?

请看上面源码实现。

自定义

方案1

1.禁用默认重试
2.如果异常,就无限递归调用当前方法


方案2

自定义实现类,覆盖默认的handler类的校验是否重试的方法。

超时和重试之间,有必然的关系吗?

没有。

重试,是因为异常。但是,异常有很多种,超时异常只是其中的一种,比如建立连接超时,读超时。

主要参数

1.重试次数
2.是否重试

工作使用-支付

采用的方案1。

客户端工具类HttpClient

/**
 * This interface represents only the most basic contract for HTTP request
 * execution. It imposes no restrictions or particular details on the request
 * execution process and leaves the specifics of state management,
 * authentication and redirect handling up to individual implementations.
 * This should make it easier to decorate the interface with additional
 * functionality such as response content caching.
 * <p/>
 * The usual execution flow can be demonstrated by the code snippet below:
 * <PRE>
 * HttpClient httpclient = new DefaultHttpClient();
 *
 * // Prepare a request object
 * HttpGet httpget = new HttpGet("http://www.apache.org/");
 *
 * // Execute the request
 * HttpResponse response = httpclient.execute(httpget);
 *
 * // Examine the response status
 * System.out.println(response.getStatusLine());
 *
 * // Get hold of the response entity
 * HttpEntity entity = response.getEntity();
 *
 * // If the response does not enclose an entity, there is no need
 * // to worry about connection release
 * if (entity != null) {
 *     InputStream instream = entity.getContent();
 *     try {
 *
 *         BufferedReader reader = new BufferedReader(
 *                 new InputStreamReader(instream));
 *         // do something useful with the response
 *         System.out.println(reader.readLine());
 *
 *     } catch (IOException ex) {
 *
 *         // In case of an IOException the connection will be released
 *         // back to the connection manager automatically
 *         throw ex;
 *
 *     } catch (RuntimeException ex) {
 *
 *         // In case of an unexpected exception you may want to abort
 *         // the HTTP request in order to shut down the underlying
 *         // connection and release it back to the connection manager.
 *         httpget.abort();
 *         throw ex;
 *
 *     } finally {
 *
 *         // Closing the input stream will trigger connection release
 *         instream.close();
 *
 *     }
 *
 *     // When HttpClient instance is no longer needed,
 *     // shut down the connection manager to ensure
 *     // immediate deallocation of all system resources
 *     httpclient.getConnectionManager().shutdown();
 * }
 * </PRE>
 *
 * @since 4.0
 */
public interface HttpClient {

自定义实现

首先,为什么要自定义实现? 因为要简单的封装一下。

为什么要简单的封装一下? 因为要使用单例。

如何实现?源码。

自定义实现类{
    HttpClient hc;
    自定义实现类 ; //供应用层面调用
    
    创建对象{ 
        单例创建hc; //使用默认实现DefaultHttpClient
    }
}

handler类

接口

/**
 * A handler for determining if an HttpRequest should be retried after a
 * recoverable exception during execution.
 * <p>
 * Implementations of this interface must be thread-safe. Access to shared
 * data must be synchronized as methods of this interface may be executed
 * from multiple threads.
 *
 * @since 4.0
 */
public interface HttpRequestRetryHandler {

    /**
     * Determines if a method should be retried after an IOException
     * occurs during execution.
     *
     * @param exception the exception that occurred
     * @param executionCount the number of times this method has been
     * unsuccessfully executed
     * @param context the context for the request execution
     *
     * @return <code>true</code> if the method should be retried, <code>false</code>
     * otherwise
     */
    boolean retryRequest(IOException exception, int executionCount, HttpContext context);

}

实现,有个默认实现。

怎么又使用单例,又有连接池?这不矛盾吗?

连接池是为了不关闭对象,复用连接对象。

优化

优化httpclient框架 www.cnblogs.com/bethunebtj/…

参考

hc.apache.org/httpcompone…

www.cnblogs.com/kingszelda/…

juejin.cn/post/684490…