Mono::zip对含flatMap的Mono不短路:该行为是特性还是Bug?
Reactor中Mono.zip的短路行为疑问
我尝试将两个Mono对象通过zip操作合并为一个,示例代码如下:
final Mono<String> mono1 = ...; final Mono<String> mono2 = ...; final Mono<String> result = Mono.zip(mono1, mono2) .map(args -> args.getT1() + ":" + args.getT1());
我的预期是,若mono1是error-Mono,则mono2不会被执行,但实际并非总是如此。当mono2是Mono::flatMap操作的结果时,即便mono1报错,mono2仍会被执行。以下测试代码可复现该行为:
@Test public void shortCircuitZip() { final var count = new AtomicInteger(); // 第一个为error-Mono时,zip会短路 final var mono1 = Mono.zip( Mono.<String>error(new IllegalArgumentException("Illegal")), Mono.fromSupplier(() -> { count.incrementAndGet(); return "RESULT"; }) ) .map(args -> "%s:%s".formatted(args.getT1(), args.getT2())); assertThatThrownBy(mono1::block).isInstanceOf(IllegalArgumentException.class); assertThat(count.get()).isEqualTo(0); // 若第二个Mono包含flatMap,zip不再短路 final var mono2 = Mono.zip( Mono.<String>error(new IllegalArgumentException("Illegal")), Mono.fromSupplier(() -> { count.incrementAndGet(); return "RESULT"; }) .flatMap(v -> Mono.just(v)) ) .map(args -> "%s:%s".formatted(args.getT1(), args.getT2())); assertThatThrownBy(mono2::block).isInstanceOf(IllegalArgumentException.class); // 第二个Mono含flatMap时,该断言失败 assertThat(count.get()).isEqualTo(0); }
请问该行为是Reactor库的设计特性,还是Bug?我使用的Reactor版本为:io.projectreactor:reactor-core:3.4.19
这是Reactor的设计特性,而非Bug,核心原因在于Reactor中Mono的订阅时机与操作符的实现逻辑:
- 基础冷源的订阅延迟:像
Mono.fromSupplier这类基础源操作符属于冷源,只有被订阅时才会执行内部逻辑。第一个测试用例中,Mono.zip收到第一个Mono的错误信号后会立即终止,不会订阅第二个Mono,因此Supplier逻辑不会执行,count保持0。 - flatMap的提前订阅特性:
flatMap的实现逻辑是,自身被订阅时会立即订阅上游源,再等待上游产生数据后订阅内部Mono。第二个测试用例中,Mono.zip初始化时会先订阅两个参数Mono,第二个Mono包含的flatMap会立即触发上游fromSupplier的执行——此时第一个Mono的错误还未传递到zip的终止逻辑,导致count被递增。
如果需要实现严格短路(第一个Mono出错时第二个Mono的上游逻辑完全不执行),可以用Mono.defer包裹第二个Mono,延迟其初始化与订阅:
final var mono2 = Mono.zip( Mono.<String>error(new IllegalArgumentException("Illegal")), Mono.defer(() -> Mono.fromSupplier(() -> { count.incrementAndGet(); return "RESULT"; }) .flatMap(v -> Mono.just(v)) ) ) .map(args -> "%s:%s".formatted(args.getT1(), args.getT2()));
这样只有当zip真正需要订阅第二个Mono时,defer内的逻辑才会初始化执行,从而实现短路效果。
内容的提问来源于stack exchange,提问作者Franz Wilhelmstätter
相关产品推荐
相关产品推荐

