Springboot:如何使用 WebClient 而不是 RestTemplate 来执行非阻塞和异步调用

Abh*_*bhi 9 java spring spring-boot spring-webflux

我有一个使用 Springboot Resttemplate 的 springboot 项目。我们已经从 1.5.3 迁移到 springboot 2.0.1,我们正在尝试使用 WebClient 使其余的调用异步进行。我们曾经使用 Resttemplate 处理收到的字符串,如下所示。但是 WebClient 只返回 Mono 或 Flux 中的数据。如何将数据作为字符串获取。已经尝试了 block() 方法,但它执行异步调用。

@Retryable(maxAttempts = 4, value = java.net.ConnectException.class,
           backoff = @Backoff(delay = 3000, multiplier = 2))
public Mono<String> getResponse(String url) {
    return webClient.get().uri(urlForCurrent).accept(APPLICATION_JSON)
                    .retrieve()
                    .bodyToMono(String.class);
}
Run Code Online (Sandbox Code Playgroud)

使用 RestTemplate 呈现数据流

  1. 控制器接收客户端调用
  2. provider 获取 String 格式的数据
  3. 提供程序处理字符串
  4. 数据提供给控制器

控制器.java

@RequestMapping(value = traffic/, method = RequestMethod.GET,
                produces = MediaType.APPLICATION_JSON_VALUE)
public String getTraffic(@RequestParam("place") String location) throws InterruptedException, ExecutionException {
    String trafficJSON = Provider.getTrafficJSON(location)
    return trafficJSON;
}
Run Code Online (Sandbox Code Playgroud)

提供者.java

public String getTrafficJSON(String location) {
    String url = ----;

    ResponseEntity<String> response = dataFetcher.getResponse(url);

    /// RESPONSEBODY IS READ AS STRING AND IT NEEDS TO BE PROCESSED
    if (null != response {
        return parser.transformJSON(response.getBody(), params);
    }

    return null;
}
Run Code Online (Sandbox Code Playgroud)

数据获取器

public String getTrafficJSON(String location) {
    String url = ----;

    ResponseEntity<String> response = dataFetcher.getResponse(url);

    /// RESPONSEBODY IS READ AS STRING AND IT NEEDS TO BE PROCESSED
    if (null != response {
        return parser.transformJSON(response.getBody(), params);
    }

    return null;
}
Run Code Online (Sandbox Code Playgroud)

Tho*_*olf 31

由于存在很多误解,所以在这里我要澄清一些事情。

Spring 已正式声明它们将RestTemplate在未来弃用,因此如果可以的话,WebClient如果您想成为未来的证明,请使用它。

RestTemplate API 中所述

注意:从 5.0 开始,非阻塞、反应式org.springframework.web.reactive.client.WebClientRestTemplate同步和异步以及流场景提供了有效支持的现代替代方案。该RestTemplate会在未来的版本中被淘汰,并没有重大的新功能的加入前进。有关WebClient更多详细信息和示例代码,请参阅Spring Framework 参考文档部分。

非反应性应用

如果您的应用程序是非响应式应用程序(不向调用客户端返回通量或单声道),您必须做的是在block()需要该值时使用。您当然可以在应用程序内部使用MonoFlux但最后您必须调用block()以获取需要返回给调用客户端的具体值。

非响应式应用程序tomcat用作底层服务器实现,这是一个基于 servlet 的服务器,将为每个请求分配 1 个线程,因此您不会获得响应式应用程序所获得的性能提升。

反应式应用

另一方面,如果您有一个响应式应用程序,则在任何情况下都不应该调用block()您的应用程序。阻塞正是​​它所说的,它会阻塞一个线程并阻塞该线程的执行,直到它可以继续,这在反应式世界中是很糟糕的。

您也不应该调用subscribe您的应用程序,除非您的应用程序是响应的最终使用者。例如,如果您正在调用 api 来获取数据并写入您的应用程序所连接的数据库。您的后端应用程序是最终消费者。如果外部客户端正在调用您的后端(例如 React、Angular 应用程序、移动客户端等),则外部客户端是最终消费者,并且是订阅者。不是你。

这里的底层默认服务器实现是一个netty服务器,它是一个非 servlet、基于事件的服务器,它不会为每个请求分配一个线程,服务器本身是线程不可知的,任何可用的线程将在任何请求期间的任何时间处理任何事情。

webflux文件明确指出,两者的servlet 3.1+支持的服务器Tomcat和码头可webflux以及非serlet服务器网状和暗流中。

我怎么知道我有什么应用程序?

Spring 指出,如果您在类路径上同时拥有spring-webspring-webflux,则应用程序将支持spring-web并默认启动具有底层 tomcat 服务器的非反应性应用程序。

如果需要作为弹簧状态,可以手动覆盖此行为。

在应用程序中同时添加spring-boot-starter-webspring-boot-starter-webflux模块会导致 Spring Boot 自动配置 Spring MVC,而不是 WebFlux。之所以选择这种行为,是因为许多 Spring 开发人员spring-boot-starter-webflux在他们的 Spring MVC 应用程序中添加了使用响应式 WebClient 的内容。您仍然可以通过将所选应用程序类型设置为 来强制您的选择SpringApplication.setWebApplicationType(WebApplicationType.REACTIVE)

“Spring WebFlux 框架”

那么如何根据问题提供的代码实现WebClient呢?

@Retryable(maxAttempts = 4,
       value = java.net.ConnectException.class,
       backoff = @Backoff(delay = 3000, multiplier = 2))
public ResponseEntity<String> getResponse(String url) {
    return webClient.get()
            .uri(url)
            .exchange()
            .flatMap(response -> response.toEntity(String.class))
            .block();
}
Run Code Online (Sandbox Code Playgroud)

我会说这是最简单和侵入性最小的实现。您当然需要在 a 中构建一个合适的 webclient@Bean并将其自动装配到它的类中。