【问题标题】:Spring-Boot WebClient block() method returns error java.lang.IllegalStateExceptionSpring-Boot WebClient block() 方法返回错误 java.lang.IllegalStateException
【发布时间】:2021-08-02 01:25:59
【问题描述】:

我正在尝试使用 Spring WebFlux WebClient 获取值(字符串),(使用 SpringBoot 版本 2.4.5,)

@GetMapping("/test")
public Mono<String> getData(){
    WebClient webClient = WebClient.create("http://localhost:9999");
    Mono<String> stringMono = webClient.get()
            .uri("/some/thing")
            .retrieve()
            .bodyToMono(String.class);
    stringMono.subscribe( System.out::println);
    System.out.println("Value : " + stringMono.block()); // this doesn't work,  expecting to return ResponseBody as "Hello World" ,
    return stringMono;
}

但是遇到错误

2021-05-11 20:02:15.521 ERROR 55613 --- [ctor-http-nio-2] a.w.r.e.AbstractErrorWebExceptionHandler : [19114471-1]  500 Server Error for HTTP GET "/test"
java.lang.IllegalStateException: block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-nio-2
    at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:83) ~[reactor-core-3.4.3.jar:3.4.3]
    Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):
    |_ checkpoint ⇢ HTTP GET "/test" [ExceptionHandlingWebHandler]
Stack trace:
        at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:83) ~[reactor-core-3.4.3.jar:3.4.3]
        at reactor.core.publisher.Mono.block(Mono.java:1703) ~[reactor-core-3.4.3.jar:3.4.3]
        at com.example.demo.DemoApplication.getData(DemoApplication.java:28) ~[main/:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
        at org.springframework.web.reactive.result.method.InvocableHandlerMethod.lambda$invoke$0(InvocableHandlerMethod.java:146) ~[spring-webflux-5.3.4.jar:5.3.4]
        at reactor.core.publisher.FluxFlatMap.trySubscribeScalarMap(FluxFlatMap.java:151) ~[reactor-core-3.4.3.jar:3.4.3]

块方法参考 - https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#webflux-client-synchronous

【问题讨论】:

    标签: spring-boot spring-webflux spring-webclient


    【解决方案1】:

    在使用 Webflux 时,整个想法是您不要阻塞 - 如果这样做会导致大量性能问题(请参阅 here for a related answer 解释原因),因此框架明确禁止它, 如果你尝试抛出异常。

    您也不应该手动订阅 - 虽然在响应式世界中不是像阻塞那样的“大罪”,但这肯定是另一个危险信号。订阅由框架处理。您只需返回Mono,Webflux 将在需要处理您的请求时进行订阅。在您的情况下,您的手动订阅意味着整个链实际上将为每个请求执行两次 - 一次将结果打印到终端,一次将结果返回到/test 端点。

    相反,如果您想要这样的“副作用”(当您拥有值时打印出该值),那么您需要使用doOnNext() 运算符来更改反应链来执行此操作。这意味着您可以这样做:

    return webClient.get()
              .uri("/some/thing")
              .retrieve()
              .bodyToMono(String.class)
              .doOnNext(s -> System.out.println("Value: " + s));
    

    这将确保将值打印到终端,但不会阻塞,也无需手动订阅。

    【讨论】:

    • Michael 我同意你的观点,但是我有一个 webClient 的响应(POJO)将用于下一次 webClient api 调用的场景,那么我将如何处理后续的 api 调用?
    • @GautamGarg 您只需扩展反应链、平面映射或将此 Web 调用的结果转换为下一个 Web 服务调用的结果。反应式编程几乎要求你以这种方式做事——打破反应链并不允许你对管道中的前一个结果做出“反应”。
    猜你喜欢
    • 1970-01-01
    • 2019-05-16
    • 2020-02-20
    • 2021-10-13
    • 1970-01-01
    • 2018-07-15
    • 2017-03-23
    • 2018-07-09
    • 2017-12-11
    相关资源
    最近更新 更多