使用Apache HttpClient 4.x進行異常重試


在進行http請求時,難免會遇到請求失敗的情況,失敗后需要重新請求,嘗試再次獲取數據。

Apache的HttpClient提供了異常重試機制,在該機制中,我們可以很靈活的定義在哪些異常情況下進行重試。

重試前提

被請求的方法必須是冪等的:就是多次請求服務端結果應該是准確且一致的。 

適合的方法:比如根據ID,修改人員姓名,無論請求多次結果都是一樣,這就是冪等。 
不適合的方法:比如減少賬號50元,多次請求將多次扣減。

實現方式

想要實現異常重試,需要實現它提供的一個接口   HttpRequestRetryHandler ,並實現  retryRequest 方法。然后set到HttpClient中。該httpClient就具備了重試機制。
 
HttpClient自身提供了  StandardHttpRequestRetryHandler 和  DefaultHttpRequestRetryHandler 兩個實現類。  DefaultHttpRequestRetryHandler 繼承自  DefaultHttpRequestRetryHandler ,  StandardHttpRequestRetryHandler 默認幾個方法為冪等,如PUT、GET、HEAD等除POST外的方法,如果自定義可以參考它的實現方式。
 
代碼如下:
  1 import java.io.IOException;
  2 import java.io.InterruptedIOException;
  3 import java.net.ConnectException;
  4 import java.net.UnknownHostException;
  5 
  6 import javax.net.ssl.SSLException;
  7 
  8 import org.apache.commons.lang3.ObjectUtils;
  9 import org.apache.commons.lang3.StringUtils;
 10 import org.apache.http.Consts;
 11 import org.apache.http.HttpEntityEnclosingRequest;
 12 import org.apache.http.HttpRequest;
 13 import org.apache.http.HttpStatus;
 14 import org.apache.http.ParseException;
 15 import org.apache.http.client.HttpRequestRetryHandler;
 16 import org.apache.http.client.config.RequestConfig;
 17 import org.apache.http.client.methods.CloseableHttpResponse;
 18 import org.apache.http.client.methods.HttpPost;
 19 import org.apache.http.client.protocol.HttpClientContext;
 20 import org.apache.http.entity.ContentType;
 21 import org.apache.http.entity.StringEntity;
 22 import org.apache.http.impl.client.CloseableHttpClient;
 23 import org.apache.http.impl.client.HttpClients;
 24 import org.apache.http.protocol.HttpContext;
 25 import org.apache.http.util.EntityUtils;
 26 
 27 /**
 28  * @author 29  *
 30  * @date 2017年5月18日 上午9:17:30
 31  *
 32  * @Description
 33  */
 34 public class HttpPostUtils {
 35     /**
 36      * 
 37      * @param uri
 38      *            the request address
 39      * @param json
 40      *            the request data that must be a JSON string
 41      * @param retryCount
 42      *            the number of times this method has been unsuccessfully
 43      *            executed
 44      * @param connectTimeout
 45      *            the timeout in milliseconds until a connection is established
 46      * @param connectionRequestTimeout
 47      *            the timeout in milliseconds used when requesting a connection
 48      *            from the connection manager
 49      * @param socketTimeout
 50      *            the socket timeout in milliseconds, which is the timeout for
 51      *            waiting for data or, put differently, a maximum period
 52      *            inactivity between two consecutive data packets
 53      * @return null when method parameter is null, "", " "
 54      * @throws IOException
 55      *             if HTTP connection can not opened or closed successfully
 56      * @throws ParseException
 57      *             if response data can not be parsed successfully
 58      */
 59     public String retryPostJson(String uri, String json, int retryCount, int connectTimeout,
 60             int connectionRequestTimeout, int socketTimeout) throws IOException, ParseException {
 61         if (StringUtils.isAnyBlank(uri, json)) {
 62             return null;
 63         }
 64 
 65         HttpRequestRetryHandler httpRequestRetryHandler = new HttpRequestRetryHandler() {
 66 
 67             @Override
 68             public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
 69                 if (executionCount > retryCount) {
 70                     // Do not retry if over max retry count
 71                     return false;
 72                 }
 73                 if (exception instanceof InterruptedIOException) {
 74                     // An input or output transfer has been terminated
 75                     return false;
 76                 }
 77                 if (exception instanceof UnknownHostException) {
 78                     // Unknown host 修改代碼讓不識別主機時重試,實際業務當不識別的時候不應該重試,再次為了演示重試過程,執行會顯示retryCount次下面的輸出
 79                     System.out.println("不識別主機重試"); return true;
 80                 }
 81                 if (exception instanceof ConnectException) {
 82                     // Connection refused
 83                     return false;
 84                 }
 85                 if (exception instanceof SSLException) {
 86                     // SSL handshake exception
 87                     return false;
 88                 }
 89                 HttpClientContext clientContext = HttpClientContext.adapt(context);
 90                 HttpRequest request = clientContext.getRequest();
 91                 boolean idempotent = !(request instanceof HttpEntityEnclosingRequest);
 92                 if (idempotent) {
 93                     // Retry if the request is considered idempotent
 94                     return true;
 95                 }
 96                 return false;
 97             }
 98         };
 99 
100         CloseableHttpClient client = HttpClients.custom().setRetryHandler(httpRequestRetryHandler).build();
101         HttpPost post = new HttpPost(uri);
102         // Create request data
103         StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON);
104         // Set request body
105         post.setEntity(entity);
106 
107         RequestConfig config = RequestConfig.custom().setConnectTimeout(connectTimeout)
108                 .setConnectionRequestTimeout(connectionRequestTimeout).setSocketTimeout(socketTimeout).build();
109         post.setConfig(config);
110         // Response content
111         String responseContent = null;
112         CloseableHttpResponse response = null;
113         try {
114             response = client.execute(post, HttpClientContext.create());
115             if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
116                 responseContent = EntityUtils.toString(response.getEntity(), Consts.UTF_8.name());
117             }
118         } finally {
119             if (ObjectUtils.anyNotNull(response)) {
120                 response.close();
121             }
122             if (ObjectUtils.anyNotNull(client)) {
123                 client.close();
124             }
125         }
126         return responseContent;
127     }

在實現的 retryRequest 方法中,遇到不識別主機異常,返回 true ,請求將重試。最多重試請求retryCount次。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM