在多次异步请求调用失败后,我如何完成重试?我正在使用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) {
}
}
...
发布于 2020-09-30 04:30:23
AsyncHttpClient中有一个预定义函数来处理MaxRetries,
下面的代码显示了一个简单的实现
AsyncHttpClientConfig cf = new DefaultAsyncHttpClientConfig.Builder().setMaxRequestRetry(5).setKeepAlive(true).build()
final AsyncHttpClient asyncHttpClient = new DefaultAsyncHttpClient(cf);
您可以删除您的重试逻辑,让AsyncHttpClient处理相同的逻辑。
发布于 2020-09-27 15:06:53
好的,所以试着用代码复制您想要实现的目标,但是立即意识到,只有当CustomException
是RuntimeException
类型时,它才能工作。原因是您希望在运行时和在另一个线程中抛出异常。
下面的代码显示了异常的简单实现。请记住,并非所有的RuntimeExceptions都停止程序。在这个线程中解释了这一点。因此,如果您想终止程序,您必须手动停止它。
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);
}
}
发布于 2020-10-03 17:49:50
在某些情况下,许多人希望抛出新的RuntimeException (ee),而不是抛出e。
catch (ExecutionException e) {
Throwable ee = e.getCause ();
if (ee instanceof InvalidInputException)
{
//error handling 1
} else if (ee instanceof MiscalculationException e)
{
//error handling 2
}
else throw e; // Not ee here
}
https://stackoverflow.com/questions/64040323
复制相似问题