woshikid / blog

Apache License 2.0
8 stars 1 forks source link

Spring Retry学习笔记 #169

Open woshikid opened 2 years ago

woshikid commented 2 years ago

POM

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

编程式重试

RetryPolicy

// 永不重试,直接抛出异常或调用RecoveryCallback
NeverRetryPolicy neverRetryPolicy = new NeverRetryPolicy();

// 无限重试
AlwaysRetryPolicy alwaysRetryPolicy = new AlwaysRetryPolicy();

// 发生异常时重试,直到时间超时
TimeoutRetryPolicy timeoutRetryPolicy = new TimeoutRetryPolicy();
timeoutRetryPolicy.setTimeout(1000); // 默认为1000毫秒

// 发生异常时重试,直到最大次数
SimpleRetryPolicy simpleRetryPolicy = new SimpleRetryPolicy();
simpleRetryPolicy.setMaxAttempts(3); // 默认为3次=初始1次+重试2次

// 发生异常时重试,直到最大次数或表达式为false
ExpressionRetryPolicy expressionRetryPolicy = new ExpressionRetryPolicy("#root.class == T(java.lang.RuntimeException)");
expressionRetryPolicy.setMaxAttempts(3); // 继承SimpleRetryPolicy

// 需要配合RetryState进行全局重试,此时单次请求不会重试
// 在熔断窗口内代理的策略发生不能重试时进行熔断
// 熔断时抛出最后一次异常或调用RecoveryCallback
// 熔断时间必须大于熔断窗口,熔断超时后全开
CircuitBreakerRetryPolicy circuitBreakerRetryPolicy = new CircuitBreakerRetryPolicy(simpleRetryPolicy); // 代理策略,默认为SimpleRetryPolicy
circuitBreakerRetryPolicy.setOpenTimeout(5000); // 熔断窗口,默认为5000
circuitBreakerRetryPolicy.setResetTimeout(20000); // 熔断时间(必须大于熔断窗口,到时间后全开),默认为20000

// 组合策略,乐观模式为任一策略允许重试即可重试,悲观模式为每一策略允许重试才可重试
CompositeRetryPolicy compositeRetryPolicy = new CompositeRetryPolicy();
compositeRetryPolicy.setOptimistic(false); // 乐观模式,默认为false
compositeRetryPolicy.setPolicies(new RetryPolicy[] {simpleRetryPolicy, timeoutRetryPolicy});

// 基于异常类型的组合策略
ExceptionClassifierRetryPolicy exceptionClassifierRetryPolicy = new ExceptionClassifierRetryPolicy();
//exceptionClassifierRetryPolicy.setExceptionClassifier(new SubclassClassifier<>(Map.of(RuntimeException.class, simpleRetryPolicy), new NeverRetryPolicy()));
exceptionClassifierRetryPolicy.setPolicyMap(Map.of(RuntimeException.class, simpleRetryPolicy)); // 效果同上

RetryState

// 发生异常后不再抛出(继续重试)
RetryState retryState = new DefaultRetryState("key", new BinaryExceptionClassifier(false));
// 发生特定异常后抛出(全局重试时,forceRefresh必须为false,局部重试时,forceRefresh必须为true)
RetryState retryState = new DefaultRetryState("key", false, new BinaryExceptionClassifier(Map.of(RuntimeException.class, true)));

BackOffPolicy

// 无退让,直接重试
NoBackOffPolicy noBackOffPolicy = new NoBackOffPolicy();

// 固定退让
FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
fixedBackOffPolicy.setBackOffPeriod(1000); // 退让时间,默认为1000

// 随机退让
UniformRandomBackOffPolicy uniformRandomBackOffPolicy = new UniformRandomBackOffPolicy();
uniformRandomBackOffPolicy.setMaxBackOffPeriod(1500); // 最大退让时间,默认为1500
uniformRandomBackOffPolicy.setMinBackOffPeriod(500); // 最小退让时间,默认为500

// 指数退让
ExponentialBackOffPolicy exponentialBackOffPolicy = new ExponentialBackOffPolicy();
exponentialBackOffPolicy.setInitialInterval(100); // 初始退让时间,默认为100
exponentialBackOffPolicy.setMultiplier(2.0); // 退让乘数,默认为2.0
exponentialBackOffPolicy.setMaxInterval(30000); // 最大退让时间,默认为30000

// 指数随机退让
ExponentialRandomBackOffPolicy exponentialRandomBackOffPolicy = new ExponentialRandomBackOffPolicy();
exponentialRandomBackOffPolicy.setInitialInterval(100); // 初始退让时间,默认为100
exponentialRandomBackOffPolicy.setMultiplier(2.0); // 退让乘数,默认为2.0
exponentialRandomBackOffPolicy.setMaxInterval(30000); // 最大退让时间,默认为30000

使用RetryTemplate

RetryTemplate retryTemplate = new RetryTemplate();
retryTemplate.setRetryPolicy(simpleRetryPolicy); // 默认为SimpleRetryPolicy
retryTemplate.setBackOffPolicy(noBackOffPolicy); // 默认为NoBackOffPolicy

// 无状态重试
String result = retryTemplate.execute(context -> "RetryCallback", context -> "RecoveryCallback");
// 有状态重试(用于回滚的局部重试或用于熔断的全局重试)
String result = retryTemplate.execute(context -> "RetryCallback", context -> "RecoveryCallback", retryState);

使用RetryTemplateBuilder

RetryTemplate.builder()
    .retryOn(Exception.class) // 默认为Exception.class
    .maxAttempts(3) // 默认为3
    .noBackoff() // 默认为NoBackOffPolicy
    .build();

RetryTemplate.builder()
    .notRetryOn(RuntimeException.class) // 与retryOn二选一
    .traversingCauses() // 遍历异常的cause,配合retryOn/notRetryOn使用
    .infiniteRetry() // 无限重试
    .exponentialBackoff(100, 2.0, 30000, true) // 指数随机退让
    .build();

声明式重试

需依赖Spring AOP,详见 #140,#161

启用注解重试

<bean class="org.springframework.retry.annotation.RetryConfiguration"/>

或使用Java配置

@EnableRetry

使用注解重试

@Retryable // 默认maxAttempts = 3, backoff = @Backoff(1000), include = Exception.class
@Retryable(maxAttemptsExpression = "${retry.maxAttempts:10}")
@Retryable(exclude = RuntimeException.class) // 可与include同时使用
@Retryable(exceptionExpression = "#root.class == T(java.lang.RuntimeException)") // 相当于ExpressionRetryPolicy
@Retryable(backoff = @Backoff(delay = 100, multiplier = 2.0, maxDelay = 30000, random = true)) // 相当于ExponentialRandomBackOffPolicy
@Retryable(recover = "recover") // 指定RecoveryCallback,不使用此参数将随机选择@Recover
@Retryable(stateful = true) // 仅限用于熔断的全局重试

@Recover // 返回值类型需与原方法一致,使用recover指定时,方法参数需为Exception加原方法参数

// 注意:@CircuitBreaker中的属性将覆盖@Retryable中的属性
// 注意:include,exclude与exceptionExpression表示一个异常不能重试时,将立即熔断
@CircuitBreaker // 默认maxAttempts = 3, openTimeout = 5000, resetTimeout = 20000,默认包含@Retryable(stateful = true)

内部调用重试

AopContext.currentProxy(); // 需打开expose-proxy