依賴pom
<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5</version> </dependency>
工具類
package com.hjf.boot.demo.boot_start; import java.io.IOException; import java.io.InterruptedIOException; import java.net.SocketTimeoutException; import java.net.UnknownHostException; import java.nio.charset.Charset; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.HashMap; import java.util.Map; import java.util.Set; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLException; import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import org.apache.http.HttpEntity; import org.apache.http.HttpEntityEnclosingRequest; import org.apache.http.HttpRequest; import org.apache.http.NoHttpResponseException; import org.apache.http.ParseException; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpRequestRetryHandler; import org.apache.http.client.config.RequestConfig; 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.client.protocol.HttpClientContext; import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; import org.apache.http.config.SocketConfig; import org.apache.http.conn.ConnectTimeoutException; import org.apache.http.conn.routing.HttpRoute; 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.NoopHostnameVerifier; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.entity.ContentType; 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.pool.PoolStats; import org.apache.http.protocol.HTTP; import org.apache.http.protocol.HttpContext; import org.apache.http.util.EntityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class HttpClientUtil { private static Logger logger = LoggerFactory.getLogger(HttpClientUtil.class); private final static int CONNECT_TIMEOUT = 4000;// 連接超時毫秒 private final static int SOCKET_TIMEOUT = 10000;// 傳輸超時毫秒 private final static int REQUESTCONNECT_TIMEOUT = 3000;// 獲取請求超時毫秒 private final static int CONNECT_TOTAL = 200;// 最大連接數 private final static int CONNECT_ROUTE = 20;// 每個路由基礎的連接數 private final static String ENCODE_CHARSET = "utf-8";// 響應報文解碼字符集 private final static String RESP_CONTENT = "通信失敗"; private static PoolingHttpClientConnectionManager connManager = null; private static CloseableHttpClient httpClient = null; static { ConnectionSocketFactory plainsf = PlainConnectionSocketFactory.getSocketFactory(); LayeredConnectionSocketFactory sslsf = createSSLConnSocketFactory(); Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory> create() .register("http", plainsf).register("https", sslsf).build(); connManager = new PoolingHttpClientConnectionManager(registry); // 將最大連接數增加到200 connManager.setMaxTotal(CONNECT_TOTAL ); // 將每個路由基礎的連接增加到20 connManager.setDefaultMaxPerRoute(CONNECT_ROUTE ); // 可用空閑連接過期時間,重用空閑連接時會先檢查是否空閑時間超過這個時間,如果超過,釋放socket重新建 立 connManager.setValidateAfterInactivity(30000); // 設置socket超時時間 SocketConfig socketConfig = SocketConfig.custom().setSoTimeout(SOCKET_TIMEOUT ).build(); connManager.setDefaultSocketConfig(socketConfig); RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(REQUESTCONNECT_T IMEOUT ) .setConnectTimeout(CONNECT_TIMEOUT ).setSocketTimeout(SOCKET_TIMEOUT ).build(); HttpRequestRetryHandler httpRequestRetryHandler = new HttpRequestRetryHandler() { public boolean retryRequest(IOException exception, int executionCount, HttpContext context) { if (executionCount >= 3) {// 如果已經重試了3次,就放棄 return false; } if (exception instanceof NoHttpResponseException) {// 如果服務器丟掉了連接,那么就重試 return true; } if (exception instanceof SSLHandshakeException) {// 不要重試SSL握手異常 return false; } if (exception instanceof InterruptedIOException) {// 超時 return true; } 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(connManager).setDefaultRequestConfig(request Config) .setRetryHandler(httpRequestRetryHandler).build(); if (connManager != null && connManager.getTotalStats() != null) { logger.info("now client pool " + connManager.getTotalStats().toString()); } } /** * 發送HTTP_GET請求 * * @see 1)該方法會自動關閉連接,釋放資源 * @see 2)方法內設置了連接和讀取超時時間,單位為毫秒,超時或發生其它異常時方法會自動返回"通信失敗"字符串 * @see 3)請求參數含中文時,經測試可直接傳入中文,HttpClient會自動編碼發給Server,應用時應根據實際效果決 定傳入前是否轉碼 * @see 4)該方法會自動獲取到響應消息頭中[Content-Type:text/html; charset=GBK]的charset值作為響應報文的 解碼字符集 * @see 若響應消息頭中無Content-Type屬性,則會使用HttpClient內部默認的ISO-8859-1作為響應報文的解碼字符 集 * @param requestURL 請求地址(含參數) * @return 遠程主機響應正文 */ public static String sendGetRequest(String reqURL, String param) { if (null != param) { reqURL += "?" + param; } String respContent = RESP_CONTENT ; // 響應內容 // reqURL = URLDecoder.decode(reqURL, ENCODE_CHARSET); HttpGet httpget = new HttpGet(reqURL); CloseableHttpResponse response = null; try { response = httpClient.execute(httpget, HttpClientContext.create()); // 執行GET請求 HttpEntity entity = response.getEntity(); // 獲取響應實體 if (null != entity) { Charset respCharset = ContentType.getOrDefault(entity).getCharset(); respContent = EntityUtils.toString(entity, respCharset); EntityUtils.consume(entity); } } catch (ConnectTimeoutException cte) { logger.error("請求通信[" + reqURL + "]時連接超時,堆棧軌跡如下", cte); } catch (SocketTimeoutException ste) { logger.error("請求通信[" + reqURL + "]時讀取超時,堆棧軌跡如下", ste); } catch (ClientProtocolException cpe) { // 該異常通常是協議錯誤導致:比如構造HttpGet對象時傳入協議不對(將'http'寫成'htp')or響應內容不符合 HTTP協議要求等 logger.error("請求通信[" + reqURL + "]時協議異常,堆棧軌跡如下", cpe); } catch (ParseException pe) { logger.error("請求通信[" + reqURL + "]時解析異常,堆棧軌跡如下", pe); } catch (IOException ioe) { // 該異常通常是網絡原因引起的,如HTTP服務器未啟動等 logger.error("請求通信[" + reqURL + "]時網絡異常,堆棧軌跡如下", ioe); } catch (Exception e) { logger.error("請求通信[" + reqURL + "]時偶遇異常,堆棧軌跡如下", e); } finally { try { if (response != null) response.close(); } catch (IOException e) { e.printStackTrace(); } if (httpget != null) { httpget.releaseConnection(); } } return respContent; } public static String sendPostRequest(String reqURL, String param) { return sendPostRequest(reqURL, param, ""); } /** * 發送HTTP_POST請求 type: 默認是表單請求, * @see 1)該方法允許自定義任何格式和內容的HTTP請求報文體 * @see 2)該方法會自動關閉連接,釋放資源 * @see 3)方法內設置了連接和讀取超時時間,單位為毫秒,超時或發生其它異常時方法會自動返回"通信失敗"字符串 * @see 4)請求參數含中文等特殊字符時,可直接傳入本方法,並指明其編碼字符集encodeCharset參數,方法內部會自 動對其轉碼 * @see 5)該方法在解碼響應報文時所采用的編碼,取自響應消息頭中的[Content-Type:text/html; charset=GBK]的 charset值 * @see 若響應消息頭中未指定Content-Type屬性,則會使用HttpClient內部默認的ISO-8859-1 * @param reqURL 請求地址 * @param reqData 請求參數,若有多個參數則應拼接為param11=value11&22=value22&33=value33的形式 * @param encodeCharset 編碼字符集,編碼請求數據時用之,此參數為必填項(不能為""或null) * @return 遠程主機響應正文 */ public static String sendPostRequest(String reqURL, String param, String type) { String result = RESP_CONTENT ; // 設置請求和傳輸超時時間 HttpPost httpPost = new HttpPost(reqURL); // 這就有可能會導致服務端接收不到POST過去的參數,比如運行在Tomcat6.0.36中的Servlet,所以我們手工指定C ONTENT_TYPE頭消息 if (type != null && type.length() > 0) { httpPost.setHeader(HTTP.CONTENT_TYPE, "application/json; charset=" + ENCODE_CHARSET ); } else { httpPost.setHeader(HTTP.CONTENT_TYPE, "application/x-www-form-urlencoded; charset=" + ENCO DE_CHARSET ); } CloseableHttpResponse response = null; try { if (param != null) { StringEntity entity = new StringEntity(param, ENCODE_CHARSET ); httpPost.setEntity(entity); } logger.info("開始執行請求:" + reqURL); // reqURL = URLDecoder.decode(reqURL, ENCODE_CHARSET); response = httpClient.execute(httpPost, HttpClientContext.create()); HttpEntity entity = response.getEntity(); if (null != entity) { result = EntityUtils.toString(entity, ContentType.getOrDefault(entity).getCharset()); logger.info("執行請求完畢:" + result); EntityUtils.consume(entity); } } catch (ConnectTimeoutException cte) { logger.error("請求通信[" + reqURL + "]時連接超時,堆棧軌跡如下", cte); } catch (SocketTimeoutException ste) { logger.error("請求通信[" + reqURL + "]時讀取超時,堆棧軌跡如下", ste); } catch (ClientProtocolException cpe) { logger.error("請求通信[" + reqURL + "]時協議異常,堆棧軌跡如下", cpe); } catch (ParseException pe) { logger.error("請求通信[" + reqURL + "]時解析異常,堆棧軌跡如下", pe); } catch (IOException ioe) { logger.error("請求通信[" + reqURL + "]時網絡異常,堆棧軌跡如下", ioe); } catch (Exception e) { logger.error("請求通信[" + reqURL + "]時偶遇異常,堆棧軌跡如下", e); } finally { try { if (response != null) response.close(); } catch (IOException e) { e.printStackTrace(); } if (httpPost != null) { httpPost.releaseConnection(); } } return result; } //SSL的socket工廠創建 private static SSLConnectionSocketFactory createSSLConnSocketFactory() { SSLConnectionSocketFactory sslsf = null; // 創建TrustManager() 用於解決javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated X509TrustManager trustManager = new X509TrustManager() { @Override public X509Certificate[] getAcceptedIssuers() { return null; } @Override public void checkClientTrusted(X509Certificate[] arg0, String authType) throws CertificateExce ption { // TODO Auto-generated method stub } @Override public void checkServerTrusted(X509Certificate[] arg0, String authType) throws CertificateExce ption { // TODO Auto-generated method stub } }; SSLContext sslContext; try { sslContext = SSLContext.getInstance(SSLConnectionSocketFactory.TLS); sslContext.init(null, new TrustManager[] {(TrustManager)trustManager}, null); // 創建SSLSocketFactory , // 不校驗域名 ,取代以前驗證規則 sslsf = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return sslsf; } public static Map<HttpRoute, PoolStats> getConnManagerStats() { if (connManager != null) { Set<HttpRoute> routeSet = connManager.getRoutes(); if (routeSet != null && !routeSet.isEmpty()) { Map<HttpRoute, PoolStats> routeStatsMap = new HashMap<HttpRoute, PoolStats>(); for (HttpRoute route : routeSet) { PoolStats stats = connManager.getStats(route); routeStatsMap.put(route, stats); } return routeStatsMap; } } return null; } public static PoolStats getConnManagerTotalStats() { if (connManager != null) { return connManager.getTotalStats(); } return null; } /** * 關閉系統時關閉httpClient */ public static void releaseHttpClient() { try { httpClient.close(); } catch (IOException e) { logger.error("關閉httpClient異常" + e); } finally { if (connManager != null) { connManager.shutdown(); } } } }
