Spring Integration中WebClient使用问题:block()调用报错及无block()时请求未执行的解决方法
解决方案:在Spring Integration中正确使用WebClient(非阻塞方式)
这个问题的核心在于对反应式编程模型的理解:WebClient返回的Mono/Flux是"冷流"——只有当有订阅(subscribe)操作时,请求才会实际执行。同时,反应式线程池(比如reactor-http-nio-*)禁止阻塞操作(block()/blockFirst()等),因为这些线程是为非阻塞IO设计的,阻塞会破坏整个反应式系统的性能和弹性。
下面是具体的修复步骤:
1. 修改操作类方法,返回反应式类型
首先,让verifyContract方法返回Mono<ResponseEntity<LisResponse>>,而不是void。这样我们就能把反应式流传递给上层,由Spring Integration来处理订阅:
private final WebClient webClient; @Override public Mono<ResponseEntity<LisResponse>> verifyContract(EContractRequest contractRequest, String vendorTransactionId, String loanId) { return webClient.post() .uri(baseUrl + contract) .header("sourceRequestId", sourceRequestId) .header("VENDOR-ID", String.join(";", List.of(vendorId, vendorTransactionId, loanId))) .bodyValue(contractRequest) .retrieve() .toEntity(LisResponse.class); }
2. 修改激活器方法,使用反应式流处理
将sendEContract方法的返回类型改为Mono<Application>,利用反应式操作符来串联请求逻辑,让Spring Integration自动订阅这个流,触发WebClient请求:
@ServiceActivator(inputChannel = "lisContractValidationChannel", outputChannel = "aopContractValidationReplyAndContinueRouterChannel") public Mono<Application> sendEContract(Application application) { ContractRequest contractRequest = prepareRequest(application); // 调用verifyContract获取反应式流,请求完成后返回原Application return lisOperations.verifyContract( contractRequest, application.getContractData().getRouteOne().getConversationID(), application.getRouteOneId() ) .then(Mono.just(application)); // then()表示等待请求完成,忽略响应内容 }
如果需要处理WebClient的响应结果(比如更新Application的状态),可以用map()操作符:
public Mono<Application> sendEContract(Application application) { ContractRequest contractRequest = prepareRequest(application); return lisOperations.verifyContract( contractRequest, application.getContractData().getRouteOne().getConversationID(), application.getRouteOneId() ) .map(response -> { // 根据响应更新Application LisResponse lisResponse = response.getBody(); if (lisResponse != null && lisResponse.isValid()) { application.setContractStatus(ContractStatus.VALID); } return application; }) .onErrorResume(ex -> { // 处理请求失败的情况,比如记录日志、设置错误状态 log.error("Contract verification failed for loan ID: {}", application.getRouteOneId(), ex); application.setContractStatus(ContractStatus.VERIFICATION_FAILED); return Mono.just(application); }); }
3. 为什么这样能解决问题?
- 非阻塞执行:整个流程基于反应式流,不会阻塞
reactor-http-nio线程,符合反应式编程的规则,避免了IllegalStateException。 - 自动订阅:Spring Integration会自动订阅
@ServiceActivator方法返回的Mono,触发WebClient的请求执行,解决了"请求未执行"的问题。
额外注意事项
- 如果你的通道需要更好的反应式支持,可以考虑使用
FluxMessageChannel(Spring Integration提供的反应式通道实现),不过默认的DirectChannel也能正常处理反应式消息。 - 尽量避免在反应式代码中使用任何阻塞操作(包括
block()、Thread.sleep()等),保持整个调用链的非阻塞性。
内容的提问来源于stack exchange,提问作者TuGordoBello
相关产品推荐
相关产品推荐

