环境
-
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){ //同步操作,直接调用service xxxService.info(form.getXXX()); }
- @HttpExchange的配置信息
@Configuration public class RemoteXXXFactory { @Bean public 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){ //同步操作,直接调用service return xxxService.info(form.getXXX()) //info调用成功则进入flatMap .flatMap( //成功之后的其他操作 ) .map(//封装返回结果) .onErrorResume( //info失败了进入这个里面 ) //在boundedElastic这个弹性线程池中进行 .subscribeOn(Schedulers.boundedElastic()); ; }
- 对于service中调用mapper方法使用异步调用
return Mono.fromCallable(() -> { //数据库操作 }).subscribeOn(Schedulers.boundedElastic());