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;
	}
}
相關文章
相關標籤/搜索