关键字:PoolingHttpClientConnectionManager,HttpPost
我为何用写连接池?
- 最近老板要我做一个支付平台统一接口
- 支付平台接口逻辑基本都是对支付宝,微信,唯宝等其他服务的网络请求
我为何要用PoolingHttpClientConnectionManager?
- 在找寻连接池的相关博客资料,引用(4.0)版本的HttpConnectionManager时发现已经Deprecated
- 然后去到Apache官网查询下个版本(4.1)的ThreadSafeClientConnManager,很遗憾也过时了
- 最后找到了(4.2)PoolingHttpClientConnectionManager是ok的
- 既然是新的项目就想用最新的东西,更何况最新版本的HttpClent都已经4.5.*了(我有强迫症)
查询相关资料发现对应博文相对较少,而且并不是完全满足自身需求,以下推荐自身查阅的相关资料
- http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/impl/conn/PoolingHttpClientConnectionManager.html
2.http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html (英文版)
3.http://www.yeetrack.com/?p=782 (惊现中文版)
4.https://my.oschina.net/ramboo/blog/519871/
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpRequest;
import org.apache.http.NameValuePair;
import org.apache.http.NoHttpResponseException;
import org.apache.http.client.ClientProtocolException;
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.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
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.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HttpConnectionManager {
private PoolingHttpClientConnectionManager connectionManager;
private ConnectionSocketFactory plainsf ;
private LayeredConnectionSocketFactory sslsf;
private Registry registry ;
private HttpRequestRetryHandler httpRequestRetryHandler;
private CloseableHttpClient httpClient;
private static HttpConnectionManager httpConnectionManager = new HttpConnectionManager();
private static final Logger logger = LoggerFactory.getLogger(HttpConnectionManager.class);
private HttpConnectionManager(){
/**
* LayeredConnectionSocketFactory是ConnectionSocketFactory的拓展接口。
* 分层socket工厂类可以在明文socket的基础上创建socket连接。分层socket主要用于在代理服务器之间创建安全socket。
* HttpClient使用SSLSocketFactory这个类实现安全socket,SSLSocketFactory实现了SSL/TLS分层
* 自定义的socket工厂类可以和指定的协议(Http、Https)联系起来,用来创建自定义的连接管理器。
*/
plainsf = PlainConnectionSocketFactory.getSocketFactory();
sslsf = SSLConnectionSocketFactory.getSocketFactory();
registry = RegistryBuilder.create()
.register("http", plainsf)
.register("https", sslsf)
.build();
connectionManager = new PoolingHttpClientConnectionManager(registry);
// 将最大连接数增加到400
connectionManager.setMaxTotal(400);
// 将每个路由基础的连接增加到50
connectionManager.setDefaultMaxPerRoute(50);
//请求重试处理
httpRequestRetryHandler = new HttpRequestRetryHandler() {
@Override
public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
if (executionCount >= 1) {// 如果已经重试了1次,就放弃
return false;
}
if (exception instanceof NoHttpResponseException) {// 如果服务器丢掉了连接,那么就重试
return true;
}
if (exception instanceof SSLHandshakeException) {// 不要重试SSL握手异常
return false;
}
if (exception instanceof InterruptedIOException) {// 超时
return false;
}
if (exception instanceof UnknownHostException) {// 目标服务器不可达
return false;
}
if (exception instanceof ConnectTimeoutException) {// 连接被拒绝
return false;
}
if (exception instanceof SSLException) {// ssl握手异常
return false;
}
HttpClientContext clientContext = HttpClientContext.adapt(context);
HttpRequest request = clientContext.getRequest();
// 如果请求是幂等的,就再次尝试
if (!(request instanceof HttpEntityEnclosingRequest)) {
return true;
}
return false;
}
};
httpClient = HttpClients.custom()
.setConnectionManager(connectionManager)
.setRetryHandler(httpRequestRetryHandler)
.build();
}
public static HttpConnectionManager getInstance() {
return httpConnectionManager;
}
public CloseableHttpClient getHttpClient() {
return httpClient;
}
public static String postMapToHttp(
String url,
Map params,
int conTimeOut,
int socketTimeOut,
Map header
){
CloseableHttpClient httpClient = httpConnectionManager.getHttpClient();
HttpPost httpPost = new HttpPost(url);
if(null != header && !header.isEmpty()){
for (Map.Entry entry : header.entrySet()) {
httpPost.addHeader(entry.getKey(), entry.getValue());
}
}
RequestConfig requestConfig = RequestConfig.custom()
.setConnectionRequestTimeout(conTimeOut)
.setConnectTimeout(conTimeOut).setSocketTimeout(socketTimeOut).build();
httpPost.setConfig(requestConfig);
List paramList = generatNameValuePair(params);
CloseableHttpResponse response = null;
try {
httpPost.setEntity(new UrlEncodedFormEntity(paramList, "utf-8"));
response = httpClient.execute(httpPost);
//请求发生异常时,手动关闭连接
if (response.getStatusLine ().getStatusCode () != 200) {
httpPost.abort();
return null;
}
HttpEntity entity =response.getEntity();
//EntityUtils.toString inPutStream close
String result = EntityUtils.toString(entity, "utf-8");
return result;
} catch (ClientProtocolException e) {
logger.error("http发生异常1:" + CommonUntil.getErrorInfoFromException(e));
httpPost.abort();
return null;
} catch (IOException e) {
logger.error("http发生异常2:" + CommonUntil.getErrorInfoFromException(e));
httpPost.abort();
return null;
}finally {
try {
if (response != null)
response.close();
} catch (IOException e) {
logger.error("http发生异常3:" + CommonUntil.getErrorInfoFromException(e));
}
}
}
/**
* 请求数据为:XML字符串
*/
public static String postXmlToHttp(
String url,
String paramXml,
int conTimeOut,
int socketTimeOut,
Map header
){
CloseableHttpClient httpClient = httpConnectionManager.getHttpClient();
HttpPost httpPost = new HttpPost(url);
if(null != header && !header.isEmpty()){
for (Map.Entry entry : header.entrySet()) {
httpPost.addHeader(entry.getKey(), entry.getValue());
}
}
RequestConfig requestConfig = RequestConfig.custom()
.setConnectionRequestTimeout(conTimeOut)
.setConnectTimeout(conTimeOut).setSocketTimeout(socketTimeOut).build();
httpPost.setConfig(requestConfig);
CloseableHttpResponse response = null;
try {
StringEntity se = new StringEntity(paramXml, "utf-8");
httpPost.setEntity(se);
response = httpClient.execute(httpPost);
//请求发生异常时,手动关闭连接
if (response.getStatusLine ().getStatusCode () != 200) {
httpPost.abort();
return null;
}
HttpEntity entity =response.getEntity();
//EntityUtils.toString inPutStream close
String result = EntityUtils.toString(entity, "utf-8");
return result;
} catch (ClientProtocolException e) {
logger.error("http发生异常1:" + CommonUntil.getErrorInfoFromException(e));
httpPost.abort();
return null;
} catch (IOException e) {
logger.error("http发生异常2:" + CommonUntil.getErrorInfoFromException(e));
httpPost.abort();
return null;
}finally {
try {
if (response != null)
response.close();
} catch (IOException e) {
logger.error("http发生异常3:" + CommonUntil.getErrorInfoFromException(e));
}
}
}
/**
* MAP类型数组转换成NameValuePair类型
* @param properties MAP类型数组
* @return NameValuePair类型数组
*/
private static List generatNameValuePair(Map properties) {
ArrayList nameValuePair = new ArrayList(properties.size());
for (Map.Entry entry : properties.entrySet()) {
nameValuePair.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
}
return nameValuePair;
}
/**
* 获取请求头
* @return
*/
public static Map getDefaultHeaders(){
Map header = new HashMap<>();
header.put("Content-Type", "text/html");
header.put("Accept-Charset", "utf-8");
header.put("User-Agent", "Mozilla/4.0");
return header;
}
}
1.注意调整相关连接池参数;
2.连接池可以实现线程保活,静态路由等,由于代码只是个demo,所有没有详细给出,想了解可以查阅我上文贴出的相关文章链接,里面资料有说到,这里我就不把事情搞复杂了。