OkHttpUtils.java 13.9 KB
package com.huaheng.control.management.utils;

import java.io.IOException;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

import javax.validation.constraints.NotNull;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;

import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.util.StrUtil;
import okhttp3.ConnectionPool;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

/**
 * OkHttp发送请求
 * @author huaheng
 * @Date   2022-5-30
 */
public class OkHttpUtils {

    private static final Logger log = LoggerFactory.getLogger(OkHttpUtils.class);

    /**
     * JSON格式
     */
    public static final MediaType MEDIA_TYPE_JSON = MediaType.parse("application/json; charset=utf-8");

    /**
     * 最多重试次数
     */
    public final static int MAX_RENTRY_COUNT = 1;

    /**
     * 最大连接时间(秒)
     */
    public final static int CONNECTION_TIMEOUT = 1;

    /**
     * 最大读取时间(秒)
     */
    public final static int READ_TIMEOUT = 20;

    /**
     * 最大写入时间(秒)
     */
    public final static int WRITE_TIMEOUT = 20;

    /**
     * OkHTTP线程池最大空闲线程数
     */
    public final static int MAX_IDLE_CONNECTIONS = 200;

    /**
     * OkHTTP线程池空闲线程存活时间(秒)
     */
    public final static long KEEP_ALIVE_DURATION = 60;

    /**
     * 最大读取时间(秒)
     */
    public final static int READ_TIMEOUT_FAST = 5;

    /**
     * 最大写入时间(秒)
     */
    public final static int WRITE_TIMEOUT_FAST = 5;

    /**
     * 最多重试次数
     */
    public final static int MAX_RENTRY_COUNT_FAST = 0;

    private static final String CONTENT_TYPE = "Content-Type";

