Springboot:如何使用 WebClient 而不是 RestTemplate 来执行非阻塞和异步调用 [英] Springboot : How to use WebClient instead of RestTemplate for Performing Non blocking and Asynchronous calls

查看:70
本文介绍了Springboot:如何使用 WebClient 而不是 RestTemplate 来执行非阻塞和异步调用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a springboot project which uses Springboot Resttemplate. We have moved to springboot 2.0.1 from 1.5.3 and we are trying to make the rest calls from it asynchronous by using WebClient. We used to process the string received using Resttemplate as given below. But WebClient returns only data in Mono or Flux. How can I get the data as String. Already tried block() method , but it does asynchronous calls.

@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);
}

使用 RestTemplate 呈现数据流

Present Data flow with RestTemplate

  1. 控制器接收客户端调用
  2. provider 获取String格式的数据
  3. Provider 处理字符串
  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;
}

Provider.java

Provider.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;
}

DataFetcher.java

DataFetcher.java

@Retryable(maxAttempts = 4,
           value = java.net.ConnectException.class,
           backoff = @Backoff(delay = 3000, multiplier = 2))
public ResponseEntity<String> getResponse(String url) {
    /* ----------------------- */
    return getRestTemplate().getForEntity(urlForCurrent, String.class);
}

推荐答案

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

Due to the fact that there are lot of misconception, so here I'm going to clear up some things.

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

Spring has officially stated that they will deprecate RestTemplate in the future so if you can, use WebClient if you want to be as future proof as possible.

中所述RestTemplate API

注意:从 5.0 开始,非阻塞、反应式 org.springframework.web.reactive.client.WebClient 提供了 RestTemplate 的现代替代方案 有效支持同步和异步,以及流场景.RestTemplate 将在未来版本中弃用,并且不会在未来添加主要的新功能.有关更多详细信息和示例代码,请参阅 Spring Framework 参考文档的 WebClient 部分.

NOTE: As of 5.0, the non-blocking, reactive org.springframework.web.reactive.client.WebClient offers a modern alternative to the RestTemplate with efficient support for both sync and async, as well as streaming scenarios. The RestTemplate will be deprecated in a future version and will not have major new features added going forward. See the WebClient section of the Spring Framework reference documentation for more details and example code.

非响应式应用

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

If your application is a non-reactive application (not returning fluxes or monos to the calling clients) what you have to do is to use block() if you need the value. You can of course use Mono or Flux internally in your application but in the end you must call block() to get the concrete value that you need to return to the calling client.

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

Non reactive applications use tomcat as the underlying server implementation, which is a servlet based server that will assign 1 thread per request so you will not gain the performance gains you get with a reactive application.

反应式应用

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

If you on the other hand you have a reactive application you should never under any circumstances ever call block() in your application. Blocking is exactly what it says, it will block a thread and block that threads execution until it can move on, this is bad in a reactive world.

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

You should also not call subscribe in your application unless your application is the final consumer of the response. For instance, if you are calling an api to get data and write into a database that your application is connected to. Your backend application is the final consumer. If an external client is calling your backend (for instance an react, angular app, mobile client, etc. etc.) the external client is the final consumer, and is the one subscribing. Not you.

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

Underlying default server implementation here is a netty server which is a non servlet, event based server that will not assign one thread to each request, the server itself is thread agnostic and any thread available will handle anything at any time during any request.

webflux文档明确指出 servlet 3.1+ 支持的服务器 tomcat 和 jetty 可以与 webflux 以及非 serlet 服务器 netty 和 undertow 一起使用.

The webflux documentation clearly states that both servlet 3.1+ supported servers tomcat and jetty can be used with webflux as well as non-serlet servers netty and undertow.

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

Spring 声明,如果您在类路径上同时具有 spring-webspring-webflux,应用程序将支持 spring-web 和默认情况下启动一个带有底层 tomcat 服务器的非响应式应用程序.

Spring states that if you have both spring-web and spring-webflux on the classpath, the application will favor spring-web and per default start up a non-reactive application with an underlying tomcat server.

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

This behaviour can manually be overridden if needed as spring states.

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

Adding both spring-boot-starter-web and spring-boot-starter-webflux modules in your application results in Spring Boot auto-configuring Spring MVC, not WebFlux. This behavior has been chosen because many Spring developers add spring-boot-starter-webflux to their Spring MVC application to use the reactive WebClient. You can still enforce your choice by setting the chosen application type to 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();
}

我想说这是最简单、侵入性最小的实现.您当然需要在 @Bean 中构建一个合适的 web 客户端并将其自动装配到它的类中.

I would say this is the easiest and the most less intrusive implementation. You of course need to build a proper webclient in maybe a @Bean and autowire it into its class.

这篇关于Springboot:如何使用 WebClient 而不是 RestTemplate 来执行非阻塞和异步调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