环境
-
jdk : 21
-
spring boot : 3.2.0
-
spring cloud : 2023.0.0
代码信息
-
报错信息
java.lang.IllegalStateException: block()/blockFirst()/blockLast() are blocking
-
提供者的controller
@GetMapping("/info/{xxx}")public R<XXX> info(@PathVariable("xxx") String xxx) {return xxxService.getOneByXXX(xxx).flatMap(xxx-> Mono.just(R.ok(xxx)));}
-
调用者的controller
@PostMapping("xxx") public R<Map<String, Object>> login(@RequestBody LoginBody form){//同步操作,直接调用servicexxxService.info(form.getXXX()); }
-
@HttpExchange的配置信息
@Configuration public class RemoteXXXFactory {@Beanpublic RemoteXXXClient remoteXXXClient() {WebClient webClient =WebClient.builder().baseUrl("http://localhost:9200/").clientConnector(new ReactorClientHttpConnector(HttpClient.create())).build();//创建代理工厂,设置超时时间HttpServiceProxyFactory proxyFactory =HttpServiceProxyFactory.builderFor(WebClientAdapter.create(webClient)).build();//创建某个接口的代理服务return proxyFactory.createClient(RemoteXXXClient.class);}}
-
远程调用的方法@HttpExchange
@HttpExchange("/xxx") public interface RemoteXXXClient {@GetExchange("/info/{xxx}")R<XXX> getXXXInfo(@PathVariable("xxx") String xxx,@RequestHeader(SecurityConstants.FROM_SOURCE) String source); }
问题分析
-
进行post请求时,出现block()/blockFirst()/blockLast() are blocking,在进行排除法后发现原因出现在@RequestBody这个注解上。和webclient产生了冲突。同步阻塞与异步非阻塞,产生的错误。
解决方案
-
将该开放的远程接口改为异步操作,
-
用Mono来对这一系列的操作进行包装
-
对提供者的controller中暴露的方法改为Mono,使用的service中的方法也需要该为Mono
-
对调用者同样接受改为Mono,如httpExchange中调用的外露方法同步改为Mono
@HttpExchange("/xxx") public interface RemoteXXXClient {@GetExchange("/info/{xxx}")Mono<R<XXX>> getXXXInfo(@PathVariable("xxx") String xxx,@RequestHeader(SecurityConstants.FROM_SOURCE) String source); }
-
例如对提供者
@GetMapping("/info/{xxx}")public Mono<R<XXX>> info(@PathVariable("xxx") String xxx) {return xxxService.getOneByXXX(xxx).flatMap(loginUser -> Mono.just(R.ok(xxx)));}
然后将调用者
-
@PostMapping("xxx")public Mono<R<Map<String, Object>>> login(@RequestBody LoginBody form){//同步操作,直接调用servicereturn xxxService.info(form.getXXX())//info调用成功则进入flatMap.flatMap(//成功之后的其他操作).map(//封装返回结果).onErrorResume(//info失败了进入这个里面)//在boundedElastic这个弹性线程池中进行.subscribeOn(Schedulers.boundedElastic());;}
-
对于service中调用mapper方法使用异步调用
return Mono.fromCallable(() -> {//数据库操作}).subscribeOn(Schedulers.boundedElastic());