java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > java http连接池的实现

java http连接池的实现方式(带有失败重试等高级功能)

作者:苦荞米

这篇文章主要介绍了java http连接池的实现方式(带有失败重试等高级功能),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

java 本身提供的java.net.HttpURLConnection不支持连接池功能。

如果不想从头实现的话,最好的方式便是引用第三方依赖包,目前是有一个特别不错的,org.apache.httpcomponents:httpclient依赖

引入方式如下:

<dependency>
	<groupId>org.apache.httpcomponents</groupId>
	<artifactId>httpclient</artifactId>
	<version>4.5.13</version>
</dependency>

使用httpclient依赖

在开始使用连接池之前,要学会如何使用httpclient去完成http请求,其请求方式与java的原生http请求完全不同。

其中CloseableHttpClient对象便是我们的http请求连接池,其实声明方式会在下面介绍。

// 引用的包
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.*;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DoHttp {
	private static final Logger LOG = LoggerFactory.getLogger(DoHttp.class);
	// httpGet请求
	public static String get(CloseableHttpClient httpClient, String url) {
	    HttpGet httpGet = new HttpGet(url);
	    return doRequest(httpClient, url, httpGet);
	}
	// httpPost请求 (json格式)
	public static String jsonPost(CloseableHttpClient httpClient, String url, String json) {
	    HttpPost httpPost = new HttpPost(url);
	    httpPost.setHeader("Content-Type", "application/json");
	    StringEntity entity = new StringEntity(json, "UTF-8");
	    httpPost.setEntity(entity);
	    return doRequest(httpClient, url, httpPost);
	}
	// 统一的请求处理逻辑
	private static String doRequest(CloseableHttpClient httpClient, String url, HttpRequestBase httpRequest) {
	    try (CloseableHttpResponse response = httpClient.execute(httpRequest)) {
	        int code = response.getStatusLine().getStatusCode();
	        HttpEntity responseEntity = response.getEntity();
	        String responseBody = null;
	        if (responseEntity != null) {
	            responseBody = EntityUtils.toString(responseEntity);
	        }
	        if (code != 200) {
	            LOG.error("http post error, url: {}, code: {}, result: {}", url, code, responseBody);
	            return null;
	        }
	        return responseBody;
	    } catch (Exception e) {
	        LOG.error("http post error, url: {}", url, e);
	    }
	    return null;
	}
}

连接池的实现

连接池的配置类

如下:

public class HttpPoolConfig {
    /** http连接池大小 */
    public int httpPoolSize;
    /** http连接超时时间 */
    public int httpConnectTimeout;
    /** http连接池等待超时时间 */
    public int httpWaitTimeout;
    /** http响应包间隔超时时间 */
    public int httpSocketTimeout;
    /** http重试次数 */
    public int httpRetryCount;
    /** http重试间隔时间 */
    public int httpRetryInterval;
    /** http监控间隔时间 定时清理 打印连接池状态 */
    public int httpMonitorInterval;
    /** http关闭空闲连接的等待时间 */
    public int httpCloseIdleConnectionWaitTime;
}

连接池实现类

import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpRequest;
import org.apache.http.NoHttpResponseException;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.pool.PoolStats;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLException;
import java.io.InterruptedIOException;
import java.net.UnknownHostException;
import java.util.Map;
import java.util.TimerTask;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * http连接池
 */
public class HttpPool {

    private static final Logger LOG = LoggerFactory.getLogger(HttpPool.class);

    /**
     * 初始化连接池
     * @param httpPoolConfig 配置信息
     */
    public HttpPool(HttpPoolConfig httpPoolConfig) {
        PoolingHttpClientConnectionManager manager = buildHttpManger(httpPoolConfig);
        httpClient = buildHttpClient(httpPoolConfig, manager);
        monitorExecutor = buildMonitorExecutor(httpPoolConfig, manager);
    }

    private final CloseableHttpClient httpClient;
    private final ScheduledExecutorService monitorExecutor;

    /**
     * 连接池管理器
     */
    private PoolingHttpClientConnectionManager buildHttpManger(HttpPoolConfig httpPoolConfig) {
        LayeredConnectionSocketFactory sslSocketFactory = SSLConnectionSocketFactory.getSocketFactory();
        Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("https", sslSocketFactory).build();
        PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager(registry);
        manager.setMaxTotal(httpPoolConfig.httpPoolSize);
        manager.setDefaultMaxPerRoute(httpPoolConfig.httpPoolSize);
        return manager;
    }

    /**
     * 建立httpClient
     */
    private CloseableHttpClient buildHttpClient(HttpPoolConfig httpPoolConfig, PoolingHttpClientConnectionManager manager) {
        // 请求配置
        RequestConfig config = RequestConfig.custom()
                .setConnectTimeout(httpPoolConfig.httpConnectTimeout)
                .setSocketTimeout(httpPoolConfig.httpSocketTimeout)
                .setConnectionRequestTimeout(httpPoolConfig.httpWaitTimeout)
                .build();
        // 失败重试机制
        HttpRequestRetryHandler retryHandler = (e, c, context) -> {
            if (c > httpPoolConfig.httpRetryCount) {
                LOG.error("HttpPool request retry more than {} times", httpPoolConfig.httpRetryCount, e);
                return false;
            }
            if (e == null) {
                LOG.info("HttpPool request exception is null.");
                return false;
            }
            if (e instanceof NoHttpResponseException) {
                //服务器没有响应,可能是服务器断开了连接,应该重试
                LOG.error("HttpPool receive no response from server, retry");
                return true;
            }
            // SSL握手异常
            if (e instanceof InterruptedIOException // 超时
                    || e instanceof UnknownHostException // 未知主机
                    || e instanceof SSLException) { // SSL异常
                LOG.error("HttpPool request error, retry", e);
                return true;
            } else {
                LOG.error("HttpPool request unknown error, retry", e);
            }
            // 对于关闭连接的异常不进行重试
            HttpClientContext clientContext = HttpClientContext.adapt(context);
            HttpRequest request = clientContext.getRequest();
            return !(request instanceof HttpEntityEnclosingRequest);
        };
        // 构建httpClient
        return HttpClients.custom().setDefaultRequestConfig(config)
                .setConnectionManager(manager).setRetryHandler(retryHandler).build();
    }

    /**
     * 建立连接池监视器
     */
    private ScheduledExecutorService buildMonitorExecutor(HttpPoolConfig httpPoolConfig,
                                                          PoolingHttpClientConnectionManager manager) {
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                // 关闭过期连接
                manager.closeExpiredConnections();
                // 关闭空闲时间超过一定时间的连接
                manager.closeIdleConnections(httpPoolConfig.httpCloseIdleConnectionWaitTime, TimeUnit.MILLISECONDS);
                // 打印连接池状态
                PoolStats poolStats = manager.getTotalStats();
                // max:最大连接数, available:可用连接数, leased:已借出连接数, pending:挂起(表示当前等待从连接池中获取连接的线程数量)
                LOG.info("HttpPool status {}", poolStats);
            }
        };
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
        int time = httpPoolConfig.httpMonitorInterval;
        executor.scheduleAtFixedRate(timerTask, time, time, TimeUnit.MILLISECONDS);
        return executor;
    }

    /**
     * 关闭连接池
     */
    public void close() {
        try {
            httpClient.close();
            monitorExecutor.shutdown();
        } catch (Exception e) {
            LOG.error("HttpPool close http client error", e);
        }
    }

    /**
     * 发起get请求
     */
    public String get(String url) { return DoHttp.get(httpClient, url); }

    /**
     * 发起json格式的post请求
     */
    public String jsonPost(String url, String json) { return DoHttp.jsonPost(httpClient, url, json); }
}

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文