目前的項目接口都是http,因此在java項目中使用apache httpclient進行數(shù)據(jù)傳輸、訪問。
目前程序中涉及到需要callback操作,product需要被動的接收consume的處理狀態(tài),為了最大程度的能夠callback成功因此consume在http調(diào)用出現(xiàn)問題(如:服務(wù)不可用、異常、超時)情況下需要進行重試(retry request),在這里我列舉出我找到的retry方案,有些成功有些不成功。
我是用的httpclient版本是4.5.2。關(guān)于retry功能我在網(wǎng)上也找了不少的資料,但是都不是我對應(yīng)的httpclient版本,大多是過時的。
在httpclient版本4.5.2提供了以下幾種retry方案:
StandardHttpRequestRetryHandler
這種方案沒有測試通過,StandardHttpRequestRetryHandler實際上是DefaultHttpRequestRetryHandler的子類,這是官方提供的一個標(biāo)準(zhǔn)的retry方案,為了保證冪等性約定resetful接口必須是GET, HEAD, PUT, DELETE, OPTIONS, and TRACE中的一種,如下,是我定義的httpclient pool,
public static CloseableHttpClient getHttpClient() {
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
cm.setMaxTotal(MAX_TOTAL);
cm.setDefaultMaxPerRoute(MAX_PERROUTE);
CloseableHttpClient httpClient = HttpClients
.custom()
.setRetryHandler(new StandardHttpRequestRetryHandler())
.setConnectionManager(cm)
.build();
return httpClient;
}1234567891011
如下是我的測試代碼,
@Test
public void test6(){
HttpPost httpPost=new HttpPost("http://127.0.0.1:8080/testjobs1");
try {
rsp=httpClient.execute(httpPost);
log.info(">> {}",rsp.getStatusLine().getStatusCode());
} catch (Exception e) {
log.error(e.getMessage(),e);
}finally{
HttpUtil.close(rsp);
}
}123456789101112
運行測試,當(dāng)url錯誤、后臺報錯、后臺超時等情況的時候不能進行retry,因此放棄了此方案。
DefaultHttpRequestRetryHandler
這種方案沒有測試通過,和上面的StandardHttpRequestRetryHandler類似,它提供了一種默認的retry方案,并沒有像StandardHttpRequestRetryHandler一樣約定接口必須是冥等的,如下,是我定義的httpclient pool,
public static CloseableHttpClient getHttpClient() {
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
cm.setMaxTotal(MAX_TOTAL);
cm.setDefaultMaxPerRoute(MAX_PERROUTE);
CloseableHttpClient httpClient = HttpClients
.custom()
.setRetryHandler(new DefaultHttpRequestRetryHandler())
.setConnectionManager(cm)
.build();
return httpClient;
}1234567891011
如下是我的測試代碼,
@Test
public void test6(){
HttpPost httpPost=new HttpPost("http://127.0.0.1:8080/testjobs1");
try {
rsp=httpClient.execute(httpPost);
log.info(">> {}",rsp.getStatusLine().getStatusCode());
} catch (Exception e) {
log.error(e.getMessage(),e);
}finally{
HttpUtil.close(rsp);
}
}123456789101112
依然沒有達到希望的效果。
HttpRequestRetryHandler
可以實現(xiàn),但是不夠完美。在官方文檔有這么一段,如下,
HttpRequestRetryHandler myRetryHandler = new HttpRequestRetryHandler() {
public boolean retryRequest(
IOException exception,
int executionCount,
HttpContext context) {
if (executionCount >= 5) {
// Do not retry if over max retry count
return false;
}
if (exception instanceof InterruptedIOException) {
// Timeout
return false;
}
if (exception instanceof UnknownHostException) {
// Unknown host
return false;
}
if (exception instanceof ConnectTimeoutException) {
// Connection refused
return false;
}
if (exception instanceof SSLException) {
// SSL handshake exception
return false;
}
HttpClientContext clientContext = HttpClientContext.adapt(context);
HttpRequest request = clientContext.getRequest();
boolean idempotent = !(request instanceof HttpEntityEnclosingRequest);
if (idempotent) {
// Retry if the request is considered idempotent
return true;
}
return false;
}
};
CloseableHttpClient httpclient = HttpClients.custom()
.setRetryHandler(myRetryHandler)
.build();12345678910111213141516171819202122232425262728293031323334353637383940
自定義retry實現(xiàn),這比較靈活,可以根據(jù)異常自定義retry機制以及重試次數(shù),并且可以拿到返回信息,如下,是我定義的httpclient pool,
public static CloseableHttpClient getHttpClient() {
HttpRequestRetryHandler retryHandler = new HttpRequestRetryHandler() {
public boolean retryRequest(
IOException exception,
int executionCount,
HttpContext context) {
if (executionCount >= 5) {
// Do not retry if over max retry count
return false;
}
if (exception instanceof InterruptedIOException) {
// Timeout
return false;
}
if (exception instanceof UnknownHostException) {
// Unknown host
return false;
}
if (exception instanceof ConnectTimeoutException) {
// Connection refused
return false;
}
if (exception instanceof SSLException) {
// SSL handshake exception
return false;
}
HttpClientContext clientContext = HttpClientContext.adapt(context);
HttpRequest request = clientContext.getRequest();
boolean idempotent = !(request instanceof HttpEntityEnclosingRequest);
if (idempotent) {
// Retry if the request is considered idempotent
return true;
}
return false;
}
};
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
cm.setMaxTotal(MAX_TOTAL);
cm.setDefaultMaxPerRoute(MAX_PERROUTE);
CloseableHttpClient httpClient = HttpClients
.custom()
.setRetryHandler(retryHandler)
.setConnectionManager(cm)
.build();
return httpClient;
}12345678910111213141516171819202122232425262728293031323334353637383940414243444546
如下是我的測試代碼,
@Test
public void test6(){
HttpPost httpPost=new HttpPost("http://127.0.0.1:8080/testjobs1");
try {
rsp=httpClient.execute(httpPost);
log.info(">> {}",rsp.getStatusLine().getStatusCode());
} catch (Exception e) {
log.error(e.getMessage(),e);
}finally{
HttpUtil.close(rsp);
}
}123456789101112
這種方案,可以實現(xiàn)retry,并且可以根據(jù)我的需求進行retry,如:retry count,但是就不能控制retry時間的間隔,也只好放棄了,繼續(xù)尋找找到了下面這個ServiceUnavailableRetryStrategy。
ServiceUnavailableRetryStrategy
可以實現(xiàn),滿足需求,這具有HttpRequestRetryHandler的所有有點,并且可以自定義retry時間的間隔,如下,是我定義的httpclient pool,
public static CloseableHttpClient getHttpClient() {
ServiceUnavailableRetryStrategy serviceUnavailableRetryStrategy = new ServiceUnavailableRetryStrategy() {
/**
* retry邏輯
*/
@Override
public boolean retryRequest(HttpResponse response, int executionCount, HttpContext context) {
if (executionCount <= 3)
return true;
else
return false;
}
/**
* retry間隔時間
*/
@Override
public long getRetryInterval() {
return 2000;
}
};
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
cm.setMaxTotal(MAX_TOTAL);
cm.setDefaultMaxPerRoute(MAX_PERROUTE);
CloseableHttpClient httpClient = HttpClients.custom().setRetryHandler(new DefaultHttpRequestRetryHandler())
.setConnectionManager(cm).build();
return httpClient;
}12345678910111213141516171819202122232425262728
如下是我的測試代碼,
@Test
public void test6(){
HttpPost httpPost=new HttpPost("http://127.0.0.1:8080/testjobs1");
try {
rsp=httpClient.execute(httpPost);
log.info(">> {}",rsp.getStatusLine().getStatusCode());
} catch (Exception e) {
log.error(e.getMessage(),e);
}finally{
HttpUtil.close(rsp);
}
}