我正在开发几个 spring-boot 应用程序,它们具有传统的每个请求线程模式。我们使用 Spring-boot-webflux 获取 WebClient 以在应用程序之间执行 RESTful 集成。因此,我们的应用程序设计要求我们在收到响应后立即阻止发布者。
最近,我们一直在讨论在我们的阻塞应用程序设计中是否使用反应式模块不必要地花费资源。据我了解,WebClient 通过分配工作线程来执行事件循环中的反应操作来利用事件循环。因此,使用 webclient.block()
会使原始线程休眠,同时分配另一个线程来执行 http 请求。与替代的 RestTemplate 相比,WebClient 似乎会通过使用事件循环花费额外的资源。
以这种方式部分引入 spring-webflux 会导致额外的资源消耗,同时不会对性能产生任何积极的贡献,无论是单线程还是并发,是否正确?我们并不期望将当前的堆栈升级为完全响应式,因此逐步升级的论点不适用。
java performance spring-boot spring-webflux spring-webclient
我最近开始WebClient
在我的Spring boot项目中使用。有人可以扔之间的差异/用法一些轻exchange
和retrieve
方法WebClient
。
我明白exchange
返回Mono<ClientResponse>
和retrieve
返回ResponseSpec
,我只想知道何时/为什么我应该使用它们中的每一个。
非常感谢。
当响应为 5xx 时,我想在等待 10 秒后重试请求 3 次。但我没有看到可以使用的方法。在对象上
WebClient.builder()
.baseUrl("...").build().post()
.retrieve().bodyToMono(...)
Run Code Online (Sandbox Code Playgroud)
我可以看到方法:
在重试次数但没有延迟的条件下重试
.retry(3, {it is WebClientResponseException && it.statusCode.is5xxServerError} )
Run Code Online (Sandbox Code Playgroud)
重试退避和次数但没有条件
.retryBackoff
Run Code Online (Sandbox Code Playgroud)
还有一个,retryWhen
但我不知道如何使用它
我们正在使用 WebTestClient (SpringBoot) 测试我们的 GraphQL 后端,但在查看测试失败的确切原因时遇到了问题。我们的代码如下所示:
webTestClient
.post().uri(GQLKonstanten.URL)
.body(GQLRequestInserter.from(movieDeleteGQL, variables))
.exchange()
.expectBody()
.jsonPath("$.errors").doesNotExist()
.jsonPath("$.data.movie.id")
.isEqualTo(movie.getId());
Run Code Online (Sandbox Code Playgroud)
我得到的是带有以下消息的堆栈跟踪:
java.lang.AssertionError:JSON 路径“$.data.movie.id”没有值
...
引起原因:com.jayway.jsonpath.PathNotFoundException:路径 $['data']['movie'] 中缺少属性
错误消息是完全正确的。但为了真正查看这个 graphQl Exceution 命令实际返回的内容,我总是将 WebClient 执行更改为:
String responseBody = webTestClient
.post().uri(GQLKonstanten.URL)
.body(GQLRequestInserter.from(deleteMovieGQL, variables))
.exchange()
.expectStatus()
.isOk()
.expectBody(String.class)
.returnResult().getResponseBody();
System.out.print(responseBody);
Run Code Online (Sandbox Code Playgroud)
然后我看到的结果是
{"data":{"deleteMovie":{"id":7}}}
Run Code Online (Sandbox Code Playgroud)
我发现我期望的是“movie”而不是“deleteMovie”属性。所以我将测试更改为
.jsonPath("$.data.deleteMovie.id")
Run Code Online (Sandbox Code Playgroud)
总是运行两次测试并更改代码很麻烦。
有没有更好的方法让WebTestClient在测试失败时始终输出responseBody?
我是 Spring WebClient 的新手,我编写了一个通用方法,可用于在我的应用程序中使用 REST API:
public <T> List<T> get(URI url, Class<T> responseType) {
return WebClient.builder().build().get().uri(url)
.header("Authorization", "Basic " + principal)
.retrieve().bodyToFlux(responseType).collectList().block();
}
Run Code Online (Sandbox Code Playgroud)
如果消耗了rest-api返回404,我想返回并清空列表。
有人可以建议如何实现这一目标吗?
如何使用 Spring Bootwebclient
发布application/x-www-form-urlencoded
内容类型为“application/x-www-form-urlencoded”的示例 curl 请求的请求
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'username=XXXX' \
--data-urlencode 'password=XXXX'
Run Code Online (Sandbox Code Playgroud)
如何使用 webclient 发送相同的请求?
我使用 Web-flux/Reactive 和 Webclient,在 tomcat 和 spring-boot 上运行它。
一切正常。我阅读了很多关于它的信息。问题似乎是每当你使用 webclient 时,你必须返回或使用响应,否则它会关闭连接而你还没有使用它,你会看到很多日志消息说the connection close prematurely
,如果我有404 状态代码是一个错误的场景,我可以使用OnStatus
并抛出异常,但我的情况是:当上游服务返回 404 时,我必须手动返回单声道空。所以我不使用来自 Web 客户端请求的响应,我只是使用来自 .exchange() 的 ClientResponse 来检查状态并处理它。我最初的问题是日志消息,因为这只是“垃圾”,您不希望在日志消息中看到很多。我在某处读到,如果发生这种情况,连接也不能重复使用,所以这听起来很糟糕,但我不知道......如果没有找到,我只会收到这条消息,如果响应是 200它返回对象并且不打印日志消息。
我尝试使用 clientResponse.BodyToMono(Void.Class) 但它也不起作用。保持出现的日志消息
@Bean
public WebClient webClient(
@Value("${http.client.connection-timeout-millis}") final int connectionTimeoutMillis,
@Value("${http.client.socket-timeout-millis}") final int socketTimeoutMillis,
@Value("${http.client.wire-tap-enabled}") final boolean wireTapEnabled,
final ObjectMapper objectMapper) {
Consumer<Connection> doOnConnectedConsumer = connection ->
connection
.addHandler(new ReadTimeoutHandler(socketTimeoutMillis, MILLISECONDS))
.addHandler(new WriteTimeoutHandler(connectionTimeoutMillis, MILLISECONDS));
TcpClient tcpClient = TcpClient.newConnection()
.wiretap(wireTapEnabled)
.option(CONNECT_TIMEOUT_MILLIS, connectionTimeoutMillis)
.doOnConnected(doOnConnectedConsumer);
return WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(HttpClient.from(tcpClient).compress(true)))
.exchangeStrategies(customExchangeStrategies(objectMapper))
.build();
}
// .......... …
Run Code Online (Sandbox Code Playgroud) 环境:Spring Boot 2.3.1、Java 11
我已经尝试了一些东西(也与 spring 的示例应用程序进行比较),但到目前为止,我未能成功创建WebClient
需要ReactiveClientRegistrationRepository
.
启动我的 spring-boot 应用程序时出现以下异常:
required a bean of type 'org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository'
Run Code Online (Sandbox Code Playgroud)
我理解 spring-boot-autoconfigure 的方式应该使用ReactiveOAuth2ClientAutoConfiguration
,因为在 yml 文件中给出了所需的属性。
根据一些代码片段,如果缺少某些内容,我可以提供更多信息以获取整个上下文
主级
@Slf4j
@SpringBootApplication
@EnableConfigurationProperties(MyAppConfigurationProperties.class)
public class MyApp{
public static void main(final String[] args) {
SpringApplication.run(MyApp.class, args);
}
}
Run Code Online (Sandbox Code Playgroud)
配置:
@Configuration
//@Import(ReactiveOAuth2ClientAutoConfiguration.class) // in the test it works with this, but should not be required: spring-boot-autoconfigure
public class MyRestClientConfig {
@Bean
WebClient myWebClient(WebClient.Builder builder, ReactiveClientRegistrationRepository clientRegistrations) {
//content not relevant to this …
Run Code Online (Sandbox Code Playgroud) 我正在运行微服务 API 的负载,其中涉及使用 Spring Reactive Webclient 调用其他微服务 API。我正在使用 Postman runner 选项卡来测试这一点。
\n首先,我以 1500 次迭代运行负载,每个请求都会调用第二个微服务,一切都按预期正常工作。\n但是当我以 5000 次迭代运行负载时,第二个微服务被调用 3500 次,并且调用次数为 1500 次。由于问题而失败
\n\n\nWebClientRequestException:待处理获取队列已达到其最大大小 1000
\n
使用默认配置的 org.springframework.web.reactive.function.client.WebClient ,下面是代码片段。
\n private WebClient webClient;\n\n @PostConstruct\n public void init() {\n this.webClient = WebClient.builder().defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)\n .build();\n }\n
Run Code Online (Sandbox Code Playgroud)\n可以采取什么措施来避免这种情况?
\n我正在使用最新的 spring-boot-starter-parent 依赖项(版本 2.5.3)和 spring-webflux-5.3.9.jar jar。
\n日志:
\nreactor.core.Exceptions$ErrorCallbackNotImplemented: reactor.core.Exceptions$RetryExhaustedException: Retries exhausted: 3/3\nCaused by: reactor.core.Exceptions$RetryExhaustedException: Retries exhausted: 3/3\n at reactor.core.Exceptions.retryExhausted(Exceptions.java:290)\n at reactor.util.retry.RetryBackoffSpec.lambda$static$0(RetryBackoffSpec.java:67)\n at reactor.util.retry.RetryBackoffSpec.lambda$generateCompanion$4(RetryBackoffSpec.java:557)\n at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.drain(FluxConcatMap.java:375)\n at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.innerComplete(FluxConcatMap.java:296)\n at reactor.core.publisher.FluxConcatMap$ConcatMapInner.onComplete(FluxConcatMap.java:885)\n at …
Run Code Online (Sandbox Code Playgroud) 我知道使用 GET 请求发送正文并不是最好的主意,但我正在尝试使用需要它的现有 API。
使用 POST 发送正文非常简单:
webClient.post()
.uri("/employees")
.body(Mono.just(empl), Employee.class)
.retrieve()
.bodyToMono(Employee.class);
Run Code Online (Sandbox Code Playgroud)
但它不起作用webClient.get()
,因为当该post()
方法返回 a时WebClient.RequestBodyUriSpec
,该get()
方法返回WebClient.RequestHeadersUriSpec<?>
,这似乎不允许任何主体定义。
我在这里找到了 Spring RestTemplate 的解决方法:RestTemplate get with body,但没有找到新 WebClient 的解决方法。
spring-webclient ×10
spring-boot ×6
java ×4
spring ×3
netty ×1
performance ×1
reactive ×1
testing ×1