我有一个使用 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);
}
使用 RestTemplate 呈现数据流
- Controller 接收客户端调用
- 提供者获取字符串格式的数据
- Provider 处理字符串
- 数据交给控制者
Controller .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;
}
提供者.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
@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);
}
最佳答案
由于存在很多误解,所以在这里我要澄清一些事情。
Spring 已经正式声明 RestTemplate
处于维护模式
所以如果可以的话,如果你想成为 future 的证明,请使用 WebClient
可能。
如 RestTemplate API 中所述
NOTE: As of 5.0 this class is in maintenance mode, with only minor requests for changes and bugs to be accepted going forward. Please, consider using the
org.springframework.web.reactive.client.WebClient
which has a more modern API and supports sync, async, and streaming scenarios.
非 react 性应用
如果您的应用程序是非 react 性应用程序(不向调用客户端返回通量或单声道),您必须做的是在需要该值时使用 block()
。您当然可以在您的应用程序内部使用 Mono
或 Flux
但最后您必须调用 block()
来获取您想要的具体值需要返回调用客户端。
非响应式应用程序使用例如 tomcat
、undertow
作为底层服务器实现,它遵循 servlet 规范,因此它将为每个请求分配 1 个线程,因此您不会获得使用响应式(Reactive)应用程序获得的性能提升。
响应式应用
另一方面,如果您有一个响应式(Reactive)应用程序,则在任何情况下都不应在您的应用程序中调用 block()
。阻塞正是它所说的,它会阻塞一个线程并阻塞该线程的执行直到它可以继续,这在 react 世界中是不好的。
您也不应该在您的应用程序中调用订阅
,除非您的应用程序是响应的最终消费者。例如,如果您正在调用一个 api 来获取数据并写入您的应用程序连接到的数据库。您的后端应用程序是最终消费者。如果外部客户端正在调用您的后端(例如 react 、角度应用程序、移动客户端等),则外部客户端是最终消费者,并且是订阅者。不是你。
这里的底层默认服务器实现是一个 netty
服务器,它是一个非 servlet、基于事件的服务器,不会为每个请求分配一个线程,服务器本身就是线程不可知论者,任何可用的线程都将在任何请求期间随时处理任何事情。
webflux documentation明确指出 servlet 3.1+ 支持的服务器 tomcat 和 jetty 可以与 webflux 以及非 serlet 服务器 netty 和 undertow 一起使用。
我怎么知道我有什么应用程序?
Spring 声明,如果类路径中同时有 spring-web
和 spring-webflux
,应用程序将优先使用 spring-web
和默认启动一个带有底层 tomcat 服务器的非 react 性应用程序。
如果需要作为 spring 状态,可以手动覆盖此行为。
Adding both
spring-boot-starter-web
andspring-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 addspring-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 toSpringApplication.setWebApplicationType(WebApplicationType.REACTIVE)
.
The “Spring WebFlux Framework”
那么如何根据题目提供的代码实现WebClient呢?
@Retryable(maxAttempts = 4,
value = java.net.ConnectException.class,
backoff = @Backoff(delay = 3000, multiplier = 2))
public Mono<String> getResponse(String url) {
return webClient.get()
.uri(url)
.exchange()
.flatMap(response -> response.toEntity(String.class));
}
我会说这是最简单、干扰最少的实现。你当然需要在 @Bean
中构建一个合适的 webclient 并将其 Autowiring 到它的类中。
关于java - Spring 启动 : How to use WebClient instead of RestTemplate for Performing Non blocking and Asynchronous calls,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57355725/