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秒

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

相关推荐
亿牛云爬虫专家2 个月前
深度解析CancellationToken在HttpClient请求中的应用
http·c#·.net·网络请求·爬虫代理·httpclient·代理ip
响叮当!3 个月前
HttpUtils工具类(二)Apache HttpClient 5 使用详细教程
java·网络·网络协议·http·apache·httpclient
亿牛云爬虫专家3 个月前
NET 8新特性:使用ConfigurePrimaryHttpMessageHandler定制HTTP请求
http·.net·多线程·爬虫代理·httpclient·cookie·代理ip
亿牛云爬虫专家3 个月前
HttpClient在ASP.NET Core中的最佳实践:实现高效的HTTP请求
后端·http·asp.net·数据采集·爬虫代理·httpclient·代理ip
奋斗的袍子0074 个月前
SpringBoot:SpringBoot 调用第三方接口的几种方式
java·spring boot·后端·spring·feign·httpclient·restful api
试着奔跑的菜鸟6 个月前
HttpCilent进行Post请求form-data接口,服务方接收不到参数
java·httpclient·form-data
小码农叔叔6 个月前
java中http调用组件深入详解
okhttp·httpclient·resttemplate·restclient·http组件·java中http组件·常用的http调用组件
J_bean7 个月前
阐述使用 HttpClient 进行 http 请求
http·httpclient·http接口调用
社恐Coder8 个月前
IDEA自带 .http 请求工具文档
java·http·intellij-idea·httpclient