可重试的操作

在我从事的每个项目中,总是需要某些功能:重试操作。 通常,它是关于通过网络的呼叫,该呼叫可能一次失败,但随后会成功。 它可能涉及许多其他内容,主要包括与另一个系统的通信(无论是否通过网络)。 它的功能,你绝对需要在大多数应用中,特别是如果你想他们是高可用性( 如这里指出的 ,例如)。

每次我必须在项目中引入此功能时,我都会检查我们已导入的标准库,没有这样的东西。 因此,我总是最终将我以前的项目中的同一段代码复制粘贴。 我什至不记得我是什么时候第一次介绍它的,但是从那以后我就在“旅行”。 所以这里是:

/**
 * Class that provides retrying functionality. Example:
 * <p></p>
 * <code>
 * Callable<String> callable = new Callable<String>() {..};
 * String result = RetryableOperation.create(callable).retry(5, IOException.class);
 * </code>
 *
 * @param <T> the return type of the operation
 */
public class RetryableOperation<T> {
    private Callable<T> callable;
    private Runnable runnable;
    private boolean exponentialBackoff;
    private int backoffInterval = 500;

    /**
     * Create a retryable operation based on a Callable instance. The return
     * type of retry(..) is the type parameter of the Callable instance.
     *
     * @param callable
     * @return
     *      a new instance of RetryableOperation
     */
    public static <T> RetryableOperation<T> create(Callable<T> callable) {
        return new RetryableOperation<T>().withCallable(callable);
    }

    /**
     * Creates a retryable operation based on a Runnable instance. In this case
     * the retry(..) method always returns null.
     *
     * @param runnable
     * @return
     *      a new instance of RetryableOperation
     */
    public static RetryableOperation<?> create(Runnable runnable) {
        return new RetryableOperation<Object>().withRunnable(runnable);
    }

    /**
     * Retries the operation. Retrying happens regardless of the exception thrown.
     *
     * @param retries
     *      number of retries before the exception is thrown to the caller
     * @param exceptions
     *      the operation will be retried only if the exception that occurs is one of the
     *      exceptions passed in this array
     * @return
     *      the result of the operation (null if Runnable is used instead of Callable)
     * @throws Exception
     *      the exception that occurred on the last attempt
     */
    public T retry(int retries, Class<? extends Exception>... exceptions) throws Exception {
        if (callable == null && runnable == null) {
            throw new IllegalStateException("Either runnable or callable must be set");
        }
        Set<Class<? extends Exception>> retryFor = new HashSet<Class<? extends Exception>>();
        retryFor.addAll(Arrays.asList(exceptions));
        for (int i = 0; i < retries; i++) {
            try {
                if (exponentialBackoff && i > 0) {
                    int sleepTime = (int) ((Math.pow(2, i) - 1) / 2) * backoffInterval;
                    Thread.sleep(sleepTime);
                }
                if (callable != null) {
                    return callable.call();
                } else if (runnable != null) {
                    runnable.run();
                    return null;
                }
            } catch (Exception e) {
                if (retryFor.isEmpty() || retryFor.contains(e.getClass())) {
                    if (i == retries - 1) {
                        throw e;
                    }
                } else {
                    // if the exception is not the expected one, throw it immediately
                    throw e;
                }
            }
        }
        // can't be reached - in case of failure on the last iteration the exception is rethrown
        return null;
    }


    private RetryableOperation<T> withCallable(Callable<T> callable) {
        this.callable = callable;
        return this;
    }

    private RetryableOperation<T> withRunnable(Runnable runnable) {
        this.runnable = runnable;
        return this;
    }

    public RetryableOperation<T> withExponentialBackoff() {
        this.exponentialBackoff = true;
        return this;
    }
}
1

这很简单,但是效果很好。 您可以重试每个失败,也可以重试特定的异常(您不想重试NullPointerException,但是必须在配置了适当的超时后重试网络故障):

Result result = op.retry(3);
   ...
   Result result = op.retry(3, IOException.class);

我什至曾建议番石榴将其包含在内,然后再看其他类似的提议,但据我所知,番石榴或apache commons中都没有这种功能。 而且我不会创建一个新的github项目,因为那将需要在maven Central中管理一个条目,而对于单个实用程序类来说,这太费力了。

当然,还有其他解决方法,它们具有更大的API和占用空间– 重试番石榴扩展和最近提取为单独的项目spring-retry 。 它们值得检查,并且具有要导入的Maven依赖项。

无论选择什么选项,请检查它是否支持匿名功能(自Java 8起)。 它可能会自动执行,但仍会检查。

关键是要提供此功能并使用非常简单的API,以便您可以避免用户可避免的故障-必须重试几次对外部系统的调用。

翻译自: https://www.javacodegeeks.com/2015/10/retryable-operations.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值