    /** 访问接口参数配置 */
    private final static OkHttpClient HTTP_CLIENT =
        new OkHttpClient.Builder().connectTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS).readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
            .writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS).addInterceptor(new OkHttpUtils.OkhttpInterceptor(MAX_RENTRY_COUNT)) // 过滤器,设置最大重试次数
            .retryOnConnectionFailure(false).connectionPool(new ConnectionPool(MAX_IDLE_CONNECTIONS, KEEP_ALIVE_DURATION, TimeUnit.SECONDS)).build();

    /** 访问接口参数配置 */
    private final static OkHttpClient HTTP_CLIENT_FAST =
        new OkHttpClient.Builder().connectTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS).readTimeout(READ_TIMEOUT_FAST, TimeUnit.SECONDS)
            .writeTimeout(WRITE_TIMEOUT_FAST, TimeUnit.SECONDS).addInterceptor(new OkHttpUtils.OkhttpInterceptor(MAX_RENTRY_COUNT_FAST)) // 过滤器,设置最大重试次数
            .retryOnConnectionFailure(false).connectionPool(new ConnectionPool(MAX_IDLE_CONNECTIONS, KEEP_ALIVE_DURATION, TimeUnit.SECONDS)).build();

    /**
     * 向指定 URL 发送GET方法的请求
     * @param  url       发送请求的 URL
     * @param  param     请求参数,请求参数应该是 name1=value1&name2=value2 的形式
     * @return           所代表远程资源的响应结果
     * @throws Exception
     */
    public static String sendGet(String url, String param) {
        return sendGet(url, new HashMap<String, String>(), param);
    }

    /**
     * 向指定 URL 发送GET方法的请求
     * @param  url       发送请求的 URL
     * @param  param     请求参数,请求参数应该是 name1=value1&name2=value2 的形式
     * @param  headers   请求头
     * @return           所代表远程资源的响应结果
     * @throws Exception
     */
    public static String sendGet(String url, Map<String, String> headers, String param) {
        long requestTime = new Date().getTime();
        Request.Builder builder = new Request.Builder();
        buildHeader(builder, headers);
        Request request = builder.url(url + "?" + param).get().build();
        Response response = null;
        String result = null;
        try {
            response = HTTP_CLIENT.newCall(request).execute();
            result = response.body().string();
        } catch (ConnectException e) {
            String errorString = StrUtil.format("执行GET请求异常,url: {},执行耗时:{}ms,异常信息:{}", url, new Date().getTime() - requestTime, ExceptionUtil.getMessage(e));
            log.error(errorString);
            throw new RuntimeException(errorString, e);
        } catch (SocketTimeoutException e) {
            String errorString = StrUtil.format("执行GET请求异常,url: {},执行耗时:{}ms,异常信息:{}", url, new Date().getTime() - requestTime, ExceptionUtil.getMessage(e));
            log.error(errorString);
            throw new RuntimeException(errorString, e);
        } catch (IOException e) {
            String errorString = StrUtil.format("执行GET请求异常,url: {},执行耗时:{}ms,异常信息:{}", url, new Date().getTime() - requestTime, ExceptionUtil.getMessage(e));
            log.error(errorString);
            throw new RuntimeException(errorString, e);
        } catch (Exception e) {
            String errorString = StrUtil.format("执行GET请求异常,url: {},执行耗时:{}ms,异常信息:{}", url, new Date().getTime() - requestTime, ExceptionUtil.getMessage(e));
            log.error(errorString);
            throw new RuntimeException(errorString, e);
        }
        // 调用成功
        if (response.isSuccessful() && Objects.nonNull(response.body())) {
            log.info("执行GET请求成功,url: {},result: {}", url, result);
        } else {
            String errorString = StrUtil.format("执行GET请求失败,url: {},responseCode: {},responseMessage: {}", url, response.code(), response.message());
            throw new RuntimeException(errorString);
        }
        return result;
    }

    public static String sendPostByJsonStr(String url, String jsonString) {
        return sendPostByJsonStr(url, new HashMap<String, String>(), jsonString);
    }

    /**
     * JSONString形式发送POST请求
     * @throws Exception
     * @throws IOException
     */
    public static String sendPostByJsonStr(String url, Map<String, String> headers, String jsonString) {
        long requestTime = new Date().getTime();
        String result = null;
        if (StringUtils.isEmpty(url)) {
            return result;
        }
        Request.Builder builder = new Request.Builder();
        headers.put("Accept", "application/json");// 设置接收数据的格式
        headers.put("Content-Type", "application/json");// 设置发送数据的格式
        buildHeader(builder, headers);
        @SuppressWarnings("deprecation")
        RequestBody body = RequestBody.create(MEDIA_TYPE_JSON, jsonString);
        Request request = builder.url(url).post(body).build();
        Response response = null;
        try {
            response = HTTP_CLIENT.newCall(request).execute();
            result = response.body().string();
        } catch (ConnectException e) {
            String errorString = StrUtil.format("执行POST请求异常,url: {},执行耗时:{}ms,异常信息:{}", url, new Date().getTime() - requestTime, ExceptionUtil.getMessage(e));
            log.error(errorString);
            throw new RuntimeException(errorString, e);
        } catch (SocketTimeoutException e) {
            String errorString = StrUtil.format("执行POST请求异常,url: {},执行耗时:{}ms,异常信息:{}", url, new Date().getTime() - requestTime, ExceptionUtil.getMessage(e));
            log.error(errorString);
            throw new RuntimeException(errorString, e);
        } catch (IOException e) {
            String errorString = StrUtil.format("执行POST请求异常,url: {},执行耗时:{}ms,异常信息:{}", url, new Date().getTime() - requestTime, ExceptionUtil.getMessage(e));
            log.error(errorString);
            throw new RuntimeException(errorString, e);
        } catch (Exception e) {
            String errorString = StrUtil.format("执行POST请求异常,url: {},执行耗时:{}ms,异常信息:{}", url, new Date().getTime() - requestTime, ExceptionUtil.getMessage(e));
            log.error(errorString);
            throw new RuntimeException(errorString, e);
        }
        // 调用成功
        if (response.isSuccessful() && Objects.nonNull(response.body())) {
            log.info("执行POST请求成功,url: {},result: {}", url, result);
        } else {
            String errorString = StrUtil.format("执行POST请求失败,url: {},responseCode: {},responseMessage: {}", url, JSON.toJSONString(headers), jsonString,
                response.code(), response.message());
            throw new RuntimeException(errorString);
        }
        return result;
    }

    /**
     * JSONString形式发送POST请求
     * @throws Exception
     * @throws IOException
     */
    public static String sendPostByJsonStrFast(String url, Map<String, String> headers, String jsonString) {
        long requestTime = new Date().getTime();
        String result = null;
        if (StringUtils.isEmpty(url)) {
            return result;
        }
        Request.Builder builder = new Request.Builder();
        headers.put("Accept", "application/json");// 设置接收数据的格式
        headers.put("Content-Type", "application/json");// 设置发送数据的格式
        buildHeader(builder, headers);
        @SuppressWarnings("deprecation")
        RequestBody body = RequestBody.create(MEDIA_TYPE_JSON, jsonString);
        Request request = builder.url(url).post(body).build();
        Response response = null;
        try {
            response = HTTP_CLIENT_FAST.newCall(request).execute();
            result = response.body().string();
        } catch (ConnectException e) {
            String errorString = StrUtil.format("执行POST请求异常,url: {},执行耗时:{}ms,异常信息:{}", url, new Date().getTime() - requestTime, ExceptionUtil.getMessage(e));
            log.error(errorString);
            throw new RuntimeException(errorString, e);
        } catch (SocketTimeoutException e) {
            String errorString = StrUtil.format("执行POST请求异常,url: {},执行耗时:{}ms,异常信息:{}", url, new Date().getTime() - requestTime, ExceptionUtil.getMessage(e));
            log.error(errorString);
            throw new RuntimeException(errorString, e);
        } catch (IOException e) {
            String errorString = StrUtil.format("执行POST请求异常,url: {},执行耗时:{}ms,异常信息:{}", url, new Date().getTime() - requestTime, ExceptionUtil.getMessage(e));
            log.error(errorString);
            throw new RuntimeException(errorString, e);
        } catch (Exception e) {
            String errorString = StrUtil.format("执行POST请求异常,url: {},执行耗时:{}ms,异常信息:{}", url, new Date().getTime() - requestTime, ExceptionUtil.getMessage(e));
            log.error(errorString);
            throw new RuntimeException(errorString, e);
        }
        // 调用成功
        if (response.isSuccessful() && Objects.nonNull(response.body())) {
            log.info("执行POST请求成功,url: {},result: {}", url, result);
        } else {
            String errorString = StrUtil.format("执行POST请求失败,url: {},responseCode: {},responseMessage: {}", url, JSON.toJSONString(headers), jsonString,
                response.code(), response.message());
            throw new RuntimeException(errorString);
        }
        return result;
    }

    public static String sendPostByJsonStrFast(String url, String jsonString) {
        return sendPostByJsonStrFast(url, new HashMap<String, String>(), jsonString);
    }

    /**
     * 设置请求头
     * @param builder .
     * @param headers 请求头
     */
    private static void buildHeader(Request.Builder builder, Map<String, String> headers) {
        if (Objects.nonNull(headers) && headers.size() > 0) {
            headers.forEach((k, v) -> {
                if (Objects.nonNull(k) && Objects.nonNull(v)) {
                    builder.addHeader(k, v);
                }
            });
        }
    }

    /**
     * 支持嵌套泛型的POST请求
     * @param  url       链接
     * @param  json      请求json
     * @param  type      嵌套泛型
     * @return           响应对象, 可进行强转。
     * @throws Exception
     */
    public static <T> T sendPostByJsonStr(String url, String jsonString, TypeReference<T> type) throws Exception {
        String result = sendPostByJsonStr(url, jsonString);
        if (Objects.nonNull(result) && Objects.nonNull(type)) {
            return JSON.parseObject(result, type);
        }
        return null;
    }

    public static class OkhttpInterceptor implements Interceptor {
        // 最大重试次数
        private int maxRentry;

        public OkhttpInterceptor(int maxRentry) {
            this.maxRentry = maxRentry;
        }

        /** 递归 2次下发请求,如果仍然失败 则返回 null 但是 intercept must not return null. 返回 null 会报 IllegalStateException 异常 */
        public Response intercept(@NotNull Chain chain) throws IOException {
            return retry(chain, 0);
        }

        private Response retry(Chain chain, int retryCent) throws IOException {
            Request request = chain.request();
            Response response = null;
            try {
                response = chain.proceed(request);
            } catch (Exception e) {
                if (maxRentry > retryCent) {
                    return retry(chain, retryCent + 1);
                }
                if (Objects.isNull(response)) {
                    throw e;
                }
            }
            return response;
        }
    }
}