指数退避算法
本节转载自: https://cloud.google.com/memorystore/docs/redis/exponential-backoff?hl=zh-cn
指数退避算法是适用于网络应用的标准错误处理策略,使用这种策略时,客户端会定期重试失败的请求,并不断增加各次请求之间的延迟时间。
示例算法
指数退避算法以指数方式重试请求(不断增加各次重试之间的等待时间,直到达到最大退避时间)。示例如下:
- 客户端发出请求。
- 如果请求失败,请等待
1 + random_number_milliseconds
秒后再重试请求。 - 如果请求失败,请等待
2 + random_number_milliseconds
秒后再重试请求。 - 如果请求失败,请等待
4 + random_number_milliseconds
秒后再重试请求。 - 依此类推,等待时间上限为
maximum_backoff
。
等待时间达到上限后,您可以继续等待并重试,直到达到重试次数上限(但接下来的重试操作不会增加各次重试之间的等待时间
)。
其中:
- 等待时间为
min(((2^n)+random_number_milliseconds), maximum_backoff)
,其中,n 会在每次迭代(请求)后增加 1。 random_number_milliseconds 是小于或等于 1000 的毫秒数
(随机值)。这有助于避免出现以下情况:许多客户端在某些情况下全部同步进行处理并同时执行重试操作,导致同步发送每一波请求。每次重试请求后,系统都应重新计算 random_number_milliseconds 值。
maximum_backoff 通常为 32 或 64 秒
。哪个值更为适当,这取决于用例。- 达到 maximum_backoff 时间后,您可以继续重试。 此后执行的重试不需要继续增加退避时间。
- 例如,如果客户端使用的 maximum_backoff 时间为 64 秒,则在达到此值后,客户端可以每 64 秒重试一次。在某个时刻,应阻止客户端无限重试。
Spring 退避算法
@Retryable
maven依赖
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
java code
@Service
public class OrderService {
private Logger logger = LoggerFactory.getLogger(this.getClass());
private AtomicInteger loop = new AtomicInteger(1);
private Instant instantBefore;
@Retryable(
value = RuntimeException.class , //抛出该异常时,进行重试
maxAttempts=12, // 最大重试次数,超过直接抛出异常
backoff =
@Backoff(
delay=500, //初始delay时间
multiplier = 2, // 设置为2时,可以理解为
maxDelay=10000, // min(delay * multiplier^(attempt - 1), maxDelay)
random = false //是否使用随机数
)
)
public void readOrder(String orderId){
Instant now = Instant.now();
long gap = instantBefore == null ? 0 : Duration.between(instantBefore, now).toMillis();
logger.info("{} --- time-gap :{}",loop.getAndIncrement(),gap);
instantBefore = now;
if( 1 < 2) throw new RuntimeException(); // 抛出异常,进行重试
}
}
上述算法可以描述为: min(delay * multiplier^(attempt - 1), maxDelay)
,
当
multiplier=2 && random=true
,可以近似理解为指数退避算法
执行结果
RetryTemplate
public void readOrder(String orderId){
int retryCount = 3;
RetryTemplate retryTemplate = RetryTemplate.builder()
.maxAttempts(retryCount)
// .uniformRandomBackoff(10000, 15000)
.exponentialBackoff(500,2, 10000)
.retryOn(RetryException.class) //抛出该异常时,进行重试
.build();
retryTemplate.execute(context -> {
System.out.println("xx");
return null;
});
}