当前位置: > > > SpringBoot - 网络请求客户端WebClient使用详解7(超时时长、自动重试)

SpringBoot - 网络请求客户端WebClient使用详解7(超时时长、自动重试)

八、设置超时属性

(1)我们可以使用 timeout 方法设置一个超时时长。如果 HTTP 请求超时,便会发生 TimeoutException 异常。
@RestController
public class HelloController {

    // 创建 WebClient 对象
    private WebClient webClient = WebClient.builder()
            .baseUrl("http://localhost:8080")
            .build();

    @GetMapping("/test")
    public String test() {
        Mono<String> mono = webClient
                .get() // GET 请求
                .uri("/data")  // 请求一个不存在的路径
                .retrieve() // 获取响应体
                .bodyToMono(String.class)
                .timeout(Duration.ofSeconds(3)); // 3秒超时

        return mono.block();
    }

    @GetMapping("/data")
    public String data() throws InterruptedException {
        //等待2分钟再返回
        Thread.sleep(120 * 1000);
        return "hangge.com";
    }
}

(2)使用 Postman 发起请求结果如下:

九、请求异常自动重试

1,设置重试次数

(1)使用 retry() 方法可以设置当请求异常时的最大重试次数,如果不带参数则表示无限重试,直至成功。
@RestController
public class HelloController {

    // 创建 WebClient 对象
    private WebClient webClient = WebClient.builder()
            .baseUrl("http://localhost:8080")
            .build();

    @GetMapping("/test")
    public String test() {
        Mono<String> mono = webClient
                .get() // GET 请求
                .uri("/data")  // 请求一个不存在的路径
                .retrieve() // 获取响应体
                .bodyToMono(String.class)
                .timeout(Duration.ofSeconds(3)) // 3秒超时
                .retry(3); // 重试3次

        return mono.block();
    }

    @GetMapping("/data")
    public String data() throws InterruptedException {
        System.out.println("--- start ---");
        //等待2分钟再返回
        Thread.sleep(120 * 1000);
        return "hangge.com";
    }
}

(2)执行后控制台输出如下,可以看到最开始的 1 次请求加上 3 次重试,最终一共请求了 4 次。

2,设置重试时间间隔

(1)使用 retry 方法默认情况下请求失败后会立刻重新发起请求,如果希望在每次重试前加个时间间隔(等待),可以使用 retryBackoff 方法。
(2)下面代码同样是当发生请求失败后自动重试 3 次,只不过重试前会等待个 10 秒。
@RestController
public class HelloController {

    // 创建 WebClient 对象
    private WebClient webClient = WebClient.builder()
            .baseUrl("http://localhost:8080")
            .build();

    @GetMapping("/test")
    public String test() {
        Mono<String> mono = webClient
                .get() // GET 请求
                .uri("/data")  // 请求一个不存在的路径
                .retrieve() // 获取响应体
                .bodyToMono(String.class)
                .timeout(Duration.ofSeconds(3)) // 3秒超时
                .retryBackoff(3, Duration.ofSeconds(10)); // 重试3次,间隔10秒

        return mono.block();
    }

    @GetMapping("/data")
    public String data() throws InterruptedException {
        System.out.println("--- start ---");
        //等待2分钟再返回
        Thread.sleep(120 * 1000);
        return "hangge.com";
    }
}

3,指定需要重试的异常

(1)不管是前面的 retry 方法还是 retryBackoff 方法,设置后无论发生何种异常都会进行重试。如果需要更加精细的控制,比如指定的异常才需要重试,则可以使用 retryWhen 方法。
(2)在使用 retryWhen 方法之前,我们项目中还需要先引入 reactor-extra 包,因为需要用到里面的 Retry 类。
<dependency>
    <groupId>io.projectreactor.addons</groupId>
    <artifactId>reactor-extra</artifactId>
</dependency>

(3)下面样例只有发生 RuntimeException 异常时才会进行重试:
注意:如果还需要设置对应的重试次数和间隔时间,需要分别通过 RetryretryMaxbackoff 方法进行设置。
@RestController
public class HelloController {

    // 创建 WebClient 对象
    private WebClient webClient = WebClient.builder()
            .baseUrl("http://localhost:8080")
            .build();

    @GetMapping("/test")
    public String test() {

        // 定义重试条件
        Retry<?> retry = Retry.anyOf(RuntimeException.class) // 只有RuntimeException异常才重试
                .retryMax(3) // 重试3次
                .backoff(Backoff.fixed(Duration.ofSeconds(10))); // 每次重试间隔10秒

        Mono<String> mono = webClient
                .get() // GET 请求
                .uri("/data")  // 请求一个不存在的路径
                .retrieve() // 获取响应体
                .bodyToMono(String.class)
                .timeout(Duration.ofSeconds(3)) // 3秒超时
                .retryWhen(retry);
        
        return mono.block();
    }

    @GetMapping("/data")
    public String data() {
        System.out.println("--- start ---");
        throw new RuntimeException("发生错误");
    }
}

(4)下面样例除了 RuntimeException 异常外,发生其它一切异常都会进行重试:
@RestController
public class HelloController {

    // 创建 WebClient 对象
    private WebClient webClient = WebClient.builder()
            .baseUrl("http://localhost:8080")
            .build();

    @GetMapping("/test")
    public String test() {

        // 定义重试条件
        Retry<?> retry = Retry.allBut(RuntimeException.class) // 除了RuntimeException异常都重试
                .retryMax(3) // 重试3次
                .backoff(Backoff.fixed(Duration.ofSeconds(10))); // 每次重试间隔10秒

        Mono<String> mono = webClient
                .get() // GET 请求
                .uri("/data")  // 请求一个不存在的路径
                .retrieve() // 获取响应体
                .bodyToMono(String.class)
                .timeout(Duration.ofSeconds(3)) // 3秒超时
                .retryWhen(retry);

        return mono.block();
    }

    @GetMapping("/data")
    public String data() {
        System.out.println("--- start ---");
        throw new RuntimeException("发生错误");
    }
}
评论0