java-retrying: 简单灵活可配的java重试模块

由来

之前做项目的时候遇到大量这样的场景: 自个儿的服务通过http请求底层系统申请资源, 然后不断的每隔一段时间查询底层系统, 直到资源准备完成或超时.

这不同于一般的失败重试机制, 可以在一个线程里重试多次, 这样非常低效. 所以想到了异步重试, 起先自己通过spring的TaskScheduler, java8的CompletableFuture实现了一个非常简陋的异步重试类, 够用但是非常局限. 后来在网上搜到了guava-retrying, 用起来真的非常舒爽, 但是他只支持同步重试, 而且github上已经两三年没更新了, 看了他的代码非常简单, 于是打算自己在此基础上写一个, 就有了本文中的java-retrying.

Github地址: https://github.com/lowzj/java-retrying

java-retrying完全去除了第三方依赖, 使用了jdk自身的ScheduledExecutorService, CompletableFuture实现异步重试.

比较一下guava-retryingjava-retrying:

名称 JDK 第三方依赖 同步重试 异步重试
guava-retrying 大于等于6 guava,findbugs Y N
java-retrying 大于等于8 Y Y

使用方法

使用起来非常简单灵活, 可以配置各种WaitStategy, StopStategy, 以及配置各种重试条件(根据结果, 异常类型等等).

依赖:

<dependency>
    <groupId>com.github.lowzj</groupId>
    <artifactId>java-retrying</artifactId>
    <version>1.2</version>
</dependency>

举两个例子最直观:

  • 同步重试
    Retryer<Integer> retryer = RetryerBuilder.<Integer>newBuilder()
      .withWaitStrategy(WaitStrategies.fixedWait(100L, TimeUnit.MILLISECONDS))
      .retryIfResult(num -> num != 5)
      .retryIfExceptionOfType(RuntimeException.class)
      .withStopStrategy(StopStrategies.stopAfterAttempt(7))
      .build();
    try {
      retryer.call(noRuntimeExceptionAfter(4));
    } catch (ExecutionException | RetryException e) {
      e.printStackTrace();
    }
    
  • 异步重试
    AsyncRetryer<Integer> asyncRetryer = RetryerBuilder.<Integer>newBuilder()
      .withWaitStrategy(WaitStrategies.fixedWait(100L, TimeUnit.MILLISECONDS))
      .retryIfResult(num -> num != 4)
      .retryIfExceptionOfType(RuntimeException.class)
      .withStopStrategy(StopStrategies.stopAfterAttempt(7))
      .withExecutor(ExecutorsUtil.scheduledExecutorService("example", 1))
      .buildAsyncRetryer();
    CompletableFuture<Integer> future = asyncRetryer.call(noRuntimeExceptionAfter(3));
    // get the result asynchronously
    future.whenComplete((result, error) -> System.out.println(result));
    // or get the result synchronously
    try {
      System.out.println(future.get());
    } catch (InterruptedException | ExecutionException e) {
      e.printStackTrace();
    }
    

其中函数noRuntimeExceptionAfter如下:

private Callable<Integer> noRuntimeExceptionAfter(final int attemptNumber) {
    return new Callable<Integer>() {
        private int count = 0;
        @Override
        public Integer call() throws Exception {
            if (count++ < attemptNumber) {
                throw new RuntimeException("count[" + (count - 1) + "] < attemptNumber[" + attemptNumber + "]");
            }
            return count;
        }
    };
}

异步重试时需要通过RetryerBuilder#withExecutor配置一个类型为ScheduledExecutorService的线程池, java-retrying中提供了一个工具类ExecutorsUtil可以非常方便的构造线程池. 如果不配置的话, 默认会提供一个名为default-async-retry, 核心线程数为5的线程池.

其他就没什么了, 更具体的可以看下代码, 代码本身也非常简单.

Copyright © lowzj all right reserved,powered by GitbookLast Modified: 2023-03-12 01:06

results matching ""

    No results matching ""