HttpMethod自定義失敗重連

541732025發表於2014-12-04

我們做web開發時,需要經常使用httpclient來請求http服務,有時為了安全起見,服務提供方會提供多個http地址,這樣如果我們請求某個ip出現異常,可以重試其他的ip地址,來儘量保證系統的穩定,以下是自定義一個HttpMethod重試機制的簡要程式碼。

HostCluster類定義需要連線的協議、ips[]、重試次數、隨機獲取一個host等:

點選(此處)摺疊或開啟

  1. public class HostCluster {
  2.     protected static final String HTTP_PROTOCOL = \"http\";
  3.     protected static final String HTTPS_PROTOCOL = \"https\";

  4.     protected String protocol;
  5.     protected String[] ips; //contain ip and port

  6.     private int idx;
  7.     private int retry;

  8.     private Random random;

  9.     public HostCluster(String ipAndPort) {
  10.         this(HTTP_PROTOCOL, ipAndPort);
  11.     }

  12.     public HostCluster(String protocol, String ipAndPort) {
  13.         this(protocol, ipAndPort, 0);
  14.     }

  15.     public HostCluster(String protocol, String ipAndPort, int retry) {
  16.         if (StringUtils.isEmpty(ipAndPort)) {
  17.             throw new IllegalArgumentException(\"invalid constructor params.\");
  18.         }
  19.         if (retry < 0) {
  20.             throw new IllegalArgumentException(\"invalid retry.\");
  21.         }
  22.         if (!HTTP_PROTOCOL.equals(protocol) && !HTTPS_PROTOCOL.equals(protocol)) {
  23.             throw new IllegalArgumentException(\"invalid protocol.\");
  24.         }
  25.         
  26.         //split the string
  27.         String[] splitStr = StringUtils.split(ipAndPort, \",\");

  28.         this.protocol = protocol;
  29.         this.ips = splitStr;
  30.         this.retry = retry;

  31.         this.idx = this.ips.length;
  32.         this.random = new Random();
  33.     }

  34.     public String randomHost() {
  35.         int index = this.random.nextInt(idx);
  36.         log.info(\"randomIp=\" + ips[index]);
  37.         return this.protocol + \"://\" + ips[index];
  38.     }

  39.     public boolean isHttps() {
  40.         return HTTPS_PROTOCOL.equals(protocol);
  41.     }

  42.     public String getProtocol() {
  43.         return protocol;
  44.     }

  45.     public int getRetry() {
  46.         return retry;
  47.     }
  48. }


ClusterRetryHttpMethod從HostCluster獲取的randomHost,然後new URI()設定相應的base(GetMethod or PostMethod)

點選(此處)摺疊或開啟

  1. public abstract class ClusterRetryHttpMethod<T extends HttpMethod> {
  2.     protected HostCluster cluster;
  3.     protected String urlSuffix;
  4.     protected T base;
  5.     private Integer retry;

  6.     public ClusterRetryHttpMethod(HostCluster cluster, String urlSuffix) {
  7.         this(cluster, urlSuffix, null);
  8.     }

  9.     public ClusterRetryHttpMethod(HostCluster cluster, String urlSuffix, Integer retry) {
  10.         if (cluster == null || StringUtils.isBlank(urlSuffix)) {
  11.             throw new IllegalArgumentException(\"invalid params.\");
  12.         }
  13.         if (retry != null) {
  14.             if (retry < 0) {
  15.                 throw new IllegalArgumentException(\"invalid retry.\");
  16.             } else {
  17.                 this.retry = retry;
  18.             }
  19.         }
  20.         this.cluster = cluster;
  21.         this.urlSuffix = urlSuffix;

  22.         this.base = initBase();
  23.     }

  24.     public boolean isHttps() {
  25.         return this.cluster.isHttps();
  26.     }

  27.     public void setQueryString(String queryString) {
  28.         this.base.setQueryString(queryString);
  29.     }

  30.     public void setQueryString(NameValuePair[] params) {
  31.         this.base.setQueryString(params);
  32.     }

  33.     public int getRetry() {
  34.         return retry == null ? this.cluster.getRetry() : retry;
  35.     }

  36.     protected abstract T initBase();//子類各自實現,GetMethod or PostMethod

  37.     public T randomMethod() throws Exception {
  38.         String url = this.randomUrl();
  39.         if (StringUtils.isBlank(url)) {
  40.             url = \"/\";
  41.         }

  42.         this.base.setURI(new URI(url, true));
  43.         return base;
  44.     }

  45.     protected String randomUrl() {
  46.         return cluster.randomHost() + urlSuffix;
  47.     }
  48. }


最後就是怎麼呼叫了,這裡使用的是HttpClientPool來呼叫http連線,關於HttpClientPool詳見我的另一篇文章:http://blog.itpub.net/28912557/viewspace-1223241/

點選(此處)摺疊或開啟

  1. public class HttpClientPool extends GenericObjectPool<HttpClient> {

  2.     private int httpsPort;

  3.     public HttpClientPool(PoolableObjectFactory<HttpClient> factory) {
  4.         super(factory);
  5.     }

  6.     public <T> T doPost(ClusterRetryHttpMethod method, HttpClientDataCallback<T> callback) {
  7.         HttpClient toUse = null;
  8.         HttpMethod m = null;
  9.         int index = 0;
  10.         if (method == null) {
  11.             return null;
  12.         }
  13.         if (method.isHttps()) {
  14.             Protocol myhttps = new Protocol(\"https\", new SSLProtocolSocketFactoryImpl(), httpsPort);
  15.             Protocol.registerProtocol(\"https\", myhttps);
  16.         }
  17.         try {
  18.             toUse = borrowObject();
  19.             while (index <= method.getRetry()) {
  20.                 try {
  21.                     m = method.randomMethod();
  22.                     toUse.executeMethod(m);
  23.                     T rel = callback.handleResponse(m.getResponseBodyAsString());
  24.                     return rel;
  25.                 } catch (Exception e) {
  26.                     logger.error(\"failed to execute http request.\", e);
  27.                     index++;
  28.                 } finally {
  29.                     try {
  30.                         m.releaseConnection();
  31.                     } catch (Exception e) {
  32.                         // in case fail, ignore and return object
  33.                     }
  34.                 }
  35.             }
  36.         } catch (Exception e) {
  37.             return null;
  38.         } finally {
  39.             if (toUse != null) {
  40.                 try {
  41.                     returnObject(toUse);
  42.                 } catch (Exception e) {
  43.                 }
  44.             }
  45.             if (method.isHttps()) {
  46.                 try {
  47.                     Protocol.unregisterProtocol(\"https\");
  48.                 } catch (Exception e) {
  49.                 }
  50.             }
  51.         }
  52.         // all retry failed
  53.         return null;

  54.     }


  55.     public int getHttpsPort() {
  56.         return httpsPort;
  57.     }

  58.     public void setHttpsPort(int httpsPort) {
  59.         this.httpsPort = httpsPort;
  60.     }
  61. }


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/28912557/viewspace-1356116/,如需轉載,請註明出處,否則將追究法律責任。

相關文章