目录
  • 1 spring-retry是什么?
  • 2 使用步骤
    • 2.1 引入maven库
    • 2.2 在spring启动类上开启重试功能
    • 2.3 公共业务代码
    • 2.4 传统的重试做法
    • 2.5 使用spring-retry的命令式编码
      • 2.5.1 定义重试监听器
      • 2.5.2 定义重试配置
      • 2.5.3 命令式编码
    • 2.6使用spring-retry的注解式编码
    • 3 SpringBoot整合spring-retry
      • 3.1 添加@EnableRetry注解
        • 3.2 接口实现
          • 3.3 添加@Retryable注解
            • 3.4 Controller测试代码
              • 3.5 发送请求
                • 3.6 补充:@Recover

                1 spring-retry是什么?

                以往我们在进行网络请求的时候,需要考虑网络异常的情况,本文就介绍了利用spring-retry,是spring提供的一个重试框架,原本自己实现的重试机制,现在spring帮封装好提供更加好的编码体验。

                2 使用步骤

                2.1 引入maven库

                代码如下(示例):

                	<dependency>
                	    <groupId>org.springframework.retry</groupId>
                	    <artifactId>spring-retry</artifactId>
                	</dependency>
                	<dependency>
                	    <groupId>org.springframework</groupId>
                	    <artifactId>spring-aspects</artifactId>
                	    <version>4.3.9.RELEASE</version>
                	</dependency>

                2.2 在spring启动类上开启重试功能

                提示:添加@EnableRetry注解开启
                @SpringBootApplication
                @EnableRetry
                public class SpringRetryDemoApplication {
                    public static SpringRetryAnnotationService springRetryAnnotationService;
                    public static SpringRetryImperativeService springRetryImperativeService;
                    public static TranditionalRetryService tranditionalRetryService;
                    @Autowired
                    public void setSpringRetryAnnotationService(SpringRetryAnnotationService springRetryAnnotationService) {
                        SpringRetryDemoApplication.springRetryAnnotationService = springRetryAnnotationService;
                    }
                    @Autowired
                    public void setSpringRetryImperativeService(SpringRetryImperativeService springRetryImperativeService) {
                        SpringRetryDemoApplication.springRetryImperativeService = springRetryImperativeService;
                    }
                    @Autowired
                    public void setTranditionalRetryService(TranditionalRetryService tranditionalRetryService) {
                        SpringRetryDemoApplication.tranditionalRetryService = tranditionalRetryService;
                    }
                    public static void main(String[] args) {
                        SpringApplication.run(SpringRetryDemoApplication.class, args);
                        springRetryAnnotationService.test();
                        springRetryImperativeService.test();
                        tranditionalRetryService.test();
                    }
                }

                2.3 公共业务代码

                @Service
                @Slf4j
                public class CommonService {
                    public void test(String before) {
                        for (int i = 0; i < 10; i++) {
                            if (i == 2) {
                                log.error("{}:有异常哦,我再试多几次看下还有没异常", before);
                                throw new RuntimeException();
                            }
                            log.info("{}:打印次数: {}", before, i + 1);
                        }
                    }
                
                    public void recover(String before) {
                        log.error("{}:还是有异常,程序有bug哦", before);
                    }
                }
                

                2.4 传统的重试做法

                @Service
                @Slf4j
                public class TranditionalRetryService {
                    @Autowired
                    CommonService commonService;
                
                    public void test() {
                        // 定义重试次数以及重试时间间隔
                        int retryCount = 3;
                        int retryTimeInterval = 3;
                        for (int r = 0; r < retryCount; r++) {
                            try {
                                commonService.test("以前的做法");
                            } catch (RuntimeException e) {
                                if (r == retryCount - 1) {
                                    commonService.recover("以前的做法");
                                    return;
                                }
                                try {
                                    Thread.sleep(retryTimeInterval * 1000);
                                } catch (InterruptedException ex) {
                                    ex.printStackTrace();
                                }
                            }
                
                        }
                
                    }
                }
                

                2.5 使用spring-retry的命令式编码

                2.5.1 定义重试监听器

                提示:监听重试过程的生命周期
                @Slf4j
                public class MyRetryListener extends RetryListenerSupport {
                    @Override
                    public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
                        log.info("监听到重试过程关闭了");
                        log.info("=======================================================================");
                    }
                
                    @Override
                    public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
                        log.info("监听到重试过程错误了");
                    }
                
                    @Override
                    public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
                        log.info("=======================================================================");
                        log.info("监听到重试过程开启了");
                        return true;
                    }
                }

                2.5.2 定义重试配置

                提示:配置RetryTemplate重试模板类
                @Configuration
                public class RetryConfig {
                    @Bean
                    public RetryTemplate retryTemplate() {
                        // 定义简易重试策略,最大重试次数为3次,重试间隔为3s
                        RetryTemplate retryTemplate = RetryTemplate.builder()
                                .maxAttempts(3)
                                .fixedBackoff(3000)
                                .retryOn(RuntimeException.class)
                                .build();
                        retryTemplate.registerListener(new MyRetryListener());
                        return retryTemplate;
                    }
                }

                2.5.3 命令式编码

                @Service
                @Slf4j
                public class SpringRetryImperativeService {
                    @Autowired
                    RetryTemplate retryTemplate;
                    @Autowired
                    CommonService commonService;
                
                    public void test() {
                        retryTemplate.execute(
                                retry -> {
                                    commonService.test("命令式");
                                    return null;
                                },
                                recovery -> {
                                    commonService.recover("命令式");
                                    return null;
                                }
                        );
                    }
                }

                2.6使用spring-retry的注解式编码

                @Service
                @Slf4j
                public class SpringRetryAnnotationService {
                    @Autowired
                    CommonService commonService;
                
                    /**
                     * 如果失败,定义重试3次,重试间隔为3s,指定恢复名称,指定监听器
                     */
                    @Retryable(value = RuntimeException.class, maxAttempts = 3, backoff = @Backoff(value = 3000L), recover = "testRecover", listeners = {"myRetryListener"})
                    public void test() {
                        commonService.test("注解式");
                    }
                
                    @Recover
                    public void testRecover(RuntimeException runtimeException) {
                        commonService.recover("注解式");
                    }
                }
                

                3 SpringBoot整合spring-retry

                我们使用SpringBoot来整合spring-retry组件实现重试机制。

                3.1 添加@EnableRetry注解

                在主启动类Application上添加@EnableRetry注解,实现对重试机制的支持

                @SpringBootApplication
                @EnableRetry
                public class RetryApplication {
                 
                    public static void main(String[] args) {
                 
                        SpringApplication.run(RetryApplication.class, args);
                    }
                 
                }
                

                注意:@EnableRetry也可以使用在配置类、ServiceImpl类、方法上

                3.2 接口实现

                注意:接口类一定不能少,在接口类中定义你需要实现重试的方法,否则可能会无法实现重试功能

                我的测试接口类如下:

                public interface RetryService {    public String testRetry()  throws Exception;}
                

                3.3 添加@Retryable注解

                我们针对需要实现重试的方法上添加@Retryable注解,使该方法可以实现重试,这里我列出ServiceImpl中的一个方法:

                @Service
                public class RetryServiceImpl implements RetryService {
                    @Override
                    @Retryable(value = Exception.class, maxAttempts = 3, backoff = @Backoff(delay = 2000,multiplier = 1.5))
                    public String testRetry() throws Exception {
                 
                        System.out.println("开始执行代码:"+ LocalTime.now());
                        int code = 0;
                        // 模拟一直失败
                        if(code == 0){
                           // 这里可以使自定义异常,@Retryable中value需与其一致
                            throw new Exception("代码执行异常");
                        }
                        System.out.println("代码执行成功");
                        return "success";
                    }
                }
                

                说明:@Retryable配置元数据情况:
                value :针对指定抛出的异常类型,进行重试,这里指定的是Exception
                maxAttempts :配置最大重试次数,这里配置为3次(包含第一次和最后一次)
                delay: 第一次重试延迟间隔,这里配置的是2s
                multiplier :每次重试时间间隔是前一次几倍,这里是1.5倍

                3.4 Controller测试代码

                @RestController
                @RequestMapping("/test")
                public class TestController {
                    //  一定要注入接口,通过接口去调用方法
                    @Autowired
                    private RetryService retryService;
                 
                    @GetMapping("/retry")
                    public String testRetry() throws Exception {
                        return retryService.testRetry();
                    }
                }
                

                3.5 发送请求

                发送请求后,我们发现后台打印情况,确实重试了3次,并且在最后一次重试失败的情况下,才抛出异常,具体如下(可以注意下时间间隔)

                3.6 补充:@Recover

                一般情况下,我们重试最大设置的次数后,仍然失败抛出异常,我们会通过全局异常处理类进行统一处理,但是我们其实也可以自行处理,可以通过@Recover注解来实现,具体如下:

                @Service
                public class RetryServiceImpl implements RetryService {
                 
                    @Retryable(value = Exception.class, maxAttempts = 3, backoff = @Backoff(delay = 2000,multiplier = 1.5))
                    public String testRetry() throws Exception {
                 
                        System.out.println("开始执行代码:"+ LocalTime.now());
                        int code = 0;
                        if(code == 0){
                            // 这里可以使自定义异常,@Retryable中value需与其一致
                            throw new Exception("代码执行异常");
                        }
                        System.out.println("代码执行成功");
                        return "success";
                    }
                 
                    /**
                     * 最终重试失败处理
                     * @param e
                     * @return
                     */
                    @Recover
                    public String recover(Exception e){
                 
                        System.out.println("代码执行重试后依旧失败");
                        return "fail";
                    }
                }
                

                注意:
                1)@Recover的方法中的参数异常类型需要与重试方法中一致
                2)该方法的返回值类型与重试方法保持一致

                再次测试如下(发现不会再抛出异常)

                声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。