Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/325.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 使用CompletableFuture重试逻辑_Java_Exception_Asynchronous_Concurrency_Java 8 - Fatal编程技术网

Java 使用CompletableFuture重试逻辑

Java 使用CompletableFuture重试逻辑,java,exception,asynchronous,concurrency,java-8,Java,Exception,Asynchronous,Concurrency,Java 8,我需要在我正在处理的异步框架中提交一个任务,但我需要捕获异常,并在“中止”之前多次重试同一任务 我使用的代码是: int retries = 0; public CompletableFuture<Result> executeActionAsync() { // Execute the action async and get the future CompletableFuture<Result> f = executeMycustomActionH

我需要在我正在处理的异步框架中提交一个任务,但我需要捕获异常,并在“中止”之前多次重试同一任务

我使用的代码是:

int retries = 0;
public CompletableFuture<Result> executeActionAsync() {

    // Execute the action async and get the future
    CompletableFuture<Result> f = executeMycustomActionHere();

    // If the future completes with exception:
    f.exceptionally(ex -> {
        retries++; // Increment the retry count
        if (retries < MAX_RETRIES)
            return executeActionAsync();  // <--- Submit one more time

        // Abort with a null value
        return null;
    });

    // Return the future    
    return f;
}
int重试次数=0;
公共CompletableFuture executeActionAsync(){
//异步执行操作并获得未来
CompletableFuture f=executeMycustomActionHere();
//如果未来完成,但有例外:
f、 例外(ex->{
retries++;//增加重试次数
如果(重试次数<最大重试次数)

return executeActionAsync();//我最近使用这个库解决了一个类似的问题

Callable Callable=new Callable(){
公共结果调用()引发异常{
返回executeMycustomActionHere();
}
};
Retryer-Retryer=RetryerBuilder.newBuilder()
.RetryFresult(谓词.isNull())
.RetryFeExceptionofType(IOException.class)
.RetryFruntimeException()
.使用停止策略(停止策略。停止后尝试(最大重试次数))
.build();
CompletableFuture.SupplySync(()->{
试一试{
retryer.call(可调用);
}捕获(重试异常){
e、 printStackTrace();
}捕获(执行例外){
e、 printStackTrace();
}
});

链接后续重试可以直接进行:

public CompletableFuture<Result> executeActionAsync() {
    CompletableFuture<Result> f=executeMycustomActionHere();
    for(int i=0; i<MAX_RETRIES; i++) {
        f=f.exceptionally(t -> executeMycustomActionHere().join());
    }
    return f;
}
演示如何将“撰写”和“异常”处理程序结合起来

此外,如果所有重试都失败,则只会报告最后一个异常。更好的解决方案应该报告第一个异常,并将重试的后续异常添加为抑制异常。这样的解决方案可以通过链接递归调用来构建,正如所暗示的,但是,为了将此思想用于异常处理,我们必须执行上面所示的组合“组合”和“例外”的步骤:

public CompletableFuture<Result> executeActionAsync() {
    return executeMycustomActionHere()
        .thenApply(CompletableFuture::completedFuture)
        .exceptionally(t -> retry(t, 0))
        .thenCompose(Function.identity());
}
private CompletableFuture<Result> retry(Throwable first, int retry) {
    if(retry >= MAX_RETRIES) return CompletableFuture.failedFuture(first);
    return executeMycustomActionHere()
        .thenApply(CompletableFuture::completedFuture)
        .exceptionally(t -> { first.addSuppressed(t); return retry(first, retry+1); })
        .thenCompose(Function.identity());
}

我想我成功了。下面是我创建的示例类和测试代码:


RetriableTask.java
主要思想和一些粘合代码(
failAfter
函数)来自


欢迎任何其他建议或改进。

这里有一种方法,适用于任何
CompletionStage
子类,并且不会返回一个虚拟的
CompletableFuture
,它只会等待其他futures更新

/**
 * Sends a request that may run as many times as necessary.
 *
 * @param request  a supplier initiates an HTTP request
 * @param executor the Executor used to run the request
 * @return the server response
 */
public CompletionStage<Response> asyncRequest(Supplier<CompletionStage<Response>> request, Executor executor)
{
    return retry(request, executor, 0);
}

/**
 * Sends a request that may run as many times as necessary.
 *
 * @param request  a supplier initiates an HTTP request
 * @param executor the Executor used to run the request
 * @param tries    the number of times the operation has been retried
 * @return the server response
 */
private CompletionStage<Response> retry(Supplier<CompletionStage<Response>> request, Executor executor, int tries)
{
    if (tries >= MAX_RETRIES)
        throw new CompletionException(new IOException("Request failed after " + MAX_RETRIES + " tries"));
    return request.get().thenComposeAsync(response ->
    {
        if (response.getStatusInfo().getFamily() != Response.Status.Family.SUCCESSFUL)
            return retry(request, executor, tries + 1);
        return CompletableFuture.completedFuture(response);
    }, executor);
}
/**
*发送一个请求,该请求可以根据需要多次运行。
*
*@param request供应商发起HTTP请求
*@param executor用于运行请求的执行器
*@返回服务器响应
*/
公共CompletionStage异步请求(供应商请求、执行者执行者)
{
返回重试(请求,执行者,0);
}
/**
*发送一个请求,该请求可以根据需要多次运行。
*
*@param request供应商发起HTTP请求
*@param executor用于运行请求的执行器
*@param尝试重试该操作的次数
*@返回服务器响应
*/
私有CompletionStage重试(供应商请求、执行者执行者、整数次重试)
{
如果(尝试次数>=最大重试次数)
抛出新的CompletionException(新的IOException(“在“+MAX_RETRIES+”Trys”之后请求失败”);
返回请求.get().ThencomposeSync(响应->
{
if(response.getStatusInfo().getFamily()!=response.Status.Family.SUCCESSFUL)
返回重试(请求、执行者、重试次数+1);
返回CompletableFuture.completedFuture(响应);
},遗嘱执行人);
}

我建议您使用一个经过验证的库,而不是实现您自己的重试逻辑,它内置了对未来的支持(而且似乎比以前更流行)。例如,它看起来像:

private static RetryPolicy retryPolicy = new RetryPolicy()
    .withMaxRetries(MAX_RETRIES);

public CompletableFuture<Result> executeActionAsync() {
    return Failsafe.with(retryPolicy)
        .with(executor)
        .withFallback(null)
        .future(this::executeMycustomActionHere);
}
private static RetryPolicy RetryPolicy=new RetryPolicy()
.具有最大重试次数(最大重试次数);
公共CompletableFuture executeActionAsync(){
使用(retryPolicy)返回故障保护
.与(遗嘱执行人)
.withFallback(空)
.future(此::executeMycustomActionHere);
}
可能您应该避免
.withFallback(null)
,只需让返回的future的
.get()
方法抛出结果异常,以便方法的调用方可以专门处理它,但这是您必须做出的设计决定

需要考虑的其他事项包括是否应立即重试或在尝试之间等待一段时间,任何类型的递归回退(在调用可能已关闭的web服务时非常有用),以及是否存在不值得重试的特定异常(例如,如果方法的参数无效).

util类:

public class RetryUtil {

    public static <R> CompletableFuture<R> retry(Supplier<CompletableFuture<R>> supplier, int maxRetries) {
        CompletableFuture<R> f = supplier.get();
        for(int i=0; i<maxRetries; i++) {
            f=f.thenApply(CompletableFuture::completedFuture)
                .exceptionally(t -> {
                    System.out.println("retry for: "+t.getMessage());
                    return supplier.get();
                })
                .thenCompose(Function.identity());
        }
        return f;
    }
}

可能已经晚了,但希望有人会觉得这很有用,我最近解决了在失败时重试rest API调用的问题。在我的情况下,我必须重试500个HTTP状态代码,下面是我的rest客户端代码(我们使用的是play framework中的WSClient),您可以根据需要将其更改为任何rest客户端

 int MAX_RETRY = 3;
 CompletableFuture<WSResponse> future = new CompletableFuture<>();

 private CompletionStage<WSResponse> getWS(Object request,String url, int retry, CompletableFuture future) throws JsonProcessingException {
 ws.url(url)
        .post(Json.parse(mapper.writeValueAsString(request)))
        .whenCompleteAsync((wsResponse, exception) -> {
            if(wsResponse.getStatus() == 500 && retry < MAX_RETRY) {
                try {
                    getWS(request, retry+1, future);
                } catch (IOException e) {
                    throw new Exception(e);
                }
            }else {
                future.complete(wsResponse);
            }
        });

     return future;
}
int MAX_RETRY=3;
CompletableFuture=新的CompletableFuture();
私有CompletionStage getWS(对象请求、字符串url、int重试、CompletableFuture)抛出JsonProcessingException{
url(url)
.post(Json.parse(mapper.writeValueAsString(请求)))
.WhenCompleteTasync((wsResponse,exception)->{
if(wsResponse.getStatus()==500&&retry

如果状态代码为200或不是500,此代码将立即返回,而如果HTTP状态为500,则将重试3次。

我们需要根据错误条件重试任务

public static <T> CompletableFuture<T> retryOnCondition(Supplier<CompletableFuture<T>> supplier,
                                             Predicate<Throwable> retryPredicate, int maxAttempts) {
    if (maxAttempts <= 0) {
        throw new IllegalArgumentException("maxAttempts can't be <= 0");
    }
    return retryOnCondition(supplier, retryPredicate, null, maxAttempts);
}

private static <T> CompletableFuture<T> retryOnCondition(
    Supplier<CompletableFuture<T>> supplier, Predicate<Throwable> retryPredicate,
    Throwable lastError, int attemptsLeft) {

    if (attemptsLeft == 0) {
        return CompletableFuture.failedFuture(lastError);
    }

    return supplier.get()
        .thenApply(CompletableFuture::completedFuture)
        .exceptionally(error -> {
            boolean doRetry = retryPredicate.test(error);
            int attempts = doRetry ? attemptsLeft - 1 : 0;
            return retryOnCondition(supplier, retryPredicate, error, attempts);
        })
        .thenCompose(Function.identity());
}

注意:如果您不想向调用者报告异常,而是退回到问题中指出的
null
值(尽管我
generating...
issuing...
Waiting...
0 I got exception!
1 I got exception!
2 I got exception!
3 I got exception!
4 I got exception!
5 I got exception!
6 result is 36
7 result is 42
8 result is 48
9 result is 54
Done waiting...
/**
 * Sends a request that may run as many times as necessary.
 *
 * @param request  a supplier initiates an HTTP request
 * @param executor the Executor used to run the request
 * @return the server response
 */
public CompletionStage<Response> asyncRequest(Supplier<CompletionStage<Response>> request, Executor executor)
{
    return retry(request, executor, 0);
}

/**
 * Sends a request that may run as many times as necessary.
 *
 * @param request  a supplier initiates an HTTP request
 * @param executor the Executor used to run the request
 * @param tries    the number of times the operation has been retried
 * @return the server response
 */
private CompletionStage<Response> retry(Supplier<CompletionStage<Response>> request, Executor executor, int tries)
{
    if (tries >= MAX_RETRIES)
        throw new CompletionException(new IOException("Request failed after " + MAX_RETRIES + " tries"));
    return request.get().thenComposeAsync(response ->
    {
        if (response.getStatusInfo().getFamily() != Response.Status.Family.SUCCESSFUL)
            return retry(request, executor, tries + 1);
        return CompletableFuture.completedFuture(response);
    }, executor);
}
private static RetryPolicy retryPolicy = new RetryPolicy()
    .withMaxRetries(MAX_RETRIES);

public CompletableFuture<Result> executeActionAsync() {
    return Failsafe.with(retryPolicy)
        .with(executor)
        .withFallback(null)
        .future(this::executeMycustomActionHere);
}
public class RetryUtil {

    public static <R> CompletableFuture<R> retry(Supplier<CompletableFuture<R>> supplier, int maxRetries) {
        CompletableFuture<R> f = supplier.get();
        for(int i=0; i<maxRetries; i++) {
            f=f.thenApply(CompletableFuture::completedFuture)
                .exceptionally(t -> {
                    System.out.println("retry for: "+t.getMessage());
                    return supplier.get();
                })
                .thenCompose(Function.identity());
        }
        return f;
    }
}
public CompletableFuture<String> lucky(){
    return CompletableFuture.supplyAsync(()->{
        double luckNum = Math.random();
        double luckEnough = 0.6;
        if(luckNum < luckEnough){
            throw new RuntimeException("not luck enough: " + luckNum);
        }
        return "I'm lucky: "+luckNum;
    });
}
@Test
public void testRetry(){
    CompletableFuture<String> retry = RetryUtil.retry(this::lucky, 10);
    System.out.println("async check");
    String join = retry.join();
    System.out.println("lucky? "+join);
}
async check
retry for: java.lang.RuntimeException: not luck enough: 0.412296354211683
retry for: java.lang.RuntimeException: not luck enough: 0.4099777199676573
lucky? I'm lucky: 0.8059089479049389
 int MAX_RETRY = 3;
 CompletableFuture<WSResponse> future = new CompletableFuture<>();

 private CompletionStage<WSResponse> getWS(Object request,String url, int retry, CompletableFuture future) throws JsonProcessingException {
 ws.url(url)
        .post(Json.parse(mapper.writeValueAsString(request)))
        .whenCompleteAsync((wsResponse, exception) -> {
            if(wsResponse.getStatus() == 500 && retry < MAX_RETRY) {
                try {
                    getWS(request, retry+1, future);
                } catch (IOException e) {
                    throw new Exception(e);
                }
            }else {
                future.complete(wsResponse);
            }
        });

     return future;
}
public static <T> CompletableFuture<T> retryOnCondition(Supplier<CompletableFuture<T>> supplier,
                                             Predicate<Throwable> retryPredicate, int maxAttempts) {
    if (maxAttempts <= 0) {
        throw new IllegalArgumentException("maxAttempts can't be <= 0");
    }
    return retryOnCondition(supplier, retryPredicate, null, maxAttempts);
}

private static <T> CompletableFuture<T> retryOnCondition(
    Supplier<CompletableFuture<T>> supplier, Predicate<Throwable> retryPredicate,
    Throwable lastError, int attemptsLeft) {

    if (attemptsLeft == 0) {
        return CompletableFuture.failedFuture(lastError);
    }

    return supplier.get()
        .thenApply(CompletableFuture::completedFuture)
        .exceptionally(error -> {
            boolean doRetry = retryPredicate.test(error);
            int attempts = doRetry ? attemptsLeft - 1 : 0;
            return retryOnCondition(supplier, retryPredicate, error, attempts);
        })
        .thenCompose(Function.identity());
}
public static void main(String[] args) {
    retryOnCondition(() -> myTask(), e -> {
        //log exception
        return e instanceof MyException;
    }, 3).join();
}