文章目录

  • 分析重试
  • 使用
  • 风险
  • 重试实现
  • spring-retry
  • Guava Retry



微服务之间的调用会因为一方的不稳定或其他原因,导致失败,从而导致系统也陷入不稳定。因此有了重试这个机制。

参考文章:
重试的实现优雅的重试


分析重试

使用

重试次数:具体业务具体分析,一般三次。

重试间隔:要根据被调用的系统平均恢复时间去正确估量,通常而言这个平均恢复时间很难统计到,所以一般的经验值是3至5分钟。

重试完依旧失败:错误报警机制;手动重试开关。

风险

重试能很好的避开网络抖动或其他关联应用暂时down机维护带来的系统不可用问题。

但是在微服务环境下,重试会有放大故障的风险:

  1. 调用下游的压力加大,当调用失败进行重试,被调用方的被调用次数会加快增大,负载量升高。
  2. 微服务存在调用链,且可能不是一条直链。会有链路放大的效果,若链路最后一步失败,之前的微服务都有重试机制,这时倒数第二步重试n次,失败后倒数第三步又重试n次,这n次会让倒数第二步重试n*n次····。

重试实现

spring-retry

spring提供了原生的重试类库,提供了重试注解。

<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
</dependency>

在启动类或者配置类上添加@EnableRetry注解,并在需要重试的方法上添加@Retryable注解。

@Retryable注解的主要参数包括 exclude、include、interceptor、maxAttempts、value、backoff。其中backoff 也是个注解,包含delay、maxDelay、multiplier、random。

这种方法的问题

  1. 采用AspectJ增强,方法内部调用时注解失效。
  2. 只支持对异常进行捕获。

Guava Retry

相比spring-retry,Guava Retry更加灵活,虽然已经好几年不更新了,但够用。

<dependency>
    <groupId>com.github.rholder</groupId>
    <artifactId>guava-retrying</artifactId>
</dependency>

Guava-retry需先创建重试器,重试器可设置重试时机、间隔、次数。

// 创建一个重试器,重试器执行的方法,返回值为Boolean类型
Retryer<Integer> retryer = RetryerBuilder.<Integer>newBuilder()
   // 当运行结果大于10的时候,需要重试(表达式返回true,则重试)
    .retryIfResult(res -> {
                    return res > 10;
                })
    // 当抛出异常时,就会进行重试
    .retryIfException()

    // 或者当抛出ArithmeticException异常时,进行重试
    .retryIfExceptionOfType(ArithmeticException.class)

    // 如果抛出RuntimeException异常时,进行重试
    .retryIfRuntimeException()

    // 捕获到异常,对异常进行处理,返回true时,会进行重试
    .retryIfException(exception -> {
        System.out.println("捕获到" + exception);
        // 可以对异常信息、异常种类进行处理,决定是否需要重试
        return StringUtils.contains(exception.getMessage(), "wrong");
    })
    // 失败后,隔2秒后重试
    .withWaitStrategy(WaitStrategies.fixedWait(2, TimeUnit.SECONDS))
    // 重试3次后,仍未成功,就不再重试
    .withStopStrategy(StopStrategies.stopAfterAttempt(3))
    .build();

// 使用重试器,执行具体逻辑
Boolean res = retryer.call(() -> {
    return testCode();
});