问题描述
在多次失败的异步请求调用之后,如何耗尽重试次数? 我正在使用AsyncHttpClient将请求发送到我们的服务器。如果请求超时,连接异常等,我希望客户端重试N次并抛出自定义异常。调用方法应该收到此异常,或者可以不处理它。
// calls post
public void call(String data) throws CustomException {
asyncHttpClient.post(data,10);
}
// posts data to http endpoint
public void post(String data,int retries) throw CustomException {
// if retries are exhausted,throw CustomException to call()
if (retry <= 0) {
throw new CustomException("exc");
}
BoundRequest request = httpClient.preparePost("http_endpoint");
ListenableFuture<Response> responseFuture = httpClient.post(request);
responseFuture.addListener(() -> {
Response response = null;
int status = 0;
try {
response = responseFuture.get();
status = response.getStatusCode();
// HTTP_ACCEPTED = {200,201,202}
if (ArrayUtils.contains(HTTP_ACCEPTED,status)) {
// ok
} else {
sleep(10);
post(data,retry - 1);
}
} catch (InterruptedException e) {
sleep(10);
post(data,retry - 1);
} catch (ExecutionException e) {
// ConnectionException
// RequestTimeoutException
sleep(10); // 10 seconds
post(data,retry - 1);
} catch (Exception e) {
sleep(10); // 10 seconds
post(data,retry - 1 );
} finally {
responseFuture.done();
}
},Runnable::run);
}
此方法存在一些问题:
- 使用递归调用重试。
-
CustomException
似乎从未抛出过,并且在重试== 0之后,控件返回到finally
块。
...
} catch (ExecutionException e) {
// ConnectionException
// RequestTimeoutException
sleep(10); // 10 seconds
try {
post(data,retry - 1);
} catch (CustomException e) {
}
}
...
解决方法
好的,因此尝试重现您要使用代码实现的目标,但立即意识到CustomException
仅在类型为RuntimeException
时才有效。原因是您想在运行时和另一个线程中引发异常。
以下代码显示了Exception的简单实现。请记住,并非所有RuntimeException都会停止程序。此thread中对此进行了说明。因此,如果要终止程序,则必须手动停止它。
public class CustomException extends RuntimeException {
public CustomException(String msg) {
super(msg);
// print your exception to the console
// optional: exit the program
System.exit(0);
}
}
我更改了其余的实现,因此您不再需要进行递归调用。我删除了回调方法,而是调用了get()
方法,该方法等待请求完成。但是由于我是在单独的线程中执行所有这些操作,因此它应该在后台而不是主线程中运行。
public class Main {
private final AsyncHttpClient httpClient;
private final int[] HTTP_ACCEPTED = new int[]{200,201,202};
private final static String ENDPOINT = "https://postman-echo.com/post";
public static void main(String[] args) {
String data = "{message: 'Hello World'}";
Main m = new Main();
m.post(data,10);
}
public Main() {
httpClient = asyncHttpClient();
}
public void post(final String data,final int retry) {
Runnable runnable = () -> {
int retries = retry;
for (int i = 0; i < retry; i++) {
Request request = httpClient.preparePost(ENDPOINT)
.addHeader("Content-Type","application/json")
.setBody(data)
.build();
ListenableFuture<Response> responseFuture = httpClient.executeRequest(request);
try {
Response response = responseFuture.get();
int status = response.getStatusCode();
if (ArrayUtils.contains(HTTP_ACCEPTED,status)) {
System.out.println("Successful! Breaking Loop");
break;
} else {
Thread.sleep(10);
}
} catch (InterruptedException | ExecutionException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE,null,ex);
}
retries--;
}
System.out.println("Remaining retries: " + retries);
if (retries <= 0) {
throw new CustomException("exc");
}
};
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(runnable);
}
}
替代
您可以使用相同的Runnable
进行异步调用,而不必等待future.get()
。每个侦听器将方便地在同一线程中调用,这使它在您的用例中更加有效。
public void post2(final String data,final int retry) {
Request request = httpClient.preparePost(ENDPOINT)
.addHeader("Content-Type","application/json")
.setBody(data)
.build();
ListenableFuture<Response> future = httpClient.executeRequest(request);
MyRunnable runnable = new MyRunnable(retry,future,request);
future.addListener(runnable,null);
}
public class MyRunnable implements Runnable {
private int retries;
private ListenableFuture<Response> responseFuture;
private final Request request;
public MyRunnable(int retries,ListenableFuture<Response> future,Request request) {
this.retries = retries;
this.responseFuture = future;
this.request = request;
}
@Override
public void run() {
System.out.println("Remaining retries: " + this.retries);
System.out.println("Thread ID: " + Thread.currentThread().getId());
try {
Response response = responseFuture.get();
int status = response.getStatusCode();
if (ArrayUtils.contains(HTTP_ACCEPTED,status)) {
System.out.println("Success!");
//do something here
} else if (this.retries > 0) {
Thread.sleep(10);
this.execute();
} else {
throw new CustomException("Exception!");
}
} catch (InterruptedException | ExecutionException e) {
this.execute();
}
}
private void execute() {
this.retries -= 1;
this.responseFuture = httpClient.executeRequest(this.request);
this.responseFuture.addListener(this,null);
}
}
,
AsyncHttpClient中有一个预定义的函数来处理MaxRetries,
下面的代码显示了一个简单的实现
AsyncHttpClientConfig cf = new DefaultAsyncHttpClientConfig.Builder().setMaxRequestRetry(5).setKeepAlive(true).build()
final AsyncHttpClient asyncHttpClient = new DefaultAsyncHttpClient(cf);
您可以删除自己的重试逻辑,并让AsyncHttpClient处理相同的逻辑。
,在某些情况下,许多人希望抛出新的RuntimeException(ee),而不是抛出e
<div id="chart"></div>
<script>
$("#chart").kendoChart({
categoryAxis: [{
justified: true,categories: ["2012","2013"]
}],series: [
{ type: "line",data: [1,-2,3] },{ type: "bar",3] }
]
});
</script>