HttpClient Util 工具

package com.XXXXXX.XXXXXXX.utils;

import java.io.InputStream;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolException;
import org.apache.http.client.RedirectStrategy;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
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.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
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.protocol.HttpContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
  * http 链接工具类
  * @ClassName: HttpClientUtil
  * @date 2017年9月16日 上午11:46:45
 */
public class HttpClientUtil {
	private static final Logger logger = LoggerFactory.getLogger(HttpClientUtil.class);
	private static PoolingHttpClientConnectionManager connectionManager;
	private static final Timer connectionManagerTimer = new Timer("SimpleRouteFilter.connectionManagerTimer", true);
	static{
		connectionManager = newConnectionManager();
		connectionManagerTimer.schedule(new TimerTask() {
			
			public void run() {
				if (connectionManager == null) {
					return;
				}
				connectionManager.closeExpiredConnections(); //释放掉过时的连接
			}
		}, 30000, 5000);
	}
	
	/**
	  * 建立链接池
	  * @CreateTime: 2017年9月16日 下午12:01:54
	  * @UpdateTime: 2017年9月16日 下午12:01:54
	  * @return
	 */
	private static PoolingHttpClientConnectionManager newConnectionManager(){
		try{
			final SSLContext sslContext = SSLContext.getInstance("SSL");
			sslContext.init(null, new TrustManager[] { new X509TrustManager() {
				public void checkClientTrusted(X509Certificate[] x509Certificates,
						String s) throws CertificateException {
				}
				public void checkServerTrusted(X509Certificate[] x509Certificates,
						String s) throws CertificateException {
				}
				public X509Certificate[] getAcceptedIssuers() {
					return null;
				}
			} }, new SecureRandom());
			
			RegistryBuilder<ConnectionSocketFactory> registryBuilder = RegistryBuilder
					.<ConnectionSocketFactory> create()
					.register("http", PlainConnectionSocketFactory.INSTANCE)
					.register("https", new SSLConnectionSocketFactory(sslContext));
			final Registry<ConnectionSocketFactory> registry = registryBuilder.build();
			connectionManager = new PoolingHttpClientConnectionManager(registry);
			/**
			 * maxTotal 与 MaxPerRoute 解释:
			 * 	路由的默认最大链接(该值默认为2)限制数量实际使用DefaultMaxPerRoute并不是MaxTotal
			 * 设置太小没法支持大并发(ConnectionPoolTimeoutException: Timeout waiting for connection from pool)
			 * 路由是对maxTotal的细分例如:MaxtTotal=400 DefaultMaxPerRoute=200
			 * 而我只链接到http://sishuok.com时,到这个主机的并发最多只有200;而不是400;
			 * 而我链接到http://sishuok.com 和 http://qq.com时,到每一个主机的并发最多只有200;即加起来是400(但不能超过400)
			 */
			connectionManager.setMaxTotal(400);//设置整个链接池最大链接数
			connectionManager.setDefaultMaxPerRoute(200);//每一个主机的并发最多只有200
			//this.connectionManager.setValidateAfterInactivity(ms);
			return connectionManager;
		}catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
		
	/**
	  * 建立链接
	  * @CreateTime: 2017年9月16日 下午12:02:14
	  * @UpdateTime: 2017年9月16日 下午12:02:14
	  * @param timeOut
	  * @return
	 */
	private static CloseableHttpClient  getHttpClient(int timeOut){
		final RequestConfig requestConfig = RequestConfig.custom()
				//.setSocketTimeout(PropertyPlaceholder.getInt("proxyClient.socket_timeout"))//请求获取数据的超时时间
				.setSocketTimeout(timeOut)//请求获取数据的超时时间
				.setConnectTimeout(200000)//设置链接超时时间,单位毫秒。
				.setConnectionRequestTimeout(3000000)//设置从链接池获取链接超时时间、若是不设置,默认为connection_timeout所以必定要设置并且不能太大 
				.setCookieSpec(CookieSpecs.IGNORE_COOKIES).build();
		HttpClientBuilder httpClientBuilder = HttpClients.custom();
		return httpClientBuilder.setConnectionManager(connectionManager)
				.setDefaultRequestConfig(requestConfig)
				.setRetryHandler(new DefaultHttpRequestRetryHandler(0, false))//不进行重试
				.setRedirectStrategy(new RedirectStrategy() { //重定向问题
					public boolean isRedirected(HttpRequest request, HttpResponse response, HttpContext context)
							throws ProtocolException {
						return false;
					}
					public HttpUriRequest getRedirect(HttpRequest request, HttpResponse response, HttpContext context)
							throws ProtocolException {
						return null;
					}
				}).build();
	}
	
	
	public static String httpRequest(String requestUrl,Map<String, String> params, String requestbody) {
		String result = "";
		CloseableHttpClient httpclient = null;
		try {
			httpclient = getHttpClient(100000000);//默认超时时间为30s
	        if(null != params && params.size() > 0){
	        	requestUrl = requestUrl + "?";
	        	 for(String key : params.keySet()){
	        		 requestUrl = requestUrl + key + "=" + params.get(key) + "&";
	        	 }
	        }
	        logger.info("httpCinet 发送请求地址:{}",requestUrl);
	        HttpPost httpPost = new HttpPost(requestUrl);
	        //设置请求的body
	        if(StringUtils.isNotBlank(requestbody)){
	        	httpPost.setEntity(new StringEntity(requestbody, "UTF-8"));
	        }
	        long startTime = System.currentTimeMillis();
	        CloseableHttpResponse response = httpclient.execute(httpPost);
	        //httpPost.releaseConnection();
	        logger.info("httpCinet 发送请求地址:{},共花费了:{} ms",requestUrl,(System.currentTimeMillis()-startTime));
	        try {
	        	InputStream in=response.getEntity().getContent();
	            result = IOUtils.toString(in);
	            //httpPost.releaseConnection();
	            in.close();//bufferedReader.close();做用就是将用完的链接释放,下次请求能够复用
            } finally {
            	 //这里特别注意的是,若是不使用in.close();而仅仅使用response.close();结果就是链接会被关闭,而且不能被复用,这样就失去了采用链接池的意义
                 response.close();  
            }
		} catch (Exception e) {
			logger.error(e.getMessage(),e);
		}
		return result;
	}
}