You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 06:28:07