本文是对前一篇文章关于请求异常处理(点击查看)的补充。有时当我们调用一个接口可能由于网络等原因造成第一次请求失败,如果再去尝试可能就成功了,这就是重试机制。下面演示如何结合 Spring Retry 实现请求发生异常时自动进行重试(重新发起请求)。
十一、请求异常自动重试
1,安装配置
(1)编辑项目 pom.xml 文件,添加 Spring Retry 相关依赖。
<!-- 重试机制 --> <dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> <version>1.1.2.RELEASE</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> </dependency>
(2)在主类上加入 @EnableRetry 注解,启用重试功能。
@SpringBootApplication @EnableRetry public class DemoApplication { public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(DemoApplication.class, args); } }
2,使用样例
(1)由于这是前一篇文章关于请求异常处理的补充,首先我同样要创建一个自己的异常处理控制器(RestThrowErrorHandler)并在 RestTemplate 配置类中进行配置。目的是让 4XX、5XX 这样的请求也能成功返回到客户端。具体代码参考之前的文章:
(2)首先修改前文的 Service 类,在需要重试的方法上添加 @Retryable 和 @Backoff 注解,使其在发生异常时能够自动重试。
(1)@Retryable 注解的方法在发生异常时会重试,参数说明:
- value:当指定异常发生时会进行重试
- include:和 value 一样,默认空。如果 exclude 也为空时,所有异常都重试
- exclude:指定异常不重试,默认空。如果 include 也为空时,所有异常都重试
- maxAttemps:最大重试次数,默认 3
- backoff:重试等待策略,默认没有
(2)@Backoff 注解为重试等待策略,参数说明:
- delay:指定重试的延时时间,默认为 1000L
- multiplier:指定延迟的倍数,默认为 0。比如 delay=5000l,multiplier=2 时,第一次重试为 5 秒后,第二次为 10 秒,第三次为 20 秒。
@Service public class UserService { @Autowired private RestTemplate restTemplate; @Retryable(value = RestClientException.class, maxAttempts = 3, backoff = @Backoff(delay = 5000l,multiplier = 1)) public String getInfo() { String url = "http://localhost:8080/xxxxxx"; ResponseEntityresponseEntity = restTemplate.getForEntity(url, String.class); // 判断请求是否发生异常 if(!responseEntity.getStatusCode().is2xxSuccessful()){ System.out.println("请求失败..."); // 抛出异常 throw new RestClientException(responseEntity.getBody()); } // 没有异常的话则返回正常的响应结果 return responseEntity.getBody(); } }
(2)然后 Contoller 会调用这个 Service,这边代码同前文一样:
注意:由于 retry 用到了 aspect 增强,所以会有 aspect 的坑,就是方法内部调用,会使 aspect 增强失效,那么 retry 当然也会失效。
- 比如这里重试方法是定义在 Service 类里面,Controller 调用 Service 的这个方法,重试机制是没问题的。
- 但如果重试方法直接定义在这个 Controller 里面,也就同一个类里面内部调用,那么重试机制就会失效。
@RestController public class HelloController { @Autowired private UserService userService; @GetMapping("/test") public String test() { return userService.getInfo(); } }
(3)全局的异常处理类和前文一样,当超过重试次数是异常会被抛出,这个全局的异常处理类会捕获这个异常,并返回给前端处理的结果。
@ControllerAdvice public class CustomExceptionHandler { @ExceptionHandler(RestClientException.class) public ResponseEntitythrowRestException(RestClientException restClientException){ return new ResponseEntity (restClientException.getMessage(), HttpStatus.BAD_REQUEST); } }
(4)测试一下,由于我们使用 RestTemplate 请求一个不存在的接口,可以看到 UserService 方法重复执行3次(每次间隔5秒)。
(5)超过重试次数后异常信息才返回到前端页面。
附:同时指定多个异常
@Retryable 注解的 value 属性可以同时设置多个异常类型,只要其中某个异常发生时,被注解的方法就会进行重试。
@Service public class UserService { @Autowired RestTemplate restTemplate; @Retryable(value = {RestClientException.class, ConnectException.class}, maxAttempts = 3, backoff = @Backoff(delay = 5000l,multiplier = 1)) public String getInfo() { String url = "http://localhost:8080/xxxxxx"; ResponseEntityresponseEntity = restTemplate.getForEntity(url, String.class); // 判断请求是否发生异常 if(!responseEntity.getStatusCode().is2xxSuccessful()){ System.out.println("请求失败..."); // 抛出异常 throw new RestClientException(responseEntity.getBody()); } // 没有异常的话则返回正常的响应结果 return responseEntity.getBody(); } }