package com.panchan.m2.utils; import java.io.IOException; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import org.apache.http.HttpEntity; import org.apache.http.HttpStatus; import org.apache.http.NameValuePair; 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.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; import org.apache.http.config.SocketConfig; import org.apache.http.conn.DnsResolver; import org.apache.http.conn.HttpConnectionFactory; import org.apache.http.conn.ManagedHttpClientConnection; import org.apache.http.conn.routing.HttpRoute; 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.impl.DefaultConnectionReuseStrategy; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy; 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.DefaultHttpResponseParserFactory; import org.apache.http.impl.conn.ManagedHttpClientConnectionFactory; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.impl.conn.SystemDefaultDnsResolver; import org.apache.http.impl.io.DefaultHttpRequestWriterFactory; import org.apache.http.message.BasicNameValuePair; import org.apache.http.util.EntityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @Description HTTP請求(GET/POST)工具 * @version 1.0 * @since JDK1.8 * @author huyanlon * @Created on 2019年3月28日 */ public class HttpClientHelper { private static Logger log = LoggerFactory.getLogger(HttpClientHelper.class); private static PoolingHttpClientConnectionManager poolConnManager = null; private static CloseableHttpClient httpClient =null; public static synchronized CloseableHttpClient getHttpClient(){ if(null==httpClient){ //注冊訪問協議相關的Socket工廠 Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory> create() .register("http",PlainConnectionSocketFactory.getSocketFactory()) .register("https", SSLConnectionSocketFactory.getSystemSocketFactory()).build(); //HttpConnection工廠:皮遏制寫請求/解析響應處理器 HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connectionFactory=new ManagedHttpClientConnectionFactory(DefaultHttpRequestWriterFactory.INSTANCE, DefaultHttpResponseParserFactory.INSTANCE); //DNS解析器 DnsResolver dnsResolver=SystemDefaultDnsResolver.INSTANCE; //創建池化連接管理器 poolConnManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry,connectionFactory,dnsResolver); //默認為Socket配置 SocketConfig defaultSocketConfig=SocketConfig.custom().setTcpNoDelay(true).build(); poolConnManager.setDefaultSocketConfig(defaultSocketConfig); // 設置整個連接池的最大連接數 poolConnManager.setMaxTotal(1000); // 每個路由的默認最大連接,每個路由實際最大連接默認為DefaultMaxPerRoute控制,maxTotal是整個池子最大數 // DefaultMaxPerRoute設置過小無法支持大並發(ConnectPoolTimeoutException: Timeout waiting for connect from pool) 路由是maxTotal的細分 //每個路由最大連接數 poolConnManager.setDefaultMaxPerRoute(1000); //在從連接池獲取連接時,連接不活躍多長時間后需要一次驗證,默認2S poolConnManager.setValidateAfterInactivity(5*1000); //默認請求配置 RequestConfig requestConfig = RequestConfig.custom() //設置連接超時時間 .setConnectTimeout(2*1000) //設置等待數據超時時間 .setSocketTimeout(5*1000) //設置從連接池獲取連接的等待超時時間 .setConnectionRequestTimeout(2000) .build(); HttpClientBuilder httpClientBuilder = HttpClients.custom(); httpClientBuilder.setConnectionManager(poolConnManager) //設置連接池不是共享模式 .setConnectionManagerShared(false) //定期回調空閑連接 .evictIdleConnections(60, TimeUnit.SECONDS) //定期回收過期 .evictExpiredConnections() //連接存活時間,如果不設置,根據長連接信息決定 .setConnectionTimeToLive(60, TimeUnit.SECONDS) //設置默認請求配置 .setDefaultRequestConfig(requestConfig) // 連接重試策略,是否能keepalive .setConnectionReuseStrategy(DefaultConnectionReuseStrategy.INSTANCE) //長連接配置,即獲取長連接生產多少時間 .setKeepAliveStrategy(DefaultConnectionKeepAliveStrategy.INSTANCE) //設置重試次數,默認是3次;當前是禁用 .setRetryHandler(new DefaultHttpRequestRetryHandler(0, false)); httpClient = httpClientBuilder.build(); //JVM停止或重啟時,關閉連接池釋放連接 Runtime.getRuntime().addShutdownHook(new Thread(){ public void run(){ try{ httpClient.close(); }catch(IOException e){ log.info(e.getMessage()); } } }); } return httpClient; } /** * 發送 GET 請求(HTTP),不帶輸入數據 * * @param url * @return */ public static String doGet(String url) { return doGet(url, new HashMap<String, Object>()); } /** * 發送 GET 請求(HTTP),K-V形式 * * @param url * @param params * @return */ public static String doGet(String url, Map<String, Object> params) { long a=System.currentTimeMillis(); String apiUrl = url; StringBuffer param = new StringBuffer(); int i = 0; for (String key : params.keySet()) { if (i == 0) { param.append("?"); } else { param.append("&"); } param.append(key).append("=").append(params.get(key)); i++; } apiUrl += param; String result = null; CloseableHttpClient httpClient = getHttpClient(); CloseableHttpResponse response = null; HttpGet httpPost = null; try { httpPost = new HttpGet(apiUrl); response = httpClient.execute(httpPost); int status = response.getStatusLine().getStatusCode(); if (status ==HttpStatus.SC_OK) { HttpEntity entity = response.getEntity(); if (entity != null) { result = EntityUtils.toString(response.getEntity(), "UTF-8"); } }else{ //不推薦使用CloseableHttpResponse.close關閉連接,他將直接關閉Socket,導致長連接不能復用 EntityUtils.consume(response.getEntity()); } StringBuilder logsb=new StringBuilder(); logsb.append("##################\n"); logsb.append("# 發送報文:"+getPlanText(params)+"\n"); logsb.append("# 響應代碼:"+status+"\n"); logsb.append("# 響應報文:"+result+"\n"); logsb.append("# 耗時:"+(System.currentTimeMillis()-a)+"\n"); logsb.append("########################################################################\n"); log.info(logsb.toString()); return result; } catch (IOException e) { try { if(null !=response ) EntityUtils.consume(response.getEntity()); } catch (IOException e1) { log.error(e.getMessage(), e1); } log.error(e.getMessage(), e); } return result; } public static String doPost(String url) { return doPost(url, new HashMap<String, Object>()); } /** * 發送 POST 請求(HTTP),K-V形式 * * @param url * 接口URL * @param params * 參數map * @return */ public static String doPost(String url, Map<String, Object> params) { long a=System.currentTimeMillis(); String result = null; HttpPost httpPost = new HttpPost(url); CloseableHttpClient httpClient = getHttpClient(); CloseableHttpResponse response = null; try { List<NameValuePair> pairList = new ArrayList<>(params.size()); for (Map.Entry<String, Object> entry : params.entrySet()) { NameValuePair pair = new BasicNameValuePair(entry.getKey(),entry.getValue().toString()); pairList.add(pair); } httpPost.setEntity(new UrlEncodedFormEntity(pairList, Charset.forName("UTF-8"))); response = httpClient.execute(httpPost); int status = response.getStatusLine().getStatusCode(); if (status ==HttpStatus.SC_OK) { HttpEntity entity = response.getEntity(); if (entity != null) { result = EntityUtils.toString(response.getEntity(), "UTF-8"); } }else{ //不推薦使用CloseableHttpResponse.close關閉連接,他將直接關閉Socket,導致長連接不能復用 EntityUtils.consume(response.getEntity()); } StringBuilder logsb=new StringBuilder(); logsb.append("##################\n"); logsb.append("# 發送報文:"+getPlanText(params)+"\n"); logsb.append("# 響應代碼:"+status+"\n"); logsb.append("# 響應報文:"+result+"\n"); logsb.append("# 耗時:"+(System.currentTimeMillis()-a)+"\n"); logsb.append("########################################################################\n"); log.info(logsb.toString()); return result; } catch (Exception e) { try { if(null !=response ) EntityUtils.consume(response.getEntity()); } catch (IOException e1) { log.error(e.getMessage(), e1); } log.error(e.getMessage(), e); } return result; } /** * 參數Map格式化 * @param map * @return */ public static String getPlanText(Map<String, Object> map) { StringBuilder sb=new StringBuilder(); for(Map.Entry<String, Object> entry :map.entrySet()){ sb.append(entry.getKey()). append("="). append(entry.getValue()) .append("&"); } if(sb.length()>0){ sb.deleteCharAt(sb.length()-1); } return sb.toString(); } }
