【Java】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去完成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); }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java自带的HTTP库是通过URLConnection类实现的,它并没有提供连接池的功能。但是,我们可以通过自己编写代码实现连接池。 具体实现方法如下: 1. 创建一个连接池类,用于管理HTTP连接。 2. 在连接池类中创建一个队列,用于存储HTTP连接。 3. 在连接池类中提供一个方法,用于从队列中获取HTTP连接。 4. 在连接池类中提供一个方法,用于将HTTP连接放回队列中。 5. 在使用HTTP连接时,先从连接池中获取连接,使用完毕后再将连接放回连接池中。 6. 当连接池中没有可用连接时,需要创建新的连接。 7. 为了防止连接空闲时间过长而失效,需要定期检查连接的可用性,将失效的连接从连接池中移除。 具体的代码实现可以参考以下例子: ```java import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; import java.util.LinkedList; public class HttpConnectionPool { private static final int MAX_CONNECTIONS = 5; private final LinkedList<HttpURLConnection> connections = new LinkedList<>(); public synchronized HttpURLConnection getConnection(String url) throws IOException { if (connections.isEmpty()) { return (HttpURLConnection) new URL(url).openConnection(); } return connections.removeFirst(); } public synchronized void releaseConnection(HttpURLConnection connection) { if (connections.size() >= MAX_CONNECTIONS) { try { connection.disconnect(); } catch (Exception e) { // ignore } } else { connections.addLast(connection); } } public synchronized void closeAll() { for (HttpURLConnection connection : connections) { try { connection.disconnect(); } catch (Exception e) { // ignore } } connections.clear(); } } ``` 使用方法如下: ```java HttpConnectionPool pool = new HttpConnectionPool(); try { HttpURLConnection connection = pool.getConnection("http://www.example.com"); // use connection pool.releaseConnection(connection); } catch (IOException e) { // handle exception } finally { pool.closeAll(); } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值