为啥 WebFlux-WebClient 超时不起作用?

Posted

技术标签:

【中文标题】为啥 WebFlux-WebClient 超时不起作用?【英文标题】:Why WebFlux-WebClient Timeout not working?为什么 WebFlux-WebClient 超时不起作用? 【发布时间】:2021-07-07 02:07:26 【问题描述】:

我正在使用 Spring boot Webflux 2.4.4(最新)并尝试使用 WebClient 调用后端 URL。 WebClient 总是在 20 秒以上响应。如果我直接点击 URL,它会在毫秒内响应。

Pom 如下所示:

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

代码如下:

@RestController
public class Controller 

    @GetMapping("/test")
    public Mono<String> test() 

        long startMillis = System.currentTimeMillis();
        return webClient().get().uri("https://www.google.com").exchangeToMono(response -> 
            System.out.println("Elapsed Time is:  " + (System.currentTimeMillis() - startMillis));
            if (response.statusCode().equals(HttpStatus.OK)) 
                return Mono.just("OK");
            
            return Mono.just("OK");
        );
    

    private WebClient webClient() 
        return WebClient.builder()
                .clientConnector(new ReactorClientHttpConnector(HttpClient
                        .create().secure().responseTimeout(Duration.ofMillis(1000))
                        .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000).doOnConnected(c -> c
                                .addHandlerLast(new ReadTimeoutHandler(3)).addHandlerLast(new WriteTimeoutHandler(3)))))
                .build();
    

    我配置的超时似乎没有效果。 为什么总是需要 20 秒才能响应。

请提出我遗漏或做错了什么。

【问题讨论】:

【参考方案1】:

我配置的超时似乎没有效果。

您在这里设置了各种任意超时,所有这些都做不同的事情:

您的ReadTimeoutHandler 会在给定时间窗口内读取no 数据时触发。因此,如果 任何 数据在这 3 秒的窗口中仍然被读取,无论多么慢,它都不会触发。 WriteTimeoutHandler 为完成写入提供了一定的时间窗口 - 但除非您 发送 的基础请求(不等待响应)花费的时间超过该时间,否则这也不会出错. CONNECT_TIMEOUT_MILLIS 是在发送任何数据之前实际建立 TCP 连接所需的时间。同样,除非花费超过 3 秒,否则它不会跳闸。 responseTimeout()在发送请求后读取完整响应所需的时间。请注意,这不包括发送请求之前可能需要的任何 DNS 解析、预热时间等。

我不知道你为什么要花 20 多秒的时间 - 这似乎太长了,整个请求在我的中等功率机器上执行,我的平均互联网连接最多几百毫秒。造成这种情况的原因可能是机器速度非常慢、互联网连接速度慢、DNS 服务器速度慢、网络速度慢、代理速度慢等等——但这不是你那里的代码的问题。

如果您希望 整个 链执行超时,包括它可能需要的任何后台请求、预热时间等 - 那么 AFAIK 实现这一点的唯一方法是单声道本身 -所以你的请求就变成了:

return webClient()
    .get()
    .uri("https://www.google.com")
    .exchangeToMono(
        response -> 
          System.out.println("Elapsed Time is:  " + (System.currentTimeMillis() - startMillis));
          if (response.statusCode().equals(HttpStatus.OK)) 
            return Mono.just("OK");
          
          return Mono.just("OK");
        )
    .timeout(Duration.ofMillis(500)); //Timeout specified here

【讨论】:

谢谢。当我在另一台机器上尝试时,它按预期工作。

以上是关于为啥 WebFlux-WebClient 超时不起作用?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 WCF 不支持服务端超时?

java项目页面为啥会超时?

Grafana 警报执行超时,为啥会这样?

为啥 gen_server 超时

请教ThinkPHP为啥会这么容易数据库查询超时

Hystrix 默认超时不起作用