Reactor响应式编程中Sinks.Many的核心概念、类型、用法,以及它和FluxSink的区别,从「定义定位→核心类型→实战示例→对比FluxSink→使用场景」逐步讲解,用通俗易懂的语言+业务场景示例,帮你掌握这个高级发射器的核心用法。
一、核心定义:Sinks.Many 是什么?
Sinks.Many是Reactor 3.4+版本引入的高级多订阅者发射器 ,是对基础FluxSink的增强封装,核心定位是:
- 专门处理「0到N个元素的异步序列」,支持多个订阅者同时消费数据流;
- 遵循Reactive Streams规范,原生支持背压(Backpressure);
- 提供多种「订阅者策略」,可精细化控制消息如何分发给不同订阅者(比如是否补发历史消息、是否允许多订阅者);
- 本质是「生产者-消费者模型」的高级实现,解决了
Flux.create()在多订阅者场景下灵活性不足的问题。
简单比喻:
FluxSink= 单水龙头(仅适配单个消费端,分发规则固定);Sinks.Many= 智能分水器(支持多个消费端,可配置给不同消费端发"新水"/"历史水")。
二、核心类型:4种订阅者策略(关键)
Sinks.Many的核心价值在于提供了4种不同的策略,适配不同的多订阅者场景,所有类型都通过Sinks.many()静态方法创建:
| 策略类型 | 核心特性 | 适用场景 |
|---|---|---|
unicast() |
仅支持1个订阅者,多订阅会抛异常;订阅者接收订阅后所有元素(无历史补发) | 一对一的专属数据流(如单个客户端的实时订单) |
multicast() |
支持多个订阅者 ;可配置是否补发历史消息(通过replay()控制) |
一对多广播(如奶茶店新品通知给所有用户) |
broadcast() |
轻量级多订阅者广播;不补发历史消息,仅发订阅后的新元素 | 实时状态推送(如库存实时变化) |
directAllOrNothing() |
极简多订阅者模式;要么所有订阅者都收到元素,要么都收不到(无背压) | 低延迟、无丢失要求的场景(如实时日志) |
关键补充:multicast() 的 replay 配置
multicast()是最常用的类型,通过replay()可控制历史消息补发:
replay(0):不补发历史,仅发订阅后的新元素;replay(5):补发订阅前最近的5条消息;replayAll():补发所有历史消息(需注意内存占用)。
三、实战示例:结合奶茶店场景
以下示例基于Reactor 3.5+,用「奶茶店新品通知」场景演示核心用法:
前置依赖(同Flux)
xml
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>3.5.11</version>
</dependency>
示例1:multicast() 广播新品(支持补发历史)
java
import reactor.core.publisher.Flux;
import reactor.core.publisher.Sinks;
public class SinksManyDemo {
public static void main(String[] args) throws InterruptedException {
// 1. 创建multicast类型的Sinks.Many,补发最近2条历史消息
Sinks.Many<String> productSink = Sinks.many()
.multicast()
.onBackpressureBuffer(2); // replay=2,背压策略为缓存
// 2. 转为Flux供订阅者消费(核心:Sinks.Many可转为Flux)
Flux<String> productFlux = productSink.asFlux();
// 3. 生产者先发射2条新品消息(此时暂无订阅者)
productSink.tryEmitNext("新品1:云桃乌龙");
productSink.tryEmitNext("新品2:桂花云露");
// 4. 第一个订阅者(用户A)订阅:会收到补发的2条历史消息
System.out.println("===== 用户A订阅 =====");
productFlux.subscribe(
msg -> System.out.println("用户A收到:" + msg),
error -> System.err.println("用户A出错:" + error),
() -> System.out.println("用户A流结束")
);
// 5. 生产者再发射1条新品(所有订阅者都收到新消息)
productSink.tryEmitNext("新品3:云雾观音");
// 6. 第二个订阅者(用户B)订阅:补发最近2条(新品2+新品3)
Thread.sleep(1000); // 模拟延迟
System.out.println("\n===== 用户B订阅 =====");
productFlux.subscribe(
msg -> System.out.println("用户B收到:" + msg),
error -> System.err.println("用户B出错:" + error),
() -> System.out.println("用户B流结束")
);
// 7. 触发流完成
productSink.tryEmitComplete();
}
}
执行结果:
===== 用户A订阅 =====
用户A收到:新品1:云桃乌龙
用户A收到:新品2:桂花云露
用户A收到:新品3:云雾观音
===== 用户B订阅 =====
用户B收到:新品2:桂花云露
用户B收到:新品3:云雾观音
用户A流结束
用户B流结束
示例2:unicast() 单订阅者(多订阅抛异常)
java
// 创建unicast类型,仅支持1个订阅者
Sinks.Many<String> unicastSink = Sinks.many()
.unicast()
.onBackpressureBuffer();
// 第一个订阅者正常
unicastSink.asFlux().subscribe(msg -> System.out.println("订阅者1:" + msg));
// 第二个订阅者会抛异常
try {
unicastSink.asFlux().subscribe(msg -> System.out.println("订阅者2:" + msg));
} catch (Exception e) {
System.err.println("订阅失败:" + e.getMessage()); // 输出:Only one subscriber allowed
}
// 生产者发射元素
unicastSink.tryEmitNext("仅订阅者1能收到的消息");
示例3:发射方法选择(tryEmitXxx vs emitXxx)
Sinks.Many提供两种发射元素的方式,需根据场景选择:
| 方法 | 特性 |
|---|---|
tryEmitNext() |
非阻塞,返回Sinks.EmitResult枚举(成功/失败),不会抛异常 |
emitNext() |
阻塞/异步(取决于策略),失败时抛异常,支持自定义重试逻辑 |
示例:
java
// tryEmitNext() 非阻塞发射
Sinks.EmitResult result = productSink.tryEmitNext("新品4:云谷燕麦");
if (result == Sinks.EmitResult.FAIL_OVERFLOW) {
System.err.println("元素发射失败:背压溢出");
}
// emitNext() 带重试逻辑发射
productSink.emitNext("新品5:云峰山茶",
(signalType, emitResult) -> emitResult == Sinks.EmitResult.FAIL_OVERFLOW);
四、Sinks.Many vs FluxSink:核心区别
| 维度 | Sinks.Many | FluxSink(Flux.create()) |
|---|---|---|
| 订阅者数量 | 支持多订阅者(可配置策略) | 仅支持隐式多订阅,但无精细化控制 |
| 历史消息补发 | 支持(multicast的replay配置) | 不支持,仅发订阅后的新元素 |
| 发射结果处理 | 提供tryEmitXxx,返回枚举,容错性强 | 无返回值,失败直接抛异常 |
| 状态监控 | 支持(如currentSubscriberCount()) |
无原生状态监控能力 |
| 适用场景 | 多订阅者、需补发历史、状态监控的场景 | 单订阅者、简单手动发射的场景 |
五、核心使用场景
- 一对多广播 :如奶茶店新品通知给所有在线用户(用
multicast(replay(0))); - 带历史补发的订阅 :如用户刚上线,补发最近的3条库存预警消息(用
multicast(replay(3))); - 一对一专属流 :如单个用户的实时订单推送(用
unicast()); - 高容错的元素发射 :需捕获发射失败(如背压溢出),用
tryEmitXxx做降级处理; - 状态监控 :需统计当前订阅者数量(如
productSink.currentSubscriberCount())。
总结
- 核心定位 :
Sinks.Many是Reactor的高级多订阅者发射器,是FluxSink的增强版,主打多订阅者场景的精细化控制; - 核心选择 :
- 单订阅者、简单场景 → 用
Flux.create()+FluxSink; - 多订阅者、需补发历史、高容错 → 用
Sinks.Many(优先multicast());
- 单订阅者、简单场景 → 用
- 关键技巧 :
- 发射元素优先用
tryEmitXxx,避免异常中断; multicast()的replay配置需根据内存情况选择(避免replayAll()导致OOM);- 始终配置背压策略,平衡生产/消费速率。
- 发射元素优先用
Sinks.Many是Reactor中实现"发布-订阅"模式的核心工具,在实时消息推送、多客户端状态同步、分布式事件通知等场景中不可或缺。