代码之家  ›  专栏  ›  技术社区  ›  orirab

根据结果重试方法(而不是异常)

  •  2
  • orirab  · 技术社区  · 7 年前

    我有一个具有以下签名的方法:

    public Optional<String> doSomething() {
        ...
    }
    

    如果我得到一个空的 Optional 我想重试此方法,仅在3次后返回空值 .

    我已经找过了,找到了 Retryable spring注释,但它似乎只适用于异常。

    如果可能,我希望为此使用库,并避免:

    • 创建并引发异常。
    3 回复  |  直到 7 年前
        1
  •  5
  •   Cristian Rodriguez    6 年前

    我一直在使用 failsafe 内置重试。 您可以基于谓词和异常重试。

    您的代码如下所示:

        private Optional<String> doSomethingWithRetry() {
            RetryPolicy<Optional> retryPolicy = new RetryPolicy<Optional>()
                    .withMaxAttempts(3)
                    .handleResultIf(result -> {
                        System.out.println("predicate");
                        return !result.isPresent();
                    });
    
            return Failsafe
                    .with(retryPolicy)
                    .onSuccess(response -> System.out.println("ok"))
                    .onFailure(response -> System.out.println("no ok"))
                    .get(() -> doSomething());
        }
    
        private Optional<String> doSomething() {
             return Optional.of("result");
        }
    

    如果可选项不为空,则输出为:

    predicate
    ok
    

    否则看起来像:

    predicate
    predicate
    predicate
    no ok
    
        2
  •  1
  •   Gary Russell    7 年前

    @Retryable (及有关的 RetryTemplate )完全基于例外情况。

    您可以创建子类 RetryTemplate doExecute() 检查返回值。

    retryCallback.doWithRetry() 呼叫

    您可以使用自定义 RetryTemplate RetryOperationsInterceptor @可回收 interceptor 财产)。

    编辑

    电流 RetryTemplate 代码看起来像这样。。。

    while (canRetry(retryPolicy, context) && !context.isExhaustedOnly()) {
    
        try {
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Retry: count=" + context.getRetryCount());
            }
            // Reset the last exception, so if we are successful
            // the close interceptors will not think we failed...
            lastException = null;
            return retryCallback.doWithRetry(context);
        }
        catch (Throwable e) {
    
            lastException = e;
    
            try {
                registerThrowable(retryPolicy, state, context, e);
            }
            catch (Exception ex) {
                throw new TerminatedRetryException("Could not register throwable",
                        ex);
            }
            finally {
                doOnErrorInterceptors(retryCallback, context, e);
            }
    
             ... 
    
        }
    

    你需要把它改成像。。。

    while (canRetry(retryPolicy, context) && !context.isExhaustedOnly()) {
    
        try {
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Retry: count=" + context.getRetryCount());
            }
            // Reset the last exception, so if we are successful
            // the close interceptors will not think we failed...
            lastException = null;
            T result = retryCallback.doWithRetry(context);
            if (((Optional<String>) result).get() == null) {
    
                try {
                    registerThrowable(retryPolicy, state, context, someDummyException);
                }
                catch (Exception ex) {
                    throw new TerminatedRetryException("Could not register throwable",
                            ex);
                }
                finally {
                    doOnErrorInterceptors(retryCallback, context, e);
                }
    
                ...
            }
            else {
                return result;
            }
        }
        catch (Throwable e) {
    
           ...
    
        }
    

    someDummyException 就是欺骗上下文使计数器递增。它可以是一个 static

        3
  •  1
  •   orirab    7 年前

    我目前自己编写了一个util(香草java),其他答案非常受欢迎:

    import java.util.function.Predicate;
    import java.util.function.Supplier;
    
    public class Retryable<T> {
        private Supplier<T> action = () -> null;
        private Predicate<T> successCondition = ($) -> true;
        private int numberOfTries = 3;
        private long delay = 1000L;
        private Supplier<T> fallback = () -> null;
    
        public static <A> Retryable<A> of(Supplier<A> action) {
            return new Retryable<A>().run(action);
        }
    
        public Retryable<T> run(Supplier<T> action) {
            this.action = action;
            return this;
        }
    
        public Retryable<T> successIs(Predicate<T> successCondition) {
            this.successCondition = successCondition;
            return this;
        }
    
        public Retryable<T> retries(int numberOfTries) {
            this.numberOfTries = numberOfTries;
            return this;
        }
    
        public Retryable<T> delay(long delay) {
            this.delay = delay;
            return this;
        }
    
        public Retryable<T> orElse(Supplier<T> fallback) {
            this.fallback = fallback;
            return this;
        }
    
        public T execute() {
            for (int i = 0; i < numberOfTries; i++) {
                T t = action.get();
                if (successCondition.test(t)) {
                    return t;
                }
    
                try {
                    Thread.sleep(delay);
                } catch (InterruptedException e) {
                    // do nothing
                }
            }
            return fallback.get();
        }
    }
    

    public Optional<String> doSomething() {
        return Retryable
            .of(() -> actualDoSomething())
            .successIs(Optional::isPresent)
            .retries(3)
            .delay(1000L)
            .orElse(Optional::empty)
            .execute();
    }
    
        4
  •  0
  •   Hoàng Vinh Quang    6 年前

    如果结果不理想,只需抛出异常