使用springboot的Retry重试机制

  1. 项目中很多时候会用到重试,比如在网络出现异常的时候调用第三方接口就会出现异常,在这个时候有个可以自动重试的方法就非常方便了。而springboot的Retryable就跟好的解决了这个问题。

  2. 老规矩先上需要导入的包

     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-web</artifactId>
     </dependency>
     <dependency>
      	 <groupId>org.springframework.retry</groupId>
         <artifactId>spring-retry</artifactId>
     </dependency>
     <dependency>
         <groupId>org.aspectj</groupId>
         <artifactId>aspectjweaver</artifactId>
     </dependency>	 
    
  3. 在项目的启动类上加上一个开始重试的注解@EnableRetry

    @EnableRetry
    @SpringBootApplication
    public class App {
        public static void main(String[] args) {
            SpringApplication.run(App.class,args);
        }
    }
    
  4. 创建一个测试的controller和service以及service的实现类

    @Api(tags = "测试")
    @RestController
    @RequestMapping("/test")
    public class TestController {
        @Autowired
        private TestServices testServices;
        
        @ApiOperation(value = "自动重试")
        @GetMapping("/runRetry")
        public Result<String> runRetry(){
            testServices.saveVideo(new Test());
            return Result.success();
        	}
    }
    
    public interface TestServices {
        void saveVideo(Test test);
    }
    
    @Slf4j
    @Service
    public class TestServicesImpl implements TestServices {
    
        //指定使用自定义名为taskExecutor的线程池来执行该任务,如果没有就不填使用springboot自带的 
        @Async("taskExecutor")
        @Retryable(value = Exception.class,maxAttempts = 4,backoff = @Backoff(delay = 4000L,multiplier = 2))
        @Override
        public void saveVideo(Test test) {
            String startTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(System.currentTimeMillis()));
            log.info("任务开始:{}",startTime);
            test.getStatus();
            String endTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(System.currentTimeMillis()));
            log.info("任务结束:{}",endTime);
        }
    
        @Recover
        public void recover() {
            log.error("任务结束出现异常:连接超时");
        }
    }
    
    public class Test {
        public boolean getStatus(){
            RestTemplate restTemplate=new RestTemplate();
            restTemplate.getForObject("192.168.6.100",String.class);
            return false;
        }
    }
    
  5. 重点解释一下@Retryable的参数
    value:代发触发的条件,值得是拦截捕获的异常。
    maxAttempts:重试的最大次数。
    delay:每次重试的时间间隔,单位是毫秒。
    multiplier:是重试间隔的增长系数,比如时间间隔为4秒 系数为2,那么第二次的重试的间隔就是4秒,第三次就是8秒,第四次就是16秒.
    @Recover 注解注释的方法 就是当重试次数全部完成依然还为解决,就会执行@Recover修饰的方法

  6. 看一下控制台的打印
    使用springboot的Retry重试机制

  7. 可以看到控制台打印的时间间隔,刚好就是相隔4.8.16秒。最后也走了recover方法。

  8. 最后说几个重点也是我自己给自己挖的坑
    1.逻辑代码最好不要用try catch捕获异常,即使捕获了记得要再次抛出。

     try {
    	 	String startTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(System.currentTimeMillis()));
            log.info("任务开始:{}",startTime);
            test.getStatus();
            String endTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(System.currentTimeMillis()));
            log.info("任务结束:{}",endTime);
        } catch (IllegalArgumentException Ie){
            log.info("restTemplate出现异常 任务结束出现异常:{}",Ie.getMessage());
            Ie.printStackTrace();
        } catch (IORuntimeException Io){
            log.info("IORuntimeException出现异常 任务结束出现异常:{}",Io.getMessage());
            Io.printStackTrace();
        } catch (Exception e) {
            log.info("任务结束出现异常:{}",e.getMessage());
            e.printStackTrace();
        }
    

    2.这是我最之前写的,因为把异常捕获了,所以就一直触发不了@Retryable。找了很久的原因才找到。真的是自己坑自己。
    3.使用@Retryable修饰方法的类,一定要通过@Autowired注入进去,不能手动的new,目的就是为了加入spring boot的管理中,否则就是无效。

  9. 本次分享就到这里。如有理解错误的地方欢迎指出讨论。

上一篇:python 错误重试


下一篇:精讲RestTemplate第8篇-请求失败自动重试机制