HttpClient封裝
阿新 • • 發佈:2019-01-05
1,ServiceUnavailableRetryStrategy 新增自動重試機制
import java.io.IOException; import java.nio.charset.Charset; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpEntity; import org.apache.http.HttpHost; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.ResponseHandler; import org.apache.http.client.ServiceUnavailableRetryStrategy; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.client.utils.URLEncodedUtils; import org.apache.http.config.ConnectionConfig; 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.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.impl.client.CloseableHttpClient; import org.apache.http.impl.client.DefaultServiceUnavailableRetryStrategy; 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.message.BasicNameValuePair; import org.apache.http.ssl.SSLContexts; import org.apache.http.ssl.TrustStrategy; import org.apache.http.util.EntityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Apache Httpclient 工具包裝類 <br> * required httpclient-4.3 * * @author jiucai */ public class HttpClientUtil { public static final String CHARSET_UTF8 = "UTF-8"; protected static Logger logger = LoggerFactory.getLogger(HttpClientUtil.class); // 使用ResponseHandler介面處理響應,HttpClient使用ResponseHandler會自動管理連線的釋放,解決了對連線的釋放管理 private static ResponseHandler<String> responseHandler = new ResponseHandler<String>() { // 自定義響應處理 @Override public String handleResponse(HttpResponse response) throws ClientProtocolException, IOException { int code = response.getStatusLine().getStatusCode(); // 如果不是200,則返回狀態碼 // if (HttpStatus.SC_OK <= code && code < 500) { if (code < 200 || code >= 500) { return String.valueOf(code); } HttpEntity entity = response.getEntity(); if (entity != null) { Charset charset = ContentType.getOrDefault(entity).getCharset(); if (null != charset) { return new String(EntityUtils.toByteArray(entity), charset); } else { return new String(EntityUtils.toByteArray(entity)); } } else { return null; } } }; /** * 釋放HttpClient連線 * * @param hrb * 請求物件 * @param httpclient * 物件 */ public static void abortConnection(final HttpUriRequest hrb, final CloseableHttpClient httpclient) { if (hrb != null) { hrb.abort(); } if (httpclient != null) { // httpclient.getConnectionManager().shutdown(); try { // logger.debug("closing httpclient ..."); httpclient.close(); } catch (IOException e) { logger.error("failed to close httpclient", e); } } } /** * Get方式提交,URL中包含查詢引數 * * @param url * 提交地址 * @return 響應訊息 */ public static String get(String url) { return get(url, null, CHARSET_UTF8); } /** * Get方式提交,URL中不包含查詢引數 * * @param url * 提交地址 * @param params * 查詢引數集, 鍵/值對 * @return 響應訊息 */ public static String get(String url, Map<String, String> params) { return get(url, params, CHARSET_UTF8); } /** * Get方式提交,URL中不包含查詢引數 * * @param url * 提交地址 * @param params * 查詢引數集, 鍵/值對 * @param charset * 引數提交編碼集 * @return 響應訊息 */ public static String get(String url, Map<String, String> params, String charset) { return get(url, params, charset, getHttpClient(charset)); } public static String get(String url, Map<String, String> params, String charset, CloseableHttpClient client) { if (url == null || StringUtils.isEmpty(url)) { return null; } List<NameValuePair> qparams = getParamsList(params); if (qparams != null && qparams.size() > 0) { charset = (charset == null ? CHARSET_UTF8 : charset); String formatParams = URLEncodedUtils.format(qparams, charset); url = (url.indexOf("?")) < 0 ? (url + "?" + formatParams) : (url.substring(0, url.indexOf("?") + 1) + formatParams); } HttpGet hg = new HttpGet(url); // 傳送請求,得到響應 String responseStr = null; try { responseStr = client.execute(hg, responseHandler); } catch (ClientProtocolException e) { throw new RuntimeException("客戶端連線協議錯誤", e); } catch (IOException e) { throw new RuntimeException("IO操作異常", e); } finally { abortConnection(hg, client); } return responseStr; } public static HttpClientConnectionManager getConnectionManager() { // ConnectionSocketFactory plainsf = null; LayeredConnectionSocketFactory sslsf = null; RegistryBuilder<ConnectionSocketFactory> registryBuilder = RegistryBuilder.create(); PlainConnectionSocketFactory plainsf = PlainConnectionSocketFactory.getSocketFactory(); registryBuilder.register("http", plainsf); try { // Trust own CA and all self-signed certs SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(new TrustStrategy() { @Override public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { return true; } }).build(); HostnameVerifier allowAllHostnameVerifier = NoopHostnameVerifier.INSTANCE; sslsf = new SSLConnectionSocketFactory(sslcontext, allowAllHostnameVerifier); registryBuilder.register("https", sslsf); } catch (Throwable e) { logger.error("https ssl init failed", e); } Registry<ConnectionSocketFactory> r = registryBuilder.build(); PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(r); connManager.setMaxTotal(100);// 連線池最大併發連線數 connManager.setDefaultMaxPerRoute(100);// 單路由最大併發數 return connManager; } /** * 獲取HttpClient例項 * * @param charset * 引數編碼集, 可空 * @return HttpClient 物件 */ public static CloseableHttpClient getHttpClient(final String charset) { return getHttpClientBuilder(charset, null, 0).build(); } public static HttpClientBuilder getHttpClientBuilder(final String charset, String proxyIp, int proxyPort) { HttpClientBuilder builder = HttpClients.custom(); Charset chartset = charset == null ? Charset.forName(CHARSET_UTF8) : Charset.forName(charset); ConnectionConfig.Builder connBuilder = ConnectionConfig.custom().setCharset(chartset); RequestConfig.Builder reqBuilder = RequestConfig.custom(); reqBuilder.setExpectContinueEnabled(false); reqBuilder.setSocketTimeout(10 * 60 * 1000); reqBuilder.setConnectTimeout(10 * 60 * 1000); reqBuilder.setMaxRedirects(10); if (StringUtils.isNotBlank(proxyIp) && proxyPort > 0) { logger.info("using proxy {}:{} to request ", proxyIp, String.valueOf(proxyPort)); HttpHost proxy = new HttpHost(proxyIp, proxyPort); reqBuilder.setProxy(proxy); } ServiceUnavailableRetryStrategy serviceUnavailableRetryStrategy = new DefaultServiceUnavailableRetryStrategy( 3, 3000); builder.setServiceUnavailableRetryStrategy(serviceUnavailableRetryStrategy); // 模擬瀏覽器,解決一些伺服器程式只允許瀏覽器訪問的問題 builder.setUserAgent( "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:50.0) Gecko/20100101 Firefox/50.0"); builder.setDefaultRequestConfig(reqBuilder.build()); builder.setDefaultConnectionConfig(connBuilder.build()); builder.setConnectionManager(getConnectionManager()); // HostnameVerifier allowAllHostnameVerifier = // NoopHostnameVerifier.INSTANCE; // builder.setSSLHostnameVerifier(allowAllHostnameVerifier); return builder; } /** * 將傳入的鍵/值對引數轉換為NameValuePair引數集 * * @param paramsMap * 引數集, 鍵/值對 * @return NameValuePair引數集 */ public static List<NameValuePair> getParamsList(Map<String, String> paramsMap) { if (paramsMap == null || paramsMap.size() == 0) { return null; } List<NameValuePair> params = new ArrayList<NameValuePair>(); for (Map.Entry<String, String> map : paramsMap.entrySet()) { params.add(new BasicNameValuePair(map.getKey(), map.getValue())); } return params; } /** * Post方式提交,URL中不包含提交引數 * * @param url * 提交地址 * @param params * 提交引數集, 鍵/值對 * @return 響應訊息 */ public static String post(String url, Map<String, String> params) { return post(url, params, HttpClientUtil.CHARSET_UTF8); } /** * Post方式提交,URL中不包含提交引數 * * @param url * 提交地址 * @param params * 提交引數集, 鍵/值對 * @param charset * 引數提交編碼集 * @return 響應訊息 */ public static String post(String url, Map<String, String> params, String charset) { return post(url, params, charset, getHttpClient(charset)); } public static String post(String url, Map<String, String> params, String charset, boolean isSecure) { return post(url, params, charset); } public static String post(String url, Map<String, String> params, String charset, CloseableHttpClient client) { if (url == null || StringUtils.isEmpty(url)) { return null; } UrlEncodedFormEntity formEntity = null; try { if (charset == null || StringUtils.isEmpty(charset)) { if (null != params && params.size() > 0) { formEntity = new UrlEncodedFormEntity(getParamsList(params)); } } else { if (null != params && params.size() > 0) { formEntity = new UrlEncodedFormEntity(getParamsList(params), charset); } } } catch (Exception e) { throw new RuntimeException("不支援的編碼集", e); } HttpPost hp = new HttpPost(url); if (null != formEntity) { hp.setEntity(formEntity); } // 傳送請求,得到響應 String responseStr = null; try { responseStr = client.execute(hp, responseHandler); } catch (ClientProtocolException e) { throw new RuntimeException("客戶端連線協議錯誤", e); } catch (IOException e) { throw new RuntimeException("IO操作異常", e); } finally { abortConnection(hp, client); } return responseStr; } }
guaua-retry自動義重試機制
private Map<String, String> retryRequest(String httpsUrl, Map<String, String> httpsParam) { Map<String, String> reponseMap = Maps.newHashMap(); try { Retryer<Map<String, String>> retryer = RetryerBuilder.<Map<String, String>>newBuilder().retryIfResult( new Predicate<Map<String, String>>() { @Override public boolean apply(@Nullable Map<String, String> soMap) { String returnCode = (String) soMap.get("return_code"); // 富數token異常,讓快取中的token失效,重新獲取 if (Constant.FUSHU_TOKEN_EMPTY.equals(returnCode) || Constant.FUSHU_TOKEN_EXPIRE.equals(returnCode) || Constant.FUSHU_TOKEN_INVALID.equals(returnCode)) { FsDirectToken.getInstance().getTokenCache().put(Constant.FIELD_FS_TOKEN, ""); FsDirectToken.getInstance().getTokenCache().put(Constant.FIELD_TOKEN_EXPIRY, ""); return true; } // 返回狀態碼不為0重試 if (!"0".equals(returnCode)) { return true; } return false; } } ).retryIfExceptionOfType(IOException.class) .retryIfRuntimeException() .withWaitStrategy(WaitStrategies.fixedWait(10, TimeUnit.SECONDS)) .withStopStrategy(StopStrategies.stopAfterAttempt(5)).build(); reponseMap = retryer.call(new Callable<Map<String, String>>() { @Override public Map<String, String> call() throws Exception { return retryRequestCall(httpsUrl, httpsParam); } }); } catch (Exception e) { logger.error("重試請求異常:{}", e.getMessage()); try { reponseMap = retryRequestCall(httpsUrl, httpsParam); } catch (Exception ex) { logger.error("重試補發請求異常:{}", ex); } } return reponseMap; } /** * 重發call請求 * * @param httpsUrl * @param httpsParam * @return */ private Map<String, String> retryRequestCall(String httpsUrl, Map<String, String> httpsParam) { Map<String, String> innerMap = Maps.newHashMap(); String token = getFsToken(); httpsParam.put("token", token); logger.info("富數三方請求引數:{}", JSON.toJSONString(httpsParam)); String message = HttpClientUtils.post(httpsUrl, httpsParam); logger.info("富數三方返回結果:{}", JSON.toJSONString(message)); JSONObject jsonObject = JSONObject.parseObject(message); innerMap.put("return_code", jsonObject.getString("return_code")); innerMap.put("data", message); return innerMap; }