Flux承诺零到多个异步事件,Mono承诺零或一个异步结果;前者适用于流式数据场景,后者适用于一次性操作;二者在操作符行为、背压策略及转换方式上存在本质差异。

Flux 和 Mono 是 Project Reactor 中最基础、最关键的两个发布者(Publisher)类型,它们不是“可选工具”,而是响应式编程模型的基石。核心区别不在语法或写法,而在于语义承诺:Flux 承诺“零到多个异步事件”,Mono 承诺“零或一个异步结果”。这个承诺决定了后续操作符行为、背压策略、错误传播方式,甚至影响整个链路的设计逻辑。
数据容量与生命周期语义
Flux 表示一个可能无限长的异步序列,能发出 0、1、2…N 个元素,之后以 onComplete 或 onError 终止。它天然适配流式场景:如 SSE 推送、日志行读取、数据库游标遍历、事件总线消息等。一旦开始订阅,它就可能持续发射数据,直到主动取消或流自然结束。
Mono 则只允许至多一次 onNext(或零次),紧接着必须是 onComplete 或 onError。它不支持“多次 emit”,强行调用多次 next() 会触发 IllegalStateException。这种严格性让 Mono 更适合建模“一次性动作”:HTTP GET 单资源、Redis key 查询、用户登录校验、定时任务执行结果等。
- Flux.fromIterable(list) → 每个元素单独触发 onNext
- Mono.just(value) → value 触发一次 onNext,然后 onComplete
- Mono.empty() → 直接 onComplete,无 onNext
- Mono.error(e) → 直接 onError,无 onNext
操作符行为差异
很多操作符在 Flux 和 Mono 上同名但语义不同。例如 flatMap:
- Flux.flatMap(f):对每个输入元素应用 f(返回 Flux 或 Mono),再将所有子流扁平合并为一个 Flux
- Mono.flatMap(f):对单个值(或空)应用 f(必须返回 Mono),结果仍是 Mono;若 f 返回 Flux,需显式 .flux() 转换
再比如 collectList:
- Flux.collectList() → 收集全部元素为 List,返回 Mono
- >
- Mono.collectList() → 无意义,编译报错(Mono 不支持 collectList)
其他典型差异操作符:reduce、concatWith、mergeWith、zipWith —— 它们在 Mono 上往往被重载为单值组合逻辑(如 zipWith(Mono) 返回新 Mono),而在 Flux 上则面向多值聚合或交叉。
背压与资源控制策略
两者都遵循 Reactive Streams 规范,支持背压,但默认消费模式不同:
- Flux 在多数创建方式(如 Flux.range、Flux.fromIterable)中默认采用“拉取式”背压:订阅后先 request(1),处理完再 request 下一个,天然防溢出
- Mono 因只发 0 或 1 个元素,不涉及“多次 request”,其背压更多体现在上游如何响应 demand —— 例如 Mono.fromCallable 在订阅时才执行 callable,本质是 demand 驱动的延迟求值
这意味着:在高吞吐管道中,Flux 的背压信号更频繁、更关键;而 Mono 的重点常在“何时触发计算”和“如何缓存/重试单次结果”(如 cache()、retry()、switchIfEmpty())。
转换与协同使用场景
实际开发中,Flux 和 Mono 往往混合出现,且可安全互转:
- Flux → Mono:collectList()、next()(取首个)、singleOrEmpty()、count()、any() 等,把多值流“降维”为单值结果
- Mono → Flux:flux() 方法直接转为最多含一个元素的 Flux;也可用 repeat()、concatWith() 构造重复或追加流
- 组合多个 Mono 成 Flux:Flux.concat(Mono1, Mono2, Mono3) 或 Flux.merge(Mono1, Mono2)
- 从 Flux 中提取 Mono:Flux.filter(...).next() 表示“找第一个匹配项”,结果是 Mono
典型例子:批量查用户 ID 列表 → Flux>。










