返回 导航

SpringBoot / Cloud

hangge.com

SpringBoot - 网络请求模版类RestTemplate使用详解8(自动重试机制)

作者:hangge | 2019-11-02 08:10
    本文是对前一篇文章关于请求异常处理(点击查看)的补充。有时当我们调用一个接口可能由于网络等原因造成第一次请求失败,如果再去尝试可能就成功了,这就是重试机制。下面演示如何结合 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 配置类中进行配置。目的是让 4XX5XX 这样的请求也能成功返回到客户端。具体代码参考之前的文章:

(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";
        ResponseEntity<String> responseEntity = 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 ResponseEntity<String> throwRestException(RestClientException restClientException){
        return new ResponseEntity<String>(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";
        ResponseEntity<String> responseEntity = restTemplate.getForEntity(url, String.class);
        // 判断请求是否发生异常
        if(!responseEntity.getStatusCode().is2xxSuccessful()){
            System.out.println("请求失败...");
            // 抛出异常
            throw new RestClientException(responseEntity.getBody());
        }
        // 没有异常的话则返回正常的响应结果
        return responseEntity.getBody();
    }
}
评论

全部评论(0)

回到顶部