背景
在实际工作过程中,因网络波动、服务并发限制等原因造成接口服务调用失败,MQ中间件发送消息失败等,可以采取重试手段,重试机制是常见的一种处理问题的手段。
重试的手段有很多种,比如我们自己用代码逻辑实现,但是这种方式对代码的侵入性太强,不够优雅。
今天我给大家带来一种更优雅的方式实现重试机制,那就是 spring-retry 框架,该框架利用了 AOP 的特性,只需引入注解就可以轻松实现重试次数、重试延迟时间、重试触发条件、重试的回调方法等等。
使用
添加依赖
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
启动类增加 @EnableRetry 注解
@SpringBootApplication
@EnableRetry
public class ExampleApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication .class, args);
}
}
重试接口
/**
* @author xiaoxuxuy
* @date 2022/4/26 3:18 下午
*/
public interface TestRetryService {
/**
* 测试重试
*
* @param code
* @return
* @throws Exception
*/
int testRetry(int code) throws Exception;
}
重试接口实现
import com.scaffolding.example.sys.service.TestRetryService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
/**
* @author xiaoxuxuy
* @date 2022/4/26 3:37 下午
*/
@Slf4j
@Service
public class TestRetryServiceImpl implements TestRetryService {
/**
* @param code
* @return
* @throws Exception
*/
@Override
@Retryable(value = Exception.class, maxAttempts = 3, backoff = @Backoff(delay = 2000, multiplier = 1.5))
public int testRetry(int code) throws Exception {
log.info("testRetry, 时间: {}", LocalDateTime.now());
if (code == 0) {
throw new Exception("请求参数为0,出现异常");
}
System.out.println("testRetry调用成功!");
return 200;
}
@Recover
public int recover(Exception e, int code) {
System.out.println("重试次数结束后还有异常,回调方法开始执行!");
// 记录日志到数据库或者调用其余的方法
return 400;
}
}
注解释意:
@EnableRetry:此注解用于开启重试框架,可以修饰在SpringBoot启动类上面,也可以修饰在需要重试的类上
proxyTargetClass:Boolean类型,用于指明代理方式【true:cglib代理,false:jdk动态代理】默认使用jdk动态代理
@Retryable
value:Class[]类型,用于指定需要重试的异常类型
include:Class[]类型,作用同value类似
exclude:Class[]类型,指定不需要重试的异常类型
maxAttemps:int类型,指定最多重试次数,默认3
backoff:Backoff类型,指明补偿机制
@BackOff
delay:指定延迟后重试,默认为1000L,即1s后开始重试 ;
multiplier:指定延迟的倍数
@Recover
当重试次数耗尽依然出现异常时,执行此异常对应的@Recover方法。
异常类型需要与Recover方法参数类型保持一致,
recover方法返回值需要与重试方法返回值保证一致
测试
import com.scaffolding.example.sys.service.TestRetryService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class ExampleApplicationTest {
@Autowired
private TestRetryService retryService;
@Test
public void test() throws Exception {
retryService.testRetry(0);
}
}
运行结果:
2022-04-26 17:32:41.890 INFO c.s.e.s.s.impl.TestRetryServiceImpl [28][main] testRetry, 时间: 2022-04-26T17:32:41.889
2022-04-26 17:32:43.895 INFO c.s.e.s.s.impl.TestRetryServiceImpl [28][main] testRetry, 时间: 2022-04-26T17:32:43.895
2022-04-26 17:32:46.900 INFO c.s.e.s.s.impl.TestRetryServiceImpl [28][main] testRetry, 时间: 2022-04-26T17:32:46.900
重试次数结束后还有异常,回调方法开始执行!
注意 ⚠️
retry 重试机制无效:因为 retry 用到啦 aspect 增强,所以如果在同一类方法中内部调用,会使 aspect 增强实效,自然 retry 也随之实效。
代码如下:
public class Test {
public void A() {
B();
}
@Retryable(Exception.class)
public void B() {
throw new RuntimeException("retry...");
}
}
评论区