HttpClient 连接池实践
·
1. 依赖版本
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
2.创建连接池PoolingHttpClientConnectionManager
private PoolingHttpClientConnectionManager getConnectionManager(HttpClientPoolConfig httpClientPoolConfig) {
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
// 注册http和https请求
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", SSLConnectionSocketFactory.getSocketFactory()).build();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
// 连接池最大连接数
connectionManager.setMaxTotal(httpClientPoolConfig.getMaxTotalConnect());
// 路由链接数,每个主机的并发
connectionManager.setDefaultMaxPerRoute(httpClientPoolConfig.getMaxConnectPerRoute());
//定时关闭过期的或空闲的连接,节省服务资源。
new HttpClientConnectionMonitorThread(connectionManager, httpClientPoolConfig.getKey())
.start();
return connectionManager;
}
3.配置RequestConfig的连接时间
private RequestConfig getRequestConfig(HttpClientPoolConfig httpClientPoolConfig) {
return RequestConfig.custom()
.setConnectionRequestTimeout(httpClientPoolConfig.getConnectionRequestTimout())
.setConnectTimeout(httpClientPoolConfig.getConnectTimeout())
.setSocketTimeout(httpClientPoolConfig.getReadTimeout())
.build();
}
4.配置重试策略
private HttpRequestRetryHandler httpRequestRetryHandler(int retryCount) {
return (e, i, httpContext) -> {
// 超过最大重试次数,放弃请求
if (i > retryCount) {
log.info("重试超过{}次,放弃请求。", retryCount);
return false;
}
// 针对特定异常类型判断是否需要重试
if (e instanceof ConnectTimeoutException) {
log.warn("连接超时,正在重试...");
return true;
}
if (e instanceof NoHttpResponseException) {
log.warn("服务器没有响应,正在重试...");
return true;
}
if (e instanceof UnknownHostException) {
log.warn("服务器不可达,正在重试...");
return true;
}
if (e instanceof SSLException) {
log.warn("SSL异常,正在重试...");
return true;
}
if (e instanceof SocketTimeoutException) {
log.warn("读取超时,正在重试...");
return true;
}
if (e instanceof InterruptedIOException) {
log.warn("请求被中断,不重试。");
return false;
}
// 获取请求上下文,进一步判断请求类型
HttpClientContext context = HttpClientContext.adapt(httpContext);
HttpRequest request = context.getRequest();
if (!(request instanceof HttpEntityEnclosingRequest)) {
// 如果请求不是带有实体的请求(即幂等请求),可以重试
log.info("非实体请求,正在重试...");
return true;
}
// 如果是带有请求体的请求,避免重试
log.info("实体请求,不重试。");
return false;
};
}
注意事项
-
避免非池化的 HttpClient:每次创建新实例会增加资源开销,影响性能。
-
合理配置超时参数:设置超时值可以避免长时间等待。
-
线程安全:
PoolingHttpClientConnectionManager是线程安全的,多个线程可以安全地复用同一实例。 -
清理机制:确保空闲和过期连接的清理机制正常工作,避免连接池内存泄漏。
以上配置和实践适用于 4.5.13 版本,可以在高并发环境下显著提升性能和资源利用率。
5.代码示例
5.1 HttpClientPoolConfig配置类
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import javax.xml.bind.DatatypeConverter;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Map;
@Data
@Component
@ConfigurationProperties(prefix = "spring.http-client.pool")
public class HttpClientPoolConfig {
/**
* 连接池的最大连接数
*/
private int maxTotalConnect = 128;
/**
* 同路由的并发数
*/
private int maxConnectPerRoute = 64;
/**
* 从连接池获取连接的超时时间,不宜过长,默认3s
*/
private int connectionRequestTimout = 3 * 1000;
/**
* 客户端和服务器建立连接超时,默认5s
*/
private int connectTimeout = 5 * 1000;
/**
* 指客户端从服务器读取数据包的间隔超时时间,不是总读取时间,默认30s
*/
private int readTimeout = 30 * 1000;
/**
* 字符集
*/
private String charset = "UTF-8";
/**
* 重试次数,默认2次
*/
private int retryTimes = 2;
/**
* 针对不同的地址,特别设置不同的长连接保持时间
*/
private Map<String, Integer> keepAliveTargetHost;
/**
* 针对不同的地址,特别设置不同的长连接保持时间,单位 s
*/
private int keepAliveTime = 60;
/**
* HTTP代理配置
*/
private HttpProxy httpProxy;
@Override
public String toString() {
return "HttpConfig{" +
"maxTotalConnect=" + maxTotalConnect +
", maxConnectPerRoute=" + maxConnectPerRoute +
", connectionRequestTimout=" + connectionRequestTimout +
", connectTimeout=" + connectTimeout +
", readTimeout=" + readTimeout +
", httpProxy=" + (httpProxy == null ? "null" : httpProxy.toString()) +
'}';
}
public String getKey() {
String key = this.toString();
try {
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
byte[] digestBytes = messageDigest.digest(key.getBytes(StandardCharsets.UTF_8));
key = DatatypeConverter.printHexBinary(digestBytes);
} catch (Exception ignore) {
// 此处异常无需处理,当出现异常时直接返回this.toString()对工具功能无任何影响
}
return key;
}
@Data
@Configuration
@ConfigurationProperties(prefix = "proxy")
public class HttpProxy {
/**
* 代理主机名
*/
private String httpProxyHost;
/**
* 代理端口
*/
private Integer httpProxyPort;
/**
* 登录凭证-用户名
*/
private String userName;
/**
* 登录凭证-密码
*/
private String password;
@Override
public String toString() {
return "HttpProxy{" +
"httpProxyHost='" + httpProxyHost + '\'' +
", httpProxyPort=" + httpProxyPort +
", userName='" + userName + '\'' +
", password='" + password + '\'' +
'}';
}
}
}
5.2 HttpUtil方法
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.UnicodeUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.*;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeaderElementIterator;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
import javax.net.ssl.SSLException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.net.SocketTimeoutException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.nio.charset.Charset;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
@Slf4j
public class HttpUtil {
/**
* http请求方式
*/
public enum Https {
/**
* post
*/
POST,
/**
* get
*/
GET;
}
/**
* 编码格式 发送编码格式统一用 utf-8
*/
private static final String CHARSET_UTF8 = "UTF-8";
private static final Object LOCK = new Object();
private static final Map<String, CloseableHttpClient> HTTP_CLIENTS = new ConcurrentHashMap<>();
private static final HttpClientPoolConfig DEFAULT_REST_CONFIG = new HttpClientPoolConfig();
private CloseableHttpClient httpClient;
private static HttpUtil getInstance() {
return getInstance(DEFAULT_REST_CONFIG);
}
private static HttpUtil getInstance(HttpClientPoolConfig httpClientPoolConfig) {
return new HttpUtil(httpClientPoolConfig);
}
private HttpUtil(HttpClientPoolConfig httpClientPoolConfig) {
init(httpClientPoolConfig);
}
private void init(HttpClientPoolConfig httpClientPoolConfig) {
if (httpClientPoolConfig == null) {
// 如果传入的配置为null则给个默认配置
httpClientPoolConfig = DEFAULT_REST_CONFIG;
}
String configKey = httpClientPoolConfig.getKey();
// 判断内存中是否已存在对应配置的RestTemplate实例,当不存在对应配置的实例时执行内部逻辑,构建一个新的RestTemplate实例
if (!HTTP_CLIENTS.containsKey(configKey)) {
// 加锁
synchronized (LOCK) {
// 二次判断内存中是否存在对应实例
if (!HTTP_CLIENTS.containsKey(configKey)) {
HttpClientBuilder httpClientBuilder = HttpClients.custom()
.setConnectionManager(getConnectionManager(httpClientPoolConfig))
.setDefaultRequestConfig(getRequestConfig(httpClientPoolConfig))
.setRetryHandler(httpRequestRetryHandler(httpClientPoolConfig.getRetryTimes()))
.setKeepAliveStrategy(connectionKeepAliveStrategy(httpClientPoolConfig))
.evictExpiredConnections();
HttpClientPoolConfig.HttpProxy httpProxy = httpClientPoolConfig.getHttpProxy();
if (httpProxy != null) {
configHttpProxy(httpClientBuilder, httpClientPoolConfig);
}
CloseableHttpClient client = httpClientBuilder
.build();
HTTP_CLIENTS.put(configKey, client);
}
}
}
this.httpClient = HTTP_CLIENTS.get(configKey);
}
private PoolingHttpClientConnectionManager getConnectionManager(HttpClientPoolConfig httpClientPoolConfig) {
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
// 注册http和https请求
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", SSLConnectionSocketFactory.getSocketFactory()).build();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
// 连接池最大连接数
connectionManager.setMaxTotal(httpClientPoolConfig.getMaxTotalConnect());
// 路由链接数,每个主机的并发
connectionManager.setDefaultMaxPerRoute(httpClientPoolConfig.getMaxConnectPerRoute());
//定时关闭过期的或空闲的连接,节省服务资源。
new HttpClientConnectionMonitorThread(connectionManager, httpClientPoolConfig.getKey())
.start();
return connectionManager;
}
/**
* 请求器的配置
* @return
*/
private RequestConfig getRequestConfig(HttpClientPoolConfig httpClientPoolConfig) {
return RequestConfig.custom()
.setConnectionRequestTimeout(httpClientPoolConfig.getConnectionRequestTimout())
.setConnectTimeout(httpClientPoolConfig.getConnectTimeout())
.setSocketTimeout(httpClientPoolConfig.getReadTimeout())
.build();
}
private HttpClientBuilder configHttpProxy(HttpClientBuilder httpClientBuilder, HttpClientPoolConfig httpClientPoolConfig){
HttpClientPoolConfig.HttpProxy httpProxy = httpClientPoolConfig.getHttpProxy();
if (httpProxy != null) {
String httpProxyHost = httpProxy.getHttpProxyHost();
Integer httpProxyPort = httpProxy.getHttpProxyPort();
HttpHost proxy = new HttpHost(httpProxyHost, httpProxyPort);
httpClientBuilder.setProxy(proxy);
String userName = httpProxy.getUserName();
String password = httpProxy.getPassword();
if (userName != null && password != null) {
CredentialsProvider provider = new BasicCredentialsProvider();
provider.setCredentials(new AuthScope(proxy), new UsernamePasswordCredentials(userName, password));
httpClientBuilder.setDefaultCredentialsProvider(provider);
}
}
return httpClientBuilder;
}
private ConnectionKeepAliveStrategy connectionKeepAliveStrategy(HttpClientPoolConfig httpClientPoolConfig) {
return (response, context) -> {
// Honor 'keep-alive' header
HeaderElementIterator it = new BasicHeaderElementIterator(response.headerIterator(HTTP.CONN_KEEP_ALIVE));
while (it.hasNext()) {
HeaderElement he = it.nextElement();
String param = he.getName();
String value = he.getValue();
if (value != null && "timeout".equalsIgnoreCase(param)) {
try {
return Long.parseLong(value) * 1000;
}
catch (NumberFormatException ignore) {
log.error("解析长连接过期时间异常", ignore);
}
}
}
HttpHost target = (HttpHost) context.getAttribute(HttpClientContext.HTTP_TARGET_HOST);
// 如果请求目标地址,单独配置了长连接保持时间,使用该配置
Optional<Map.Entry<String, Integer>> any = Optional
.ofNullable(httpClientPoolConfig.getKeepAliveTargetHost()).orElseGet(HashMap::new).entrySet()
.stream().filter(e -> e.getKey().equalsIgnoreCase(target.getHostName())).findAny();
// 否则使用默认长连接保持时间
return any.map(en -> en.getValue() * 1000L).orElse(httpClientPoolConfig.getKeepAliveTime() * 1000L);
};
}
private HttpRequestRetryHandler httpRequestRetryHandler(int retryCount) {
return (e, i, httpContext) -> {
// 超过最大重试次数,放弃请求
if (i > retryCount) {
log.info("重试超过{}次,放弃请求。", retryCount);
return false;
}
// 针对特定异常类型判断是否需要重试
if (e instanceof ConnectTimeoutException) {
log.warn("连接超时,正在重试...");
return true;
}
if (e instanceof NoHttpResponseException) {
log.warn("服务器没有响应,正在重试...");
return true;
}
if (e instanceof UnknownHostException) {
log.warn("服务器不可达,正在重试...");
return true;
}
if (e instanceof SSLException) {
log.warn("SSL异常,正在重试...");
return true;
}
if (e instanceof SocketTimeoutException) {
log.warn("读取超时,正在重试...");
return true;
}
if (e instanceof InterruptedIOException) {
log.warn("请求被中断,不重试。");
return false;
}
// 获取请求上下文,进一步判断请求类型
HttpClientContext context = HttpClientContext.adapt(httpContext);
HttpRequest request = context.getRequest();
if (!(request instanceof HttpEntityEnclosingRequest)) {
// 如果请求不是带有实体的请求(即幂等请求),可以重试
log.info("非实体请求,正在重试...");
return true;
}
// 如果是带有请求体的请求,避免重试
log.info("实体请求,不重试。");
return false;
};
}
/**
* 创建访问的地址 拼接url
* @param url 基础url
* @param params 请求参数
* @return URI
*/
private static URI getUrl(String url, Map<String, Object> params, Charset charset)
throws HttpUtilClosableException {
// 创建访问的地址
try {
URIBuilder uriBuilder = new URIBuilder(url).setCharset(charset);
if (params != null) {
Set<Map.Entry<String, Object>> entrySet = params.entrySet();
for (Map.Entry<String, Object> entry : entrySet) {
String key = entry.getKey();
Object value = entry.getValue();
if (value != null) {
uriBuilder.setParameter(key, String.valueOf(value));
}
}
}
return uriBuilder.build();
}
catch (URISyntaxException e) {
throw new HttpUtilClosableException(e.getMessage(), e);
}
}
/**
* 设置请求头
* @param httpRequest http请求
* @param headers 请求头参数
*/
private static HttpRequestBase setHeader(HttpRequestBase httpRequest, Map<String, String> headers) {
// 设置请求头
if (headers != null) {
Set<Map.Entry<String, String>> entrySet = headers.entrySet();
for (Map.Entry<String, String> entry : entrySet) {
// 设置请求头到 HttpRequestBase
httpRequest.setHeader(entry.getKey(), entry.getValue());
}
}
return httpRequest;
}
/**
* get请求方法 带请求头和请求参数
* @param url 请求地址
* @param headers 请求头
* @param params 请求参数
* @return HttpGet
*/
private static HttpGet createHttpGetRequest(String url, Map<String, String> headers, Map<String, Object> params, Charset charset)
throws HttpUtilClosableException {
// 创建访问的地址
URI uri = getUrl(url, params, charset);
// 创建http对象
HttpGet httpGet = new HttpGet(uri);
// 设置请求头
return (HttpGet) setHeader(httpGet, headers);
}
/**
* post请求 (key-value格式)
* @param url 请求地址
* @param headers 请求头
* @param params 请求参数
* @return HttpPost
*/
private static HttpPost createHttpPostRequest(String url, Map<String, String> headers, Map<String, Object> params,
Object jsonBody, HttpEntity httpEntity, String contentType, Charset charset)
throws HttpUtilClosableException {
// 第一步:根据参数决定URI
URI uri = getUrl(url, params, charset);
HttpPost httpPost = new HttpPost(uri);
// 第二步:设置HTTP请求体(Body)
if (httpEntity != null) {
httpPost.setEntity(httpEntity);
} else if (jsonBody != null) {
setJsonBody(httpPost, jsonBody, contentType, charset);
} else if (params != null) {
setParamsBody(httpPost, params, charset);
}
// 第三步:设置请求头
return (HttpPost) setHeader(httpPost, headers);
}
// 设置JSON请求体的辅助方法
private static void setJsonBody(HttpPost httpPost, Object jsonBody, String contentType, Charset charset) {
// 将jsonBody转换为字符串
String json = (jsonBody instanceof String) ? (String) jsonBody : JSONUtil.toJsonStr(jsonBody);
// 默认设置Content-Type为application/json
httpPost.setHeader(HTTP.CONTENT_TYPE, "application/json");
// 设置请求体内容
StringEntity entity = new StringEntity(json, charset);
if (contentType != null) {
entity.setContentType(contentType);
}
httpPost.setEntity(entity);
}
// 设置表单参数请求体的辅助方法
private static void setParamsBody(HttpPost httpPost, Map<String, Object> params, Charset charset) {
List<NameValuePair> nvp = new ArrayList<>();
for (Map.Entry<String, Object> entry : params.entrySet()) {
if (entry.getValue() != null) {
nvp.add(new BasicNameValuePair(entry.getKey(), String.valueOf(entry.getValue())));
}
}
httpPost.setEntity(new UrlEncodedFormEntity(nvp, charset));
}
public static Builder builder() {
return new Builder();
}
public static Builder builder(HttpClientPoolConfig httpClientPoolConfig) {
return new Builder(httpClientPoolConfig);
}
/**
* 资源关闭
*/
public static void close(HttpUtil httpUtil) {
try {
if (httpUtil.httpClient != null) {
httpUtil.httpClient.close();
}
}
catch (IOException e) {
log.error("Exception:", e);
}
}
/**
* 构造http请求函数
*/
public static class Builder {
/**
* 请求参数(key-value格式)
*/
private Map<String, Object> params;
/**
* 参数编码格式
*/
private Charset charset = Charset.forName(CHARSET_UTF8);
/**
* 是否打印请求响应日志.
*/
private Boolean logging = false;
/**
* 请求参数(json格式)
*/
private Object jsonBody;
private HttpEntity httpEntity;
/**
* 请求头
*/
private Map<String, String> headers;
/**
* 参数返回格式
*/
private String contentType;
/**
* http请求
*/
private HttpUriRequest httpRequest;
/**
* http响应
*/
private CloseableHttpResponse httpResponse;
/**
* HttpUtils工具类实例化
*/
private HttpUtil httpUtil;
/**
* 初始化
*/
private Builder() {
this.httpUtil = HttpUtil.getInstance();
}
private Builder(HttpClientPoolConfig httpClientPoolConfig) {
this.httpUtil = HttpUtil.getInstance(httpClientPoolConfig);
}
/**
* 添加参数
* @param key
* @param value
* @return
*/
public Builder addParams(String key, Object value) {
if (this.params == null) {
this.params = new HashMap<>(5);
}
this.params.put(key, value);
return this;
}
public Builder setLogging(boolean logging) {
this.logging = logging;
return this;
}
/**
* 添加参数
* @param params
* @return
*/
public Builder addParams(Map<String, Object> params) {
if (this.params == null) {
this.params = new HashMap<>(5);
}
this.params.putAll(params);
return this;
}
/**
* 设置参数编码格式
* @param charset
* @return
*/
public Builder setCharset(String charset) {
this.charset = Charset.forName(charset);
return this;
}
/**
* 添加参数
*/
public Builder setJsonBody(Object object) {
this.jsonBody = object;
return this;
}
public Builder setEntity(HttpEntity httpEntity) {
this.httpEntity = httpEntity;
return this;
}
/**
* 添加请求头
*/
public Builder addHeaders(String key, String value) {
if (this.headers == null) {
this.headers = new HashMap<>(5);
}
this.headers.put(key, value);
return this;
}
/**
* 设置返回格式
*/
public Builder setContentType(String contentType) {
this.contentType = contentType;
return this;
}
/**
* http请求
* @param url 请求地址
* @param https 请求方法
* @return
*/
public Builder doHttp(String url, Https https) {
switch (https) {
case POST:
return doPost(url);
case GET:
return doGet(url);
default:
return this;
}
}
/**
* get请求方法 带请求头和请求参数
* @param url 请求地址
* @return HttpGet
*/
public Builder doGet(String url) {
try {
this.httpRequest = HttpUtil.createHttpGetRequest(url, this.headers, this.params, this.charset);
}
catch (HttpUtilClosableException e) {
log.error("Exception:", e);
}
return execute();
}
/**
* post请求 (key-value格式)
* @param url 请求地址
* @return HttpPost
*/
public Builder doPost(String url) {
try {
this.httpRequest = HttpUtil.createHttpPostRequest(url, this.headers, this.params, this.jsonBody, this.httpEntity,
this.contentType, this.charset);
}
catch (HttpUtilClosableException e) {
log.error("Exception:", e);
}
return execute();
}
public Builder execute(HttpUriRequest request) {
try {
CloseableHttpClient httpClient = this.httpUtil.httpClient;
this.traceRequest();
this.httpResponse = httpClient.execute(request);
}
catch (IOException e) {
log.error("Exception:", e);
}
return callback();
}
/**
* http请求
* @return
*/
private Builder execute() {
if (this.httpRequest != null) {
try {
// 请求client
CloseableHttpClient httpClient = this.httpUtil.httpClient;;
traceRequest();
this.httpResponse = httpClient.execute(this.httpRequest);
}
catch (IOException e) {
log.error("Exception:", e);
}
}
return callback();
}
/**
* 响应参数校验
* @return
*/
private Builder callback() {
if (this.httpResponse != null) {
// 响应状态
StatusLine status = this.httpResponse.getStatusLine();
if (status.getStatusCode() != HttpStatus.SC_OK) {
try {
log.error("HttpUtil请求异常状态码>>" + status.getStatusCode());
throw new HttpUtilClosableException(status.getReasonPhrase());
}
catch (HttpUtilClosableException e) {
log.error("Exception:", e);
}
finally {
this.httpResponse = null;
}
}
}
return this;
}
/**
* 获取请求连接
* @return
*/
public String getUrl() {
if (this.httpResponse != null) {
return this.httpRequest.getURI().toString();
}
return null;
}
/**
* 获取响应头
* @param map
* @param keys
* @return
*/
public Builder getHeaders(Map<String, String> map, String... keys) {
if (this.httpResponse != null) {
for (String key : keys) {
Header[] headers = this.httpResponse.getHeaders(key);
for (Header header : headers) {
map.put(header.getName(), header.getValue());
}
}
}
return this;
}
/**
* 获取响应头
* @param map
* @return
*/
public Builder getHeaders(Map<String, String> map) {
if (this.httpResponse != null) {
Header[] headers = this.httpResponse.getAllHeaders();
for (Header header : headers) {
map.put(header.getName(), header.getValue());
}
}
return this;
}
/**
* 返回数据 byte
* @return
*/
public byte[] toByte() {
byte[] bytes = null;
if (this.httpResponse != null) {
try {
bytes = EntityUtils.toByteArray(this.httpResponse.getEntity());
}
catch (IOException e) {
log.error("Exception:", e);
}
finally {
try {
this.httpResponse.close();
}
catch (IOException e) {
log.error("Exception:", e);
}
}
}
return bytes;
}
/**
* 返回数据 InputStream
* @return
*/
public InputStream toInput() {
InputStream input = null;
if (this.httpResponse != null) {
try {
byte[] bytes = EntityUtils.toByteArray(this.httpResponse.getEntity());
// 使用InputStream对象时,再从bytes转化回来
input = new ByteArrayInputStream(bytes);
}
catch (IOException e) {
log.error("Exception:", e);
}
finally {
try {
this.httpResponse.close();
}
catch (IOException e) {
log.error("Exception:", e);
}
}
}
return input;
}
/**
* 返回数据 String
* @return
*/
public String toStr() {
return toStr(CHARSET_UTF8);
}
/**
* 返回数据 String
* @param defaultCharset
* @return
*/
public String toStr(String defaultCharset) {
String str = null;
if (this.httpResponse != null) {
try {
str = EntityUtils.toString(this.httpResponse.getEntity(), defaultCharset);
traceResponse(str);
}
catch (IOException e) {
log.error("Exception:", e);
}
finally {
try {
this.httpResponse.close();
}
catch (IOException e) {
log.error("Exception:", e);
}
}
}
return str;
}
/**
* 返回参数 map格式
* @return
*/
public <T> Map<String, T> toMap() {
Map<String, T> map = null;
if (this.httpResponse != null) {
try {
map = (Map<String, T>) JSONUtil.parseObj(this.toStr());
}
catch (Exception e) {
log.error("Exception:", e);
}
}
return map;
}
/**
* 返回参数 clazz
* @return
*/
public <T> T toObject(Class<T> clazz) {
T t = null;
if (this.httpResponse != null) {
try {
t = JSONUtil.toBean(this.toStr(), clazz);
}
catch (Exception e) {
log.error("Exception:", e);
}
}
return t;
}
/**
* 返回参数 list
* @return
*/
public <T> List<T> toArray(Class<T> clazz) {
List<T> array = null;
if (this.httpResponse != null) {
try {
JSONUtil.toList(toStr(), clazz);
}
catch (Exception e) {
log.error("Exception:", e);
}
}
return array;
}
/**
* 返回参数 response
* @return
*/
public <S> Result<S> toResponseObject(Class<S> cls) {
Result<S> result = null;
if (this.httpResponse != null) {
try {
result = Result.data(JSONUtil.toBean(this.toStr(), cls));
}
catch (Exception e) {
log.error("Exception:", e);
}
}
return result;
}
/**
* 返回参数 response array
* @return
*/
public <S> Result<List<S>> toResponseArray(Class<S> cls) {
Result<List<S>> result = null;
if (this.httpResponse != null) {
try {
result = Result.data(JSONUtil.toList(this.toStr(), cls));
}
catch (Exception e) {
log.error("Exception:", e);
}
}
return result;
}
/**
* 释放资源
*/
public void close() {
if (this.httpResponse != null) {
try {
this.httpResponse.close();
}
catch (IOException e) {
log.error("Exception:", e);
}
}
}
private void traceRequest() {
if (this.logging) {
log.info("===========================request begin================================================");
log.info("Request URI: {}", this.httpRequest.getURI());
log.info("Request Method: {}", this.httpRequest.getMethod());
if (CollUtil.isNotEmpty(this.params)) {
log.info("Request Param: {}", JSONUtil.toJsonStr(this.params));
}
if (ObjectUtil.isNotEmpty(this.jsonBody)) {
log.info("Request Body: {}", JSONUtil.toJsonStr(this.jsonBody));
}
log.info("==========================request end================================================");
}
}
private void traceResponse(String responseStr) {
if (this.logging) {
log.info("============================response begin==========================================");
log.info("Status code : {}", this.httpResponse.getStatusLine().getStatusCode());
log.info("Status text : {}", this.httpResponse.getStatusLine().getReasonPhrase());
log.info("ResponseBody : {}", UnicodeUtil.toString(responseStr));
log.info("=======================response end=================================================");
}
}
}
/********************************** 【内部资源回收】- 开始 **********************************/
/**
* 连接池监控线程。作用:定时关闭过期的或空闲的连接,节省服务资源
*/
private static class HttpClientConnectionMonitorThread extends Thread {
private final PoolingHttpClientConnectionManager connManager;
private volatile boolean shutdown = false;
public HttpClientConnectionMonitorThread(PoolingHttpClientConnectionManager connManager, String configKey) {
super();
this.setName("http-connection-monitor-" + configKey);
this.setDaemon(true);
this.connManager = connManager;
}
@Override
public void run() {
while (!shutdown) {
try {
synchronized (this) {
// 等待5秒(官方示例)
wait(5000);
// 关闭过期的链接
connManager.closeExpiredConnections();
// 选择关闭 空闲30秒的链接
connManager.closeIdleConnections(30, TimeUnit.SECONDS);
}
} catch (InterruptedException ex) {
// 发生异常时进行捕获,不会导致回收线程中断,会继续下一轮回收工作
Thread.currentThread().interrupt();
}
}
}
}
/********************************** 【内部资源回收】- 结束 **********************************/
}
更多推荐



所有评论(0)