HTTP连接池实例

HTTP连接池实例

关键字:PoolingHttpClientConnectionManager,HttpPost


我为何用写连接池?

  • 最近老板要我做一个支付平台统一接口
  • 支付平台接口逻辑基本都是对支付宝,微信,唯宝等其他服务的网络请求

我为何要用PoolingHttpClientConnectionManager?

  • 在找寻连接池的相关博客资料,引用(4.0)版本的HttpConnectionManager时发现已经Deprecated
  • 然后去到Apache官网查询下个版本(4.1)的ThreadSafeClientConnManager,很遗憾也过时了
  • 最后找到了(4.2)PoolingHttpClientConnectionManager是ok的
  • 既然是新的项目就想用最新的东西,更何况最新版本的HttpClent都已经4.5.*了(我有强迫症)

查询相关资料发现对应博文相对较少,而且并不是完全满足自身需求,以下推荐自身查阅的相关资料

  1. 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<ConnectionSocketFactory> 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.<ConnectionSocketFactory>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<String, String> params,
			int conTimeOut,
			int socketTimeOut,
			Map<String,String> header
			){
		CloseableHttpClient httpClient = httpConnectionManager.getHttpClient();
		HttpPost httpPost = new HttpPost(url);
		if(null != header && !header.isEmpty()){
			for (Map.Entry<String, String> entry : header.entrySet()) {
				httpPost.addHeader(entry.getKey(), entry.getValue());
			}
		}
		RequestConfig requestConfig = RequestConfig.custom()
                .setConnectionRequestTimeout(conTimeOut)
                .setConnectTimeout(conTimeOut).setSocketTimeout(socketTimeOut).build();
		httpPost.setConfig(requestConfig);
		List<NameValuePair> 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<String,String> header
			){
		CloseableHttpClient httpClient = httpConnectionManager.getHttpClient();
		HttpPost httpPost = new HttpPost(url);
		if(null != header && !header.isEmpty()){
			for (Map.Entry<String, String> 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<NameValuePair> generatNameValuePair(Map<String, String> properties) {
    	ArrayList<NameValuePair> nameValuePair = new ArrayList<NameValuePair>(properties.size());
        for (Map.Entry<String, String> entry : properties.entrySet()) {
        	nameValuePair.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
        }
        return nameValuePair;
    }
    
    /**
     * 获取请求头
     * @return
     */
    public static Map<String,String> getDefaultHeaders(){
    	Map<String,String> 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,所有没有详细给出,想了解可以查阅我上文贴出的相关文章链接,里面资料有说到,这里我就不把事情搞复杂了。

posted on 2017-04-10 14:21  LinLive  阅读(497)  评论(0)    收藏  举报

导航