HttpClient重试机制 --- 自定义HttpRequestRetryHandler(自定义 重试次数以及重试的时候业务处理)

            通过http做接口调用请求的时候,常常会因为网络抖动或者其他未知原因,造成接口在某个瞬间访问不了。此时需要增加重试机制。刚出来的时候掉接口需要三次重试,由于对httpclient不是很了解。只能在for循环里面对异常经常处理并重新调接口。后来做http服务端的时候,有次debug偶然发现客户端调一次请求,服务端会跳多次debug,后来查阅资料发现httpclient有重试机制。

            今天做了个通天塔接口重试的需求,便想起来了httpclient的重试机制。

   查了很久资料,也测试了很多次。后来终于成功了。是通过设置httpclient 的retryHandler来实现。
不多说废话,直接贴代码,如下:

/**
     * @param isPooled 是否使用连接池
     */
    public static CloseableHttpClient getClient(boolean isPooled) {
        HttpRequestRetryHandler handler = new HttpRequestRetryHandler() {
            @Override
            public boolean retryRequest(IOException arg0, int retryTimes, HttpContext arg2) {
                if (retryTimes > 2) {
                    return false;
                }
                if (arg0 instanceof UnknownHostException || arg0 instanceof ConnectTimeoutException
                        || !(arg0 instanceof SSLException) || arg0 instanceof NoHttpResponseException) {
                    return true;
                }

                HttpClientContext clientContext = HttpClientContext.adapt(arg2);
                HttpRequest request = clientContext.getRequest();
                boolean idempotent = !(request instanceof HttpEntityEnclosingRequest);
                if (idempotent) {
                    // 如果请求被认为是幂等的,那么就重试。即重复执行不影响程序其他效果的
                    return true;
                }
                return false;
            }
        };


        if (isPooled) {
            return HttpClients.custom().setConnectionManager(cm).setRetryHandler(handler).build();
        }
        return HttpClients.createDefault();
    }

上面的代码是封装了一个HttpClientUtil的工具类中的获取线程池并设置retry机制的方法。整个HttpClientUtil文件如下:

package com.jd.bingo.yhd.util.http;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.jd.bingo.yhd.util.JsonUtil;
import com.jd.ump.annotation.JProEnum;
import com.jd.ump.annotation.JProfiler;

import org.apache.commons.httpclient.ConnectTimeoutException;
import org.apache.commons.httpclient.NoHttpResponseException;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpRequest;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.UnknownHostException;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.net.ssl.SSLException;
import javax.servlet.http.HttpServletRequest;

@Component
public class HttpClientUtil {

    private static Logger logger = LoggerFactory.getLogger(HttpClientUtil.class);

    private static final RequestConfig REQUEST_CONFIG_TIME_OUT = RequestConfig.custom()
            .setSocketTimeout(5000)
            .setConnectTimeout(5000)
            .setConnectionRequestTimeout(5000)
            .build();

    private static PoolingHttpClientConnectionManager cm = null;

    @PostConstruct
    public void init() {
        cm = new PoolingHttpClientConnectionManager();
        cm.setMaxTotal(1000);
        cm.setDefaultMaxPerRoute(1000);
    }

    /**
     * @param isPooled 是否使用连接池
     */
    public static CloseableHttpClient getClient(boolean isPooled) {
        HttpRequestRetryHandler handler = new HttpRequestRetryHandler() {
            @Override
            public boolean retryRequest(IOException arg0, int retryTimes, HttpContext arg2) {
                if (retryTimes > 2) {
                    return false;
                }
                if (arg0 instanceof UnknownHostException || arg0 instanceof ConnectTimeoutException
                        || !(arg0 instanceof SSLException) || arg0 instanceof NoHttpResponseException) {
                    return true;
                }

                HttpClientContext clientContext = HttpClientContext.adapt(arg2);
                HttpRequest request = clientContext.getRequest();
                boolean idempotent = !(request instanceof HttpEntityEnclosingRequest);
                if (idempotent) {
                    // 如果请求被认为是幂等的,那么就重试。即重复执行不影响程序其他效果的
                    return true;
                }
                return false;
            }
        };


        if (isPooled) {
            return HttpClients.custom().setConnectionManager(cm).setRetryHandler(handler).build();
        }
        return HttpClients.createDefault();
    }

    public static final String doPostWithRequest(String path, HttpServletRequest request) {
        Enumeration params = request.getParameterNames();
        List<NameValuePair> nameValuePairs = Lists.newArrayList();
        while (params.hasMoreElements()) {
            String paramName = (String) params.nextElement();
            nameValuePairs.add(new BasicNameValuePair(paramName, request.getParameter(paramName)));
        }
        HttpPost httpPost = new HttpPost(path);

        httpPost.setConfig(REQUEST_CONFIG_TIME_OUT);
        try {
            httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
            return execReq(httpPost);
        } catch (UnsupportedEncodingException e) {
            logger.error("do post error: ", e);
        }
        return "";
    }

    public static final String doPost(String path, Map<String, Object> params) {
        logger.debug("doPost from " + path, params);
        HttpPost httpPost = new HttpPost(path);

        httpPost.setConfig(REQUEST_CONFIG_TIME_OUT);
        try {
            httpPost.setEntity(new UrlEncodedFormEntity(createParams(params)));

            String bodyAsString = execReq(httpPost);
            if (bodyAsString != null) return bodyAsString;

        } catch (UnsupportedEncodingException e) {
            logger.error(e.getMessage(), e);
        }

        return errorResponse("-2", "unknown error");
    }

    private static String execReq(HttpPost httpPost) {
        try {
            CloseableHttpResponse response = getClient(true).execute(httpPost);
            if (response != null) {
                try {
                    if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                        return EntityUtils.toString(response.getEntity());
                    } else {
                        return errorResponse(String.valueOf(response.getStatusLine().getStatusCode()),
                                "http post request error: " + httpPost.getURI());
                    }
                } finally {
                    EntityUtils.consume(response.getEntity());
                }
            } else {
                return errorResponse("-1", "response is null");
            }

        } catch (IOException e) {
            logger.error("doPost error: ", e);
        }
        return errorResponse("-3", "unknown error");
    }

    private static String errorResponse(String code, String msg) {
        return JsonUtil.toJsonStr(ImmutableMap.of("code", code, "msg", msg));
    }

    private static List<NameValuePair> createParams(Map<String, Object> params) {
        List<NameValuePair> nameValuePairs = Lists.newArrayList();
        for (Map.Entry<String, Object> entry : params.entrySet()) {
            nameValuePairs.add(new BasicNameValuePair(entry.getKey(), String.valueOf(entry.getValue())));
        }
        return nameValuePairs;
    }
}

httpclient的调用如下:

String  bingoPortalResStr = HttpClientUtil.doPostWithRequest(path, request);
 

参考:https://www.jianshu.com/p/481cbfcd3f13


  • 1
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值