生活随笔
收集整理的這篇文章主要介紹了
http工具类(支持https,连接池和失败重试)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
在實際項目中,經常會遇到調用外部(第三方)的接口,如果調用量較大的話,可能需要考慮連接池、失敗重試、SSL證書等問題,以提升性能和穩定性。
以下代碼是封裝的小組件,供大家參考。
<dependency><groupId>org.apache.httpcomponents
</groupId><artifactId>httpclient
</artifactId><version>4.5.6
</version></dependency>
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
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.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
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.util.EntityUtils;
import org.springframework.util.CollectionUtils;import javax.exceptions.ServiceException;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class HttpUtil {private static final String DEFAULT_CHARSET
= "UTF-8";private static CloseableHttpClient httpClient
;public static String get(final String url
) throws Exception {return getExecute(url
, null, getHttpClient(null), null);}public static String get(final String url
, final Map<String, Object> paramMap
) throws Exception {return getExecute(url
, paramMap
, getHttpClient(null), null);}public static String get(final String url
, final Map<String, Object> paramMap
, final Map<String, String> headers
) throws Exception {return getExecute(url
, paramMap
, getHttpClient(null), headers
);}public static String post(final String url
, final Map<String, Object> paramMap
) throws Exception {return postExecute(url
, paramMap
, new HashMap<>(), getHttpClient(null));}public static String postRetry(final String url
, final Map<String, Object> paramMap
, final Map<String, String> headers
) throws Exception {return postExecute(url
, paramMap
, headers
, getHttpClient(null));}public static String postRetry(final String url
, final String paramStr
,final CertInfo certInfo
, final Map<String, String> headers
) throws Exception {return postExecute(url
, paramStr
, headers
, getHttpClient(certInfo
));}public static String postRetry(final String url
, final Map<String, Object> paramMap
,final CertInfo certInfo
, final Map<String, String> headers
) throws Exception {return postExecute(url
, paramMap
, headers
, getHttpClient(certInfo
));}private static synchronized CloseableHttpClient getHttpClient(final CertInfo certInfo
) {if (null == certInfo
&& null != httpClient
) {return httpClient
;} else {RequestConfig config
= RequestConfig.custom().setSocketTimeout(Constants.HTTP_READ_TIMEOUT_MS
).setConnectTimeout(Constants.HTTP_CONNECTION_TIMEOUT_MS
).build();HttpClientConnectionManager connManager
= getPoolConnManager(certInfo
);HttpRequestRetryHandler retryHandler
= RetryHandler.getHandler(Constants.DEFAULT_HTTP_RETRY_MAX_TIMES
, Constants.DEFAULT_HTTP_RETRY_INTERVAL_MILLIS
);httpClient
= HttpClients.custom().setConnectionManager(connManager
).setDefaultRequestConfig(config
).setRetryHandler(retryHandler
).build();Runtime.getRuntime().addShutdownHook(new Thread(() -> {try {httpClient
.close();} catch (IOException e
) {}}));return httpClient
;}}private static HttpClientConnectionManager getPoolConnManager(final CertInfo certInfo
) {Registry<ConnectionSocketFactory> registry
;if (null != certInfo
) {certInfo
.validate();try {char[] password
= certInfo
.getPassword().toCharArray();InputStream certStream
= certInfo
.getCertStream();KeyStore ks
= KeyStore.getInstance("PKCS12");ks
.load(certStream
, password
);KeyManagerFactory kmf
= KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());kmf
.init(ks
, password
);SSLContext sslContext
= SSLContext.getInstance("TLS");sslContext
.init(kmf
.getKeyManagers(), null, new SecureRandom());SSLConnectionSocketFactory sslConnectionSocketFactory
= new SSLConnectionSocketFactory(sslContext
,new String[]{"TLSv1"},null,new DefaultHostnameVerifier());registry
= RegistryBuilder.<ConnectionSocketFactory>create().register("https", sslConnectionSocketFactory
).register("http", PlainConnectionSocketFactory.getSocketFactory()).build();} catch (Exception e
) {throw new ServiceException(ErrorCodeEnums.InternalServerError, "創建 SSLContext 失敗,證書初始化失敗");}} else {registry
= RegistryBuilder.<ConnectionSocketFactory>create().register("http", PlainConnectionSocketFactory.getSocketFactory()).register("https", SSLConnectionSocketFactory.getSocketFactory()).build();}PoolingHttpClientConnectionManager connectionManager
= new PoolingHttpClientConnectionManager(registry
);connectionManager
.setMaxTotal(Constants.HTTP_POOL_MAX_TOTAL
);connectionManager
.setDefaultMaxPerRoute(Constants.HTTP_POOL_MAX_PER_ROUTE
);return connectionManager
;}private static String postExecute(String url
, String data
, Map<String, String> headers
,CloseableHttpClient closeableHttpClient
) throws IOException {HttpPost httpPost
= new HttpPost(url
);if (!CollectionUtils.isEmpty(headers
)) {headers
.forEach(httpPost
::addHeader);}StringEntity postEntity
= new StringEntity(data
, DEFAULT_CHARSET
);httpPost
.setEntity(postEntity
);HttpResponse httpResponse
= closeableHttpClient
.execute(httpPost
);int httpStatus
= httpResponse
.getStatusLine().getStatusCode();if (HttpStatus.SC_OK
!= httpStatus
) {System.err
.println(url
+ "接口調用失敗,返回狀態碼:" + httpStatus
);}HttpEntity httpEntity
= httpResponse
.getEntity();return EntityUtils.toString(httpEntity
, DEFAULT_CHARSET
);}private static String postExecute(String url
, Map<String, Object> params
, Map<String, String> headers
,CloseableHttpClient closeableHttpClient
) throws IOException {HttpPost httpPost
= new HttpPost(url
);if (headers
!= null && !headers
.isEmpty()) {headers
.forEach(httpPost
::addHeader);}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
, DEFAULT_CHARSET
));HttpResponse httpResponse
= closeableHttpClient
.execute(httpPost
);int httpStatus
= httpResponse
.getStatusLine().getStatusCode();if (HttpStatus.SC_OK
!= httpStatus
) {System.err
.println(url
+ "接口調用失敗,返回狀態碼:" + httpStatus
);}HttpEntity httpEntity
= httpResponse
.getEntity();return EntityUtils.toString(httpEntity
, DEFAULT_CHARSET
);}private static String getExecute(String url
, Map<String, Object> params
, CloseableHttpClient closeableHttpClient
, Map<String, String> headers
) throws IOException {CloseableHttpResponse response
;if (!CollectionUtils.isEmpty(params
)) {StringBuilder realUrl
= new StringBuilder().append(url
).append("?");for (String key
: params
.keySet()) {realUrl
.append(key
).append("=").append(params
.get(key
)).append("&");}url
= realUrl
.substring(0, realUrl
.length() - 1);}HttpGet httpGet
= new HttpGet(url
);if (!CollectionUtils.isEmpty(headers
)) {headers
.forEach(httpGet
::addHeader);}response
= closeableHttpClient
.execute(httpGet
);int httpStatus
= response
.getStatusLine().getStatusCode();if (HttpStatus.SC_OK
!= httpStatus
) {System.err
.println(url
+ "接口調用失敗,返回狀態碼:" + httpStatus
);}return EntityUtils.toString(response
.getEntity(), DEFAULT_CHARSET
);}
}
public class Constants {public static final int HTTP_CONNECTION_TIMEOUT_MS
= 3000;public static final int HTTP_READ_TIMEOUT_MS
= 25000;public static final int HTTP_POOL_MAX_TOTAL
= 32;public static final int HTTP_POOL_MAX_PER_ROUTE
= 16;public static final int DEFAULT_HTTP_RETRY_MAX_TIMES
= 2;public static final Integer DEFAULT_HTTP_RETRY_INTERVAL_MILLIS
= 1000;
}
public enum ErrorCodeEnums {InternalServerError(500, "服務器內部錯誤"),
}
import org.springframework.util.Assert;import java.io.InputStream;
public class CertInfo {private String password
;private InputStream certStream
;public String getPassword() {return password
;}public CertInfo setPassword(String password
) {this.password
= password
;return this;}public InputStream getCertStream() {return certStream
;}public CertInfo setCertStream(InputStream certStream
) {this.certStream
= certStream
;return this;}public void validate() {Assert.hasLength(password
, "password不能為空");Assert.notNull(certStream
, "certStream不能為空");}
}
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpRequest;
import org.apache.http.NoHttpResponseException;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.UnknownHostException;
public class RetryHandler {private static final Logger LOGGER
= LogManager.getLogger(RetryHandler.class);public static HttpRequestRetryHandler getHandler(final Integer maxRetryTimes
, final Integer intervalMillis
) {if (maxRetryTimes
== null || maxRetryTimes
<= 0) {throw new IllegalArgumentException("maxRetryTimes參數不合法:" + maxRetryTimes
);}return (exception
, executionCount
, context
) -> {if (executionCount
> maxRetryTimes
) {LOGGER
.error(" 已超過最大重試次數。 當前為第{}次重試,最大重試次數為{}", executionCount
, maxRetryTimes
, exception
);return false;}try {long interval
= (null != intervalMillis
) ? intervalMillis
: Constants.DEFAULT_HTTP_RETRY_INTERVAL_MILLIS
;long nextInterval
= interval
* executionCount
;Thread.sleep(nextInterval
);} catch (InterruptedException e
) {Thread.currentThread().interrupt();}if (exception
instanceof UnknownHostException) {LOGGER
.error("目標服務器不可達, Unknown host", exception
);return false;}if (exception
instanceof ConnectTimeoutException) {LOGGER
.error("連接被拒絕,ConnectTimeout ", exception
);return true;}if (exception
instanceof InterruptedIOException) {LOGGER
.error("連接超時, Timeout", exception
);return true;}if (exception
instanceof IOException) {LOGGER
.error("網絡異常, IOException", exception
);return true;}if (exception
instanceof SSLHandshakeException) {LOGGER
.error("SSL握手異常,SSLHandshakeException ", exception
);return false;}if (exception
instanceof SSLException) {LOGGER
.error("SSLException exception", exception
);return false;}if (exception
instanceof NoHttpResponseException) {return true;}if (exception
instanceof Exception) {return true;}HttpClientContext clientContext
= HttpClientContext.adapt(context
);HttpRequest request
= clientContext
.getRequest();if (!(request
instanceof HttpEntityEnclosingRequest)) {return true;}LOGGER
.error("HttpRequestRetryHandler, return false");return false;};}
}
總結
以上是生活随笔為你收集整理的http工具类(支持https,连接池和失败重试)的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。