本文实验,不是通过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秒
结论:使用连接池真的香!