文章目录
- 分析重试
- 使用
- 风险
- 重试实现
- spring-retry
- Guava Retry
微服务之间的调用会因为一方的不稳定或其他原因,导致失败,从而导致系统也陷入不稳定。因此有了重试这个机制。
参考文章:
重试的实现优雅的重试
分析重试
使用
重试次数:具体业务具体分析,一般三次。
重试间隔:要根据被调用的系统平均恢复时间去正确估量,通常而言这个平均恢复时间很难统计到,所以一般的经验值是3至5分钟。
重试完依旧失败:错误报警机制;手动重试开关。
风险
重试能很好的避开网络抖动或其他关联应用暂时down机维护带来的系统不可用问题。
但是在微服务环境下,重试会有放大故障的风险:
- 调用下游的压力加大,当调用失败进行重试,被调用方的被调用次数会加快增大,负载量升高。
- 微服务存在调用链,且可能不是一条直链。会有链路放大的效果,若链路最后一步失败,之前的微服务都有重试机制,这时倒数第二步重试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。
这种方法的问题:
- 采用AspectJ增强,方法内部调用时注解失效。
- 只支持对异常进行捕获。
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();
});