HttpClient4使用连接池

本文实验,不是通过springboot配置的,直接引用jar来实现。

工具类供参考。

使用连接池工具类
java 复制代码
package com.figo.test.utils;

import com.figo.util.Logger;
import net.sf.json.JSONObject;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * HttpClient使用连接池
 * HttpClient-4.5.14.jar
 * HttpCore-4.4.16.jar
 */
public class HttpClientUseConnPoolUtil {
	/**
	 * 日志.
	 */
	private static Logger  logger = Logger.getLogger(HttpClientUseConnPoolUtil.class);
//    private static final Logger log = LoggerFactory.getLogger(HttpClientUseConnPoolUtil.class);
    private static volatile CloseableHttpClient httpClient = null;
    static PoolingHttpClientConnectionManager connectionManager;
    private HttpClientUseConnPoolUtil() {
    }
    /**
     * 获取连接池状态,leased已释放,pending请求中,available可用,max最大
     * [leased: 0; pending: 0; available: 3; max: 200]
     * @return
     */
    public static String getStatus(){
    	if(connectionManager!=null) {
    		return connectionManager.getTotalStats().toString();
    	}else
    	{
    		return "";
    	}
    }
    /**
     * 单例,获取httpclient
     * @return
     */
    private static CloseableHttpClient getHttpClient() {
        if (Objects.isNull(httpClient)) {
            synchronized (HttpClientUseConnPoolUtil.class) {
                if (Objects.isNull(httpClient)) {
                    connectionManager = new PoolingHttpClientConnectionManager();
                    // 连接池总容量
                    connectionManager.setMaxTotal(200);
                    // 每个host为一组,此参数用于控制每组中连接池的容量 【重要】
                    connectionManager.setDefaultMaxPerRoute(20);

                    RequestConfig requestConfig = RequestConfig.custom()
                            // 从连接池中取连接超时时间
                            .setConnectionRequestTimeout(30000)
                            // 建立链接超时时间
                            .setConnectTimeout(30000)
                            // 等待读取数据时间
                            .setSocketTimeout(30000)
                            .build();

                    httpClient = HttpClientBuilder.create()
                            // 关闭自动重试
                            .disableAutomaticRetries()
                            // 设置连接池
                            .setConnectionManager(connectionManager)
                            // setConnectionTimeToLive(2, TimeUnit.MINUTES) 设置链接最大存活时间 此属性无效 注意
                            // 回收空闲超过2分钟的链接
                            .evictIdleConnections(2, TimeUnit.MINUTES)
                            // 设置超时时间
                            .setDefaultRequestConfig(requestConfig)
                            .build();
                }
            }
        }
        return httpClient;
    }

    /**
     * 不带header参数的JSON格式Post请求
     *
     * @param url      请求URL
     * @param bodyJson body参数
     * @return 响应体
     */
    public static String httpPost(String url, JSONObject bodyJson) {
        return httpPost(url, null, bodyJson);
    }

    /**
     * 携带header参数的JSON格式Post请求
     *
     * @param url         请求URL
     * @param headerParam header参数
     * @param bodyJson    body参数
     * @return 响应体
     */
    public static String httpPost(String url, Map<String, String> headerParam, JSONObject bodyJson) {
        HttpPost httpPost = new HttpPost(url);
        httpPost.addHeader("Content-Type", "application/json");

        // 组装header
        if (headerParam!=null) {
            for (String key : headerParam.keySet()) {
                httpPost.addHeader(key, headerParam.get(key));
            }
        }

        // 组装body
        if (Objects.nonNull(bodyJson)) {
            StringEntity stringEntity = new StringEntity(bodyJson.toString(), StandardCharsets.UTF_8);
            stringEntity.setContentType("application/json");
            stringEntity.setContentEncoding(StandardCharsets.UTF_8.name());
            httpPost.setEntity(stringEntity);
        }

        // 不要关闭client,会导致连接池被关闭
        CloseableHttpClient client = getHttpClient();
        try (CloseableHttpResponse response = client.execute(httpPost)) {
            HttpEntity entity = response.getEntity();
            String result = EntityUtils.toString(entity, StandardCharsets.UTF_8.name());
            EntityUtils.consume(entity);

            return result;
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new RuntimeException(e);
        }
    }

    /**
     * Get请求
     *
     * @param url 请求URL
     * @return 响应体
     */
    public static String httpGet(String url) {
        return httpGet(url, null, null);
    }

    /**
     * 携带url参数的Get请求
     *
     * @param url      请求URL
     * @param urlParam url参数
     * @return 响应体
     */
    public static String httpGet(String url, Map<String, String> urlParam) {
        return httpGet(url, null, urlParam);
    }

    /**
     * 携带header参数与url参数的Get请求
     *
     * @param url         请求URL
     * @param headerParam header参数
     * @param urlParam    url参数
     * @return 响应体
     */
    public static String httpGet(String url, Map<String, String> headerParam, Map<String, String> urlParam) {
        HttpGet httpGet = new HttpGet();

        // 组装header
        if (headerParam!=null) {
            for (String key : headerParam.keySet()) {
                httpGet.addHeader(key, headerParam.get(key));
            }
        }

        try {
            // 组装url参数
            URIBuilder uriBuilder = new URIBuilder(url);
            if (urlParam!=null) {
                for (String key : urlParam.keySet()) {
                    uriBuilder.addParameter(key, urlParam.get(key));
                }
            }
            httpGet.setURI(uriBuilder.build());
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new RuntimeException(e);
        }

        // 不要关闭client,会导致连接池被关闭
        CloseableHttpClient client = getHttpClient();
        try (CloseableHttpResponse response = client.execute(httpGet)) {
            HttpEntity entity = response.getEntity();
            String result = EntityUtils.toString(entity, StandardCharsets.UTF_8.name());
            EntityUtils.consume(entity);

            return result;
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new RuntimeException(e);
        }
    }
}
不使用连接池工具类
java 复制代码
/**
 * 
 */
package com.figo.test.utils;

import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.net.ssl.SSLContext;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import com.figo.util.Logger;


/**
 * @author figo
 *
 */
public class HttpClientUtils {
	/**
	 * 日志.
	 */
	private static Logger  logger = Logger.getLogger(HttpClientUtils.class);
	/**
	 * 带参数的post请求 .
	 * 
	 * @param url .
	 * @param map .
	 * @return .
	 * @throws Exception .
	 */
	public static HttpResult doPostObject(String url, Map<String, Object> map) throws Exception {
		logger.info("doPostObject请求url="+url+",params="+map);
		// 声明httpPost请求
		HttpPost httpPost = new HttpPost(url);
		// 加入配置信息
		RequestConfig config = RequestConfig.custom().setConnectionRequestTimeout(60000).setConnectTimeout(60000)
				.setSocketTimeout(10000).build();

		httpPost.setConfig(config);

		// 判断map是否为空,不为空则进行遍历,封装from表单对象
		if (map != null) {
			List<NameValuePair> list = new ArrayList<NameValuePair>();
			for (Map.Entry<String, Object> entry : map.entrySet()) {
				list.add(new BasicNameValuePair(entry.getKey(), String.valueOf(entry.getValue())));
			}
			// 构造from表单对象
			UrlEncodedFormEntity urlEncodedFormEntity = new UrlEncodedFormEntity(list, "UTF-8");

			// 把表单放到post里
			httpPost.setEntity(urlEncodedFormEntity);
		}
		SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
			// 信任所有
			public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
				return true;
			}
		}).build();
		SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext);
		HttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
		// 发起请求
		HttpResponse response = httpClient.execute(httpPost);
		String respBody = EntityUtils.toString(response.getEntity(), "UTF-8");
		HttpResult httpResult=new HttpResult(response.getStatusLine().getStatusCode(), respBody);
		try {
			httpClient.getConnectionManager().shutdown();
		} catch (Exception e) {
			logger.error("doPostObject", e);
		}
		return httpResult;
	}
	/**
	 * 带参数的post请求 .
	 * 
	 * @param url .
	 * @param map .
	 * @return .
	 * @throws Exception .
	 */
	public static HttpResult doPost(String url, Map<String, String> map) throws Exception {
		logger.info("doPost请求url="+url+",params="+map);
		// 声明httpPost请求
		HttpPost httpPost = new HttpPost(url);
		// 加入配置信息
		RequestConfig config = RequestConfig.custom().setConnectionRequestTimeout(60000).setConnectTimeout(60000)
				.setSocketTimeout(10000).build();

		httpPost.setConfig(config);

		// 判断map是否为空,不为空则进行遍历,封装from表单对象
		if (map != null) {
			List<NameValuePair> list = new ArrayList<NameValuePair>();
			for (Map.Entry<String, String> entry : map.entrySet()) {
				list.add(new BasicNameValuePair(entry.getKey(), String.valueOf(entry.getValue())));
			}
			// 构造from表单对象
			UrlEncodedFormEntity urlEncodedFormEntity = new UrlEncodedFormEntity(list, "UTF-8");

			// 把表单放到post里
			httpPost.setEntity(urlEncodedFormEntity);
		}
		SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
			// 信任所有
			public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
				return true;
			}
		}).build();
		SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext);
		HttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
		// 发起请求
		HttpResponse response = httpClient.execute(httpPost);
		String respBody = EntityUtils.toString(response.getEntity(), "UTF-8");
		HttpResult httpResult=new HttpResult(response.getStatusLine().getStatusCode(), respBody);
		try {
			httpClient.getConnectionManager().shutdown();
		} catch (Exception e) {
			logger.error("doPostObject", e);
		}
		return httpResult;
	}
	
	
	/**
	 * 带参数的post请求 .
	 * 
	 * @param url .
	 * @param map .
	 * @return .
	 * @throws Exception .
	 */
	public static HttpResult doPostJSON(String url, String jsonParas) throws Exception {
		logger.info("doPost请求url="+url+",params="+jsonParas);
		// 声明httpPost请求
		HttpPost httpPost = new HttpPost(url);
		// 加入配置信息
		RequestConfig config = RequestConfig.custom().setConnectionRequestTimeout(60000).setConnectTimeout(60000)
				.setSocketTimeout(10000).build();

		httpPost.setConfig(config);

		// 判断map是否为空,不为空则进行遍历,封装from表单对象
		if (jsonParas != null) {
			// 把表单放到post里
			httpPost.setEntity(new StringEntity(jsonParas));
		}
		SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
			// 信任所有
			public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
				return true;
			}
		}).build();
		SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext);
		HttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
		// 发起请求
		HttpResponse response = httpClient.execute(httpPost);
		String respBody = EntityUtils.toString(response.getEntity(), "UTF-8");
		HttpResult httpResult=new HttpResult(response.getStatusLine().getStatusCode(), respBody);
		try {
			httpClient.getConnectionManager().shutdown();
		} catch (Exception e) {
			logger.error("doPostObject", e);
		}
		return httpResult;
	}
	
}
测试demo
java 复制代码
package com.figo.test;

import java.time.LocalDateTime;

import com.figo.test.utils.HttpClientUseConnPoolUtil;
import com.figo.test.utils.HttpClientUtils;
import com.figo.test.utils.HttpResult;
import com.figo.util.Logger;

import net.sf.json.JSONObject;

public class HttpClientConnectionPoolTest {
	private static Logger logger = Logger.getLogger(HttpClientConnectionPoolTest.class);

    public static void main(String[] args) {
    	try {
    		 System.out.println("使用连接池开始时间="+LocalDateTime.now());
    		 logger.info("使用连接池开始时间="+LocalDateTime.now());
    		for(int i=0;i<200;i++)
    		{
	    		String url="https://www.baidu.com";
	    		JSONObject bodyJson=new JSONObject();
	    		bodyJson.put("test", "test");
				String resp=HttpClientUseConnPoolUtil.httpPost(url, bodyJson);
				String url1="https://www.huawei.com/";
				String resp1=HttpClientUseConnPoolUtil.httpPost(url1, bodyJson);
				//System.out.print(resp);
	    		
	    		String url2="https://weixin.qq.com/";
				String resp2=HttpClientUseConnPoolUtil.httpPost(url2, bodyJson);
	    		logger.info("status="+HttpClientUseConnPoolUtil.getStatus());

    		}
    		System.out.println(HttpClientUseConnPoolUtil.getStatus());
    		logger.info("status="+HttpClientUseConnPoolUtil.getStatus());
			System.out.println("使用连接池结束时间="+LocalDateTime.now());
			logger.info("使用连接池结束时间="+LocalDateTime.now());
			 System.out.println("不使用连接池开始时间="+LocalDateTime.now());
				logger.info("不使用连接池开始时间="+LocalDateTime.now());

	    		for(int i=0;i<200;i++)
	    		{
		    		String url="https://www.baidu.com";
		    		JSONObject bodyJson=new JSONObject();
		    		bodyJson.put("test", "test");
					HttpResult resp=HttpClientUtils.doPostJSON(url, bodyJson.toString());
					//System.out.print(resp);
					String url1="https://www.huawei.com/";
					HttpResult resp1=HttpClientUtils.doPostJSON(url1, bodyJson.toString());
					String url2="https://weixin.qq.com/";
					HttpResult resp2=HttpClientUtils.doPostJSON(url2, bodyJson.toString());
	    		}
				
				System.out.println("不使用连接池结束时间="+LocalDateTime.now());
				logger.info("不使用连接池结束时间="+LocalDateTime.now());
			
		} catch (Exception e) {
			// TODO: handle exception
		}
	}
}
结果比对:

访问一个网站,各200次请求

使用连接池开始时间=2024-06-12T15:49:40.987

status=[leased: 0; pending: 0; available: 2; max: 200]

使用连接池结束时间=2024-06-12T15:49:49.366

耗时 9秒

不使用连接池开始时间=2024-06-12T15:49:49.366

不使用连接池结束时间=2024-06-12T15:50:03.829

耗时 14秒

访问两个网站,各200次请求

使用连接池开始时间=2024-06-12T16:48:55.125

status=[leased: 0; pending: 0; available: 2; max: 200]

使用连接池结束时间=2024-06-12T16:49:02.582

耗时7秒

不使用连接池开始时间=2024-06-12T16:49:02.583

不使用连接池结束时间=2024-06-12T16:49:25.323

耗时23秒

访问三个网站,各200次请求

使用连接池开始时间=2024-06-12T17:48:17.139

使用连接池结束时间=2024-06-12T17:48:32.314

耗时 15秒

不使用连接池开始时间=2024-06-12T17:48:32.314

不使用连接池结束时间=2024-06-12T17:49:10.594

耗时38秒

结论:使用连接池真的香!

相关推荐
执檀月夜游9 天前
HttpClientUtil 之 ApacheHttpClient 4.5.14替换 commons-httpclient 3.1
httpclient
疯一样的码农15 天前
如何使用Apache HttpClient来执行GET、POST、PUT和DELETE请求
http·httpclient
疯一样的码农19 天前
如何使用Apache HttpClient发送带有HTML表单数据的POST请求
java·apache·httpclient
疯一样的码农19 天前
如何使用Apache HttpClient发送带有基本认证的HTTP请求
http·apache·httpclient
疯一样的码农23 天前
使用Apache HttpClient发起一个GET HTTP请求
网络协议·http·apache·httpclient
疯一样的码农23 天前
使用Apache HttpClient发起一个POST HTTP请求
网络协议·http·apache·httpclient
亿牛云爬虫专家4 个月前
深度解析CancellationToken在HttpClient请求中的应用
http·c#·.net·网络请求·爬虫代理·httpclient·代理ip
响叮当!4 个月前
HttpUtils工具类(二)Apache HttpClient 5 使用详细教程
java·网络·网络协议·http·apache·httpclient
亿牛云爬虫专家4 个月前
NET 8新特性:使用ConfigurePrimaryHttpMessageHandler定制HTTP请求
http·.net·多线程·爬虫代理·httpclient·cookie·代理ip
亿牛云爬虫专家4 个月前
HttpClient在ASP.NET Core中的最佳实践:实现高效的HTTP请求
后端·http·asp.net·数据采集·爬虫代理·httpclient·代理ip