当前位置: 首页 > news >正文

HttpClient4使用连接池

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

工具类供参考。

使用连接池工具类
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");// 组装headerif (headerParam!=null) {for (String key : headerParam.keySet()) {httpPost.addHeader(key, headerParam.get(key));}}// 组装bodyif (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();// 组装headerif (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);}}
}
不使用连接池工具类
/*** */
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
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秒

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

相关文章:

  • 从零手写实现 nginx-20-placeholder 占位符 $
  • 谈谈微服务之间的授权方案
  • chrome 您的连接不是私密连接
  • 一条sql的执行流程
  • doc 和 docx 文件的区别
  • 基于YOLOv8的行人检测项目的实现
  • 2024 年 5 月区块链游戏研报:市值增长、玩家参与变迁、迷你游戏兴起
  • WPF界面设计
  • 夹层辊能否解决智能测径仪量程不足的问题?
  • Vulnhub-DC-3
  • MAC系统下Xcode连接iOS真机实现iOS App自动化测试(上)
  • 如何在 Windows 上安装 MySQL(保姆级教程2024版)
  • 404 页面代码
  • Spring系统学习 -Spring IOC 的XML管理Bean之类类型属性赋值、数组类型属性赋值、集合类属性赋值
  • PFA三颈平底烧瓶500ML四氟反应瓶透明可视耐酸碱腐蚀可定制
  • canvas 高仿 Apple Watch 表盘
  • Electron入门介绍
  • Fabric架构演变之路
  • iOS | NSProxy
  • Mac 鼠须管 Rime 输入法 安装五笔输入法 教程
  • PHP的Ev教程三(Periodic watcher)
  • scrapy学习之路4(itemloder的使用)
  • ⭐ Unity 开发bug —— 打包后shader失效或者bug (我这里用Shader做两张图片的合并发现了问题)
  • vue-loader 源码解析系列之 selector
  • vue学习系列(二)vue-cli
  • zookeeper系列(七)实战分布式命名服务
  • 从零开始学习部署
  • 电商搜索引擎的架构设计和性能优化
  • 计算机常识 - 收藏集 - 掘金
  • 开源地图数据可视化库——mapnik
  • 力扣(LeetCode)22
  • 聊聊flink的BlobWriter
  • 面试题:给你个id,去拿到name,多叉树遍历
  • 如何将自己的网站分享到QQ空间,微信,微博等等
  • 使用Maven插件构建SpringBoot项目,生成Docker镜像push到DockerHub上
  • 使用前端开发工具包WijmoJS - 创建自定义DropDownTree控件(包含源代码)
  • 收藏好这篇,别再只说“数据劫持”了
  • 系统认识JavaScript正则表达式
  • 扩展资源服务器解决oauth2 性能瓶颈
  • ​什么是bug?bug的源头在哪里?
  • # 数据结构
  • ###项目技术发展史
  • #if #elif #endif
  • $nextTick的使用场景介绍
  • (1综述)从零开始的嵌入式图像图像处理(PI+QT+OpenCV)实战演练
  • (android 地图实战开发)3 在地图上显示当前位置和自定义银行位置
  • (Redis使用系列) Springboot 使用redis实现接口幂等性拦截 十一
  • (vue)el-tabs选中最后一项后更新数据后无法展开
  • (南京观海微电子)——COF介绍
  • (七)c52学习之旅-中断
  • (七)glDrawArry绘制
  • (十六)、把镜像推送到私有化 Docker 仓库
  • (四)软件性能测试
  • (贪心) LeetCode 45. 跳跃游戏 II
  • (一)pytest自动化测试框架之生成测试报告(mac系统)