为什么 WebFlux-WebClient 超时不起作用?

pri*_*pri 2 java netty spring-boot spring-webflux spring-webclient

我正在使用 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>
Run Code Online (Sandbox Code Playgroud)

代码如下所示:

@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();
    }
}
Run Code Online (Sandbox Code Playgroud)
  1. 我配置的超时似乎没有效果。
  2. 为什么这总是需要 20 秒才能响应。

请建议我遗漏或做错了什么。

Mic*_*rry 5

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

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

  • ReadTimeoutHandler当在给定的时间窗口内没有读取数据时,您将被触发。因此,如果在该 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
Run Code Online (Sandbox Code Playgroud)