Reactor中Mono引发API重复调用:是Bug还是预期行为?
为什么首个API会被调用两次?这是Mono的Bug还是预期行为?
这绝对不是Reactor的Bug,而是冷Publisher(比如Mono)的预期行为。让我帮你拆解一下背后的逻辑:
核心原因:冷序列的订阅特性
Reactor中的Mono默认是冷序列——简单来说就是每一次订阅都会从头触发整个序列的执行流程。回到你的代码里看:
val firstResult = callApi().doOnSuccess { r -> println("callApi returned: $r") } val secondResult = callApi(firstResult).doOnSuccess { r -> println("callApi(result) returned: $r") } // zip操作会同时订阅firstResult和secondResult Mono.zip(firstResult, secondResult, ...).block()
- 当
secondResult内部的firstResult.flatMap(...)执行时,会第一次订阅firstResult,触发callApi()对应的API调用; - 当
Mono.zip(...)执行时,会第二次订阅firstResult,再次触发callApi()的API调用。
这就是你看到两次API调用、两个返回结果的直接原因。
怎么解决这个问题?
你需要把冷的Mono转换成热序列,让它只执行一次,后续所有订阅都复用第一次的结果。最常用的方案是使用cache()操作符:
修改你的代码,给firstResult加上cache():
val firstResult = callApi().doOnSuccess { r -> println("callApi returned: $r") }.cache() val secondResult = callApi(firstResult).doOnSuccess { r -> println("callApi(result) returned: $r") } println(Mono.zip(firstResult, secondResult, { first, second -> "First result is ${first}\nSecond result is $second" }).block())
cache()会缓存Mono第一次订阅产生的结果,之后所有的订阅都会直接获取这个缓存值,不会再触发上游的API调用。
另外你也可以用share()操作符,但cache()更适合这种只需要单次结果缓存的场景。
补充:结果不确定性的原因
因为两次API调用是异步执行的,secondResult使用的是第一次订阅firstResult的结果还是第二次的,完全取决于哪个调用先完成。这就是你看到传递给第二个API的参数时而为1时而为5的原因——完全是两次异步调用的执行顺序导致的随机性。
内容的提问来源于stack exchange,提问作者jimmy_terra
相关产品推荐
相关产品推荐

