Spring WebFlux中Mono.zip异常如何不中断执行并获取成功调用结果
问题核心原因
Mono.zip默认行为是只要任意参与组合的Mono发出错误信号,整个组合流会立刻触发错误回调,中断执行,错误会直接向下游传递,不会等待其他调用返回结果onErrorContinue不适用于当前场景:该操作符仅支持处理「上游已发出正常元素,下游处理元素时抛出异常」的场景,无法处理上游直接发出错误信号的情况
解决方案
步骤1:定义结果包装类
首先自定义一个可以同时容纳成功结果和异常信息的包装类,方便后续区分每个调用的状态:
public class CallResult { // 成功时返回的业务数据 private Model successData; // 失败时捕获的自定义异常 private CustomException error; public static CallResult success(Model data) { CallResult result = new CallResult(); result.successData = data; return result; } public static CallResult error(CustomException e) { CallResult result = new CallResult(); result.error = e; return result; } // 状态判断、getter方法自行补充 public boolean isSuccess() { return error == null; } }
步骤2:封装单个调用的错误捕获逻辑
在每个getCall调用层直接捕获异常,将错误信号转为正常的CallResult信号,不让错误流入Mono.zip:
private Mono<CallResult> wrapGetCall(String path) { return getCall(path) .map(CallResult::success) // 捕获自定义异常,转为错误状态的CallResult .onErrorResume(CustomException.class, e -> Mono.just(CallResult.error(e))); }
步骤3:调整Mono.zip逻辑
用包装后的Mono<CallResult>进行组合,此时所有调用的错误都已经被提前处理,Mono.zip会等待两个调用全部完成后再返回结果:
Tuple2<CallResult, CallResult> result = Mono.zip( wrapGetCall(call1), wrapGetCall(call2) ) .block(); // 分别处理两个调用的结果 CallResult call1Result = result.getT1(); if (call1Result.isSuccess()) { Model data1 = call1Result.getSuccessData(); // 处理call1成功逻辑 } else { CustomException e1 = call1Result.getError(); // 处理call1失败逻辑 } // 同理处理call2的结果 CallResult call2Result = result.getT2(); // ...
简化方案(不需要异常详情场景)
如果你只需要判断调用是否成功,不需要拿到具体的异常信息,可以直接用Optional代替自定义包装类,简化代码:
private Mono<Optional<Model>> wrapGetCall(String path) { return getCall(path) .map(Optional::ofNullable) .onErrorResume(CustomException.class, e -> Mono.just(Optional.empty())); }
注意事项
- 该方案会等待所有调用全部执行完成(无论成功失败)后才会返回聚合结果,完全符合你需要的不中断执行、可获取所有调用结果的需求
- 有更多调用需要组合时,对每个调用做相同的包装处理即可
onErrorResume是Spring WebFlux官方推荐的上游错误捕获操作符,不要使用onErrorContinue处理当前场景的问题
内容的提问来源于stack exchange,提问作者Hosten
相关产品推荐
相关产品推荐

