Project Reactor 是Java响应式编程库 ,提供Mono 和Flux 核心类型,支持非阻塞、背压及异步数据流处理 。它是Spring WebFlux 的基础,适用于构建高并发 、低延迟的微服务与事件驱动应用,遵循Reactive Streams规范。
2. 深入 Project Reactor:从原理到工程实践的全面指南
3. Flux 与 Mono:Project Reactor 核心响应式类型深度解析
4. Mono:Project Reactor 中最精巧的响应式原语
5. 创建 Flux/Mono 并订阅:Project Reactor 响应式编程的第一步
6. 程序化创建响应式序列:Flux.generate、Flux.create 与 Flux.push 深度解析
7. 线程调度与 Schedulers:Project Reactor 并发模型的核心引擎
引言
在 Reactor 3.4 之前,当开发者需要从外部命令式代码向响应式流中"推送"数据时,主要依赖的是 Processor 系列 API(如 UnicastProcessor、EmitterProcessor、ReplayProcessor)。然而,这套 API 存在诸多问题:
- 接口设计混乱:Processor 同时实现了 Publisher 和 Subscriber,语义模糊
- 线程安全隐患:不同 Processor 的线程安全行为不一致,文档不清晰
- 发射失败处理不透明:数据丢失时缺乏明确的反馈机制
- 背压语义不明确:不同 Processor 对背压的处理方式各异
Reactor 3.4 引入了全新的 Sinks API ,彻底重构了"程序化向流中注入数据"的体验:
Sinks 是一个专门设计用于从命令式世界向响应式世界"发射"数据的入口点。它将"发射端"与"消费端"清晰分离,提供明确的线程安全契约、显式的发射结果反馈,以及类型安全的构建器模式。
一、Sinks 的核心设计理念
1.1 与 Processor 的对比
| 维度 | 旧 Processor API(已废弃) | 新 Sinks API(推荐) |
|---|---|---|
| 接口模型 | Processor extends Publisher, Subscriber | Sinks.Many / Sinks.One(纯发射端) |
| 获取 | Publisher Processor 本身就是 Publisher | 通过 .asFlux() / .asMono() 获取 |
| 发射数据 | onNext(T) / onComplete() / onError(T) | tryEmitNext(T) / emitNext(T, handler) |
| 发射结果反馈 | 无(静默丢失) | 返回 EmitResult 枚举 |
| 线程安全文档 | 不明确 | 每种 Sink 明确标注线程安全性 |
| 背压策略 | 隐式 | 通过构建器显式选择 |
| 废弃状态 | ✅ 已废弃(3.5+) | ✅ 推荐使用 |
1.2 核心原则
java
┌───────────────────────────────────────────────────────────────┐
│ │
│ 命令式世界(生产者) 响应式世界(消费者) │
│ │
│ ┌─────────────┐ ┌─────────────────┐ │
│ │ HTTP 请求 │ │ │ │
│ │ MQ 消息 │──emitNext──→ │ Sinks.Many<T> │──asFlux()──→ Flux<T> │
│ │ 传感器数据 │ │ Sinks.One<T> │──asMono()──→ Mono<T> │
│ │ 用户事件 │ │ │ │
│ └─────────────┘ └─────────────────┘ │
│ │
│ • 多线程/单线程 • 背压感知 │
│ • 命令式调用 • 声明式消费 │
│ • 需要线程安全保证 • 操作符组合 │
│ │
└───────────────────────────────────────────────────────────────┘
二、Sinks 的类型体系
2.1 三大入口
java
// 多值 Sink → 对应 Flux
Sinks.Many<T> manySink = Sinks.many()...;
// 单值 Sink → 对应 Mono
Sinks.One<T> oneSink = Sinks.one();
// 空完成 Sink → 对应 Mono<Void>(只发完成/错误信号)
Sinks.Empty<Void> emptySink = Sinks.empty();
2.2 Sinks.Many 的三种多播模式
java
// ① Unicast:单订阅者
Sinks.Many<T> unicast = Sinks.many().unicast().onBackpressureBuffer();
// ② Multicast:多订阅者(无缓冲/有缓冲)
Sinks.Many<T> multicast = Sinks.many().multicast().onBackpressureBuffer();
Sinks.Many<T> direct = Sinks.many().multicast().directAllOrNothing();
Sinks.Many<T> bestEffort = Sinks.many().multicast().directBestEffort();
// ③ Replay:多订阅者 + 历史重放
Sinks.Many<T> replayAll = Sinks.many().replay().all();
Sinks.Many<T> replayLast = Sinks.many().replay().last();
Sinks.Many<T> replayN = Sinks.many().replay().limit(100);
2.3 完整类型速查表
| 构建方式 | 订阅者数 | 背压 | 缓冲 | 线程安全 | 典型场景 |
|---|---|---|---|---|---|
| many().unicast().onBackpressureBuffer() | 1 | ✅ 有缓冲 | 无界 Queue | 单生产者 | 单消费者管道 |
| many().unicast().onBackpressureBuffer(Queue) | 1 | ✅ 有缓冲 | 自定义 Queue | 单生产者 | 自定义队列策略 |
| many().multicast().onBackpressureBuffer() | N | ✅ 有缓冲 | 无界 | ✅ 多生产者 | 事件广播 |
| many().multicast().onBackpressureBuffer(bufferSize) | N | ✅ 有缓冲 | 有界 | ✅ 多生产者 | 限流广播 |
| many().multicast().directAllOrNothing() | N | ❌ 无缓冲 | 无 | ✅ 多生产者 | 低延迟广播 |
| many().multicast().directBestEffort() | N | ❌ 无缓冲 | 无 | ✅ 多生产者 | 尽力投递 |
| many().replay().all() | N | ✅ 有缓冲 | 全量重放 | ✅ 多生产者 | 事件溯源 |
| many().replay().last() | N | ✅ 有缓冲 | 最后 1 个 | ✅ 多生产者 | 最新状态 |
| many().replay().limit(n) | N | ✅ 有缓冲 | 最近 n 个 | ✅ 多生产者 | 滑动窗口 |
| many().replay().limit(Duration) | N | ✅ 有缓冲 | 时间窗口 | ✅ 多生产者 | 时间窗口重放 |
| one() | N | --- | --- | ✅ 多生产者 | 单值异步结果 |
| empty() | N | --- | --- | ✅ 多生产者 | 完成/错误通知 |
三、发射 API:emitNext、tryEmitNext 与 EmitResult
3.1 两种发射风格
Reactor Sinks 提供了两种发射 API,区别在于失败处理策略:
风格一:tryEmitXxx() --- 返回结果枚举
java
Sinks.EmitResult result = sink.tryEmitNext(value);
if (result.isFailure()) {
// 处理失败
log.warn("Emit failed: {}", result);
}
风格二:emitXxx(value, failureHandler) --- 回调处理
java
sink.emitNext(value, (signalType, emitResult) -> {
if (emitResult.isFailure()) {
log.error("Failed to emit {}: {}", signalType, emitResult);
return false; // 不重试
}
return true;
});
3.2 EmitResult 枚举详解
java
public enum EmitResult {
OK, // 成功
FAIL_TERMINATED, // Sink 已终止(已 complete 或 error)
FAIL_OVERFLOW, // 背压溢出(下游消费不过来)
FAIL_NON_SERIALIZED, // 并发发射冲突(非序列化调用)
FAIL_CANCELLED, // Sink 已被取消
FAIL_ZERO_SUBSCRIBER; // 无订阅者(仅 direct 模式)
}
3.3 发射方法全景
| 方法 | 说明 | 返回值 |
|---|---|---|
| tryEmitNext(T) | 尝试发射一个元素 | EmitResult |
| tryEmitComplete() | 尝试发出完成信号 | EmitResult |
| tryEmitError(Throwable) | 尝试发出错误信号 | EmitResult |
| emitNext(T, EmitFailureHandler) | 发射元素,失败时回调 | void |
| emitComplete(EmitFailureHandler) | 发出完成信号 | void |
| emitError(Throwable, EmitFailureHandler) | 发出错误信号 | void |
3.4 EmitFailureHandler 内置策略
java
// 不重试,直接抛出异常
Sinks.EmitFailureHandler.FAIL_FAST
// 对 FAIL_NON_SERIALIZED 进行有限重试
Sinks.EmitFailureHandler.busyLooping(Duration.ofMillis(100))
// 自定义重试逻辑
Sinks.EmitFailureHandler handler = (signalType, emitResult) -> {
if (emitResult == Sinks.EmitResult.FAIL_NON_SERIALIZED) {
return true; // 重试
}
return false; // 不重试,抛出异常
};
四、Sinks.Many 各模式深度解析
4.1 Unicast:单订阅者模式
java
Sinks.Many<String> unicastSink = Sinks.many().unicast().onBackpressureBuffer();
// 获取 Flux(只能被订阅一次)
Flux<String> flux = unicastSink.asFlux();
// 发射数据
unicastSink.tryEmitNext("message-1");
unicastSink.tryEmitNext("message-2");
unicastSink.tryEmitComplete();
// 订阅(只能有一个订阅者)
flux.subscribe(System.out::println);
// 输出: message-1, message-2
关键特性:
- 只允许一个订阅者------第二个订阅者会收到 onError
- 无订阅者时数据被缓冲------直到第一个订阅者到来
- 单生产者(非线程安全)------同一时间只能有一个线程调用 emit
java
// ⚠️ 第二个订阅者会失败
flux.subscribe(System.out::println); // OK
flux.subscribe(System.out::println); // onError: "UnicastProcessor allows only one Subscriber"
自定义 Queue:
java
// 使用有界队列限制缓冲
Queue<String> boundedQueue = new ArrayBlockingQueue<>(1024);
Sinks.Many<String> sink = Sinks.many().unicast().onBackpressureBuffer(boundedQueue);
// 使用优先级队列
Queue<Task> priorityQueue = new PriorityQueue<>(Comparator.comparing(Task::getPriority));
Sinks.Many<Task> taskSink = Sinks.many().unicast().onBackpressureBuffer(priorityQueue);
4.2 Multicast:多订阅者模式
4.2.1 onBackpressureBuffer(有缓冲)
java
Sinks.Many<String> multicastSink = Sinks.many().multicast().onBackpressureBuffer();
Flux<String> flux = multicastSink.asFlux();
// 多个订阅者都能收到数据
flux.subscribe(s -> System.out.println("Subscriber A: " + s));
flux.subscribe(s -> System.out.println("Subscriber B: " + s));
multicastSink.tryEmitNext("hello");
multicastSink.tryEmitComplete();
关键特性:
- 多个订阅者共享同一个数据流
- 无订阅者时数据被丢弃(与 unicast 不同!)
- 线程安全------多线程可以同时调用 tryEmitNext
- 内部有缓冲队列处理背压
4.2.2 directAllOrNothing(无缓冲,全或无)
java
Sinks.Many<String> directSink = Sinks.many().multicast().directAllOrNothing();
Flux<String> flux = directSink.asFlux();
flux.subscribe(System.out::println);
flux.subscribe(System.out::println);
Sinks.EmitResult result = directSink.tryEmitNext("data");
// 如果任何一个订阅者消费不过来 → 返回 FAIL_OVERFLOW
// 所有订阅者都准备好 → 返回 OK
语义:要么所有订阅者都能接收,要么全部失败。适合低延迟、不容忍数据丢失的场景。
4.2.3 directBestEffort(无缓冲,尽力投递)
java
Sinks.Many<String> bestEffortSink = Sinks.many().multicast().directBestEffort();
Flux<String> flux = bestEffortSink.asFlux();
语义:尽可能将数据投递给所有订阅者,消费不过来的订阅者会收到 onError(OverflowException)。适合允许部分订阅者掉队的场景。
4.3 Replay:重放模式
java
// 重放所有历史数据
Sinks.Many<String> replayAll = Sinks.many().replay().all();
// 只重放最后一个元素
Sinks.Many<String> replayLast = Sinks.many().replay().last();
// 重放最近 N 个元素
Sinks.Many<String> replayN = Sinks.many().replay().limit(100);
// 重放最近一段时间内的元素
Sinks.Many<String> replayTime = Sinks.many().replay().limit(Duration.ofMinutes(5));
典型场景:
java
// 场景:新订阅者加入时,需要收到最近 10 条消息
Sinks.Many<Notification> notificationSink = Sinks.many().replay().limit(10);
Flux<Notification> notifications = notificationSink.asFlux();
// 先发射一些数据
notificationSink.tryEmitNext(new Notification("msg-1"));
notificationSink.tryEmitNext(new Notification("msg-2"));
notificationSink.tryEmitNext(new Notification("msg-3"));
// 新订阅者此时加入 → 会收到 msg-1, msg-2, msg-3
notifications.subscribe(n -> System.out.println("New subscriber got: " + n));
// 继续发射
notificationSink.tryEmitNext(new Notification("msg-4"));
// 新订阅者收到 msg-4
五、Sinks.One:单值发射器
5.1 基本用法
java
Sinks.One<String> oneSink = Sinks.one();
// 获取 Mono
Mono<String> mono = oneSink.asMono();
// 订阅
mono.subscribe(value -> System.out.println("Got: " + value));
// 从另一个线程发射值
CompletableFuture.runAsync(() -> {
// 模拟异步操作
String result = computeExpensiveResult();
oneSink.tryEmitValue(result);
});
5.2 发射方法
java
// 发射成功值
Sinks.EmitResult result = oneSink.tryEmitValue("hello");
// 发射空(等价于 Mono.empty())
oneSink.tryEmitEmpty();
// 发射错误
oneSink.tryEmitError(new RuntimeException("failed"));
5.3 典型场景:桥接异步回调
java
public Mono<Response> callLegacyService(Request request) {
Sinks.One<Response> sink = Sinks.one();
// 调用回调式 API
legacyService.executeAsync(request, new Callback<Response>() {
@Override
public void onSuccess(Response response) {
sink.tryEmitValue(response);
}
@Override
public void onFailure(Throwable error) {
sink.tryEmitError(error);
}
});
return sink.asMono();
}
5.4 与 Mono.create 的对比
java
// Mono.create 方式(仍然有效)
Mono<Response> mono1 = Mono.create(sink -> {
legacyService.executeAsync(request, new Callback<>() {
public void onSuccess(Response r) { sink.success(r); }
public void onFailure(Throwable e) { sink.error(e); }
});
});
// Sinks.One 方式(更灵活,可在任意时刻发射)
Sinks.One<Response> oneSink = Sinks.one();
Mono<Response> mono2 = oneSink.asMono();
// 可以在任意时间、任意位置调用 oneSink.tryEmitValue(...)
**Sinks.One 的优势:**发射时机不受限于创建时的回调闭包,可以从任何地方、任何时间触发。
六、Sinks.Empty:纯信号发射器
6.1 基本用法
java
Sinks.Empty<Void> emptySink = Sinks.empty();
// 获取 Mono<Void>
Mono<Void> completionSignal = emptySink.asMono();
// 订阅:等待完成信号
completionSignal.subscribe(
null, // 无数据
error -> log.error("Failed", error), // 错误
() -> log.info("Completed!") // 完成
);
// 在某个时刻发出完成信号
emptySink.tryEmitEmpty();
// 或者发出错误
// emptySink.tryEmitError(new RuntimeException("failed"));
6.2 典型场景:通知机制
java
// 场景:通知所有等待者"初始化已完成"
public class InitializationNotifier {
private final Sinks.Empty<Void> initComplete = Sinks.empty();
public Mono<Void> waitForInitialization() {
return initComplete.asMono();
}
public void markInitialized() {
initComplete.tryEmitEmpty();
}
public void markFailed(Throwable error) {
initComplete.tryEmitError(error);
}
}
七、线程安全模型
7.1 各 Sink 的线程安全保证
| Sink 类型 | 线程安全 | 说明 |
|---|---|---|
| unicast().onBackpressureBuffer() | ❌ 单生产者 | 同一时间只能有一个线程发射 |
| multicast().onBackpressureBuffer() | ✅ 多生产者 | 内部序列化,多线程安全 |
| multicast().directAllOrNothing() | ✅ 多生产者 | 内部序列化 |
| multicast().directBestEffort() | ✅ 多生产者 | 内部序列化 |
| replay().*() | ✅ 多生产者 | 内部序列化 |
| Sinks.one() | ✅ 多生产者 | 只有第一个成功发射的生效 |
| Sinks.empty() | ✅ 多生产者 | 只有第一个成功发射的生效 |
7.2 并发发射与 FAIL_NON_SERIALIZED
java
Sinks.Many<String> sink = Sinks.many().unicast().onBackpressureBuffer();
// ❌ 两个线程同时发射 → 其中一个返回 FAIL_NON_SERIALIZED
Thread t1 = new Thread(() -> sink.tryEmitNext("A"));
Thread t2 = new Thread(() -> sink.tryEmitNext("B"));
t1.start();
t2.start();
// 其中一个线程的发射会失败!
解决方案:
java
// 方案 1:使用多生产者安全的 Sink
Sinks.Many<String> safeSink = Sinks.many().multicast().onBackpressureBuffer();
// 方案 2:外部同步
synchronized (sink) {
sink.tryEmitNext(value);
}
// 方案 3:使用 serialize() 包装(如果 Sink 支持)
// 注意:Reactor 的 multicast/replay Sink 内部已经做了序列化
7.3 Sinks.One 的"先到先得"语义
java
Sinks.One<String> oneSink = Sinks.one();
// 两个线程竞争发射
CompletableFuture.runAsync(() -> oneSink.tryEmitValue("first"));
CompletableFuture.runAsync(() -> oneSink.tryEmitValue("second"));
// 只有第一个成功的发射会生效
// 第二个会返回 FAIL_TERMINATED
八、工程实战场景
8.1 事件总线(Event Bus)
java
public class ReactiveEventBus {
private final Sinks.Many<DomainEvent> eventSink =
Sinks.many().multicast().onBackpressureBuffer(1024);
// 发布事件
public void publish(DomainEvent event) {
Sinks.EmitResult result = eventSink.tryEmitNext(event);
if (result.isFailure()) {
log.warn("Failed to publish event: {}", result);
}
}
// 订阅事件
public Flux<DomainEvent> subscribe() {
return eventSink.asFlux();
}
// 订阅特定类型的事件
public <T extends DomainEvent> Flux<T> subscribe(Class<T> eventType) {
return eventSink.asFlux()
.filter(eventType::isInstance)
.cast(eventType);
}
// 关闭事件总线
public void shutdown() {
eventSink.tryEmitComplete();
}
}
8.2 SSE(Server-Sent Events)推送
java
@RestController
public class NotificationController {
private final Sinks.Many<ServerSentEvent<String>> sseSink =
Sinks.many().multicast().onBackpressureBuffer(256);
// SSE 端点
@GetMapping(value = "/notifications", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<String>> streamNotifications() {
return sseSink.asFlux();
}
// 推送通知
@PostMapping("/notifications")
public Mono<Void> pushNotification(@RequestBody Notification notification) {
return Mono.fromRunnable(() -> {
ServerSentEvent<String> event = ServerSentEvent.<String>builder()
.id(UUID.randomUUID().toString())
.event("notification")
.data(notification.getMessage())
.build();
sseSink.tryEmitNext(event);
});
}
}
8.3 WebSocket 消息桥接
java
public class WebSocketBridge {
private final Sinks.Many<String> incomingMessages =
Sinks.many().multicast().onBackpressureBuffer();
public void onMessageReceived(String message) {
incomingMessages.tryEmitNext(message);
}
public void onConnectionClosed() {
incomingMessages.tryEmitComplete();
}
public void onError(Throwable error) {
incomingMessages.tryEmitError(error);
}
public Flux<String> getMessages() {
return incomingMessages.asFlux();
}
}
8.4 生产者-消费者模式
java
public class ReactiveProducerConsumer {
public static void main(String[] args) {
// 创建有界缓冲的 Sink
Sinks.Many<Task> taskQueue = Sinks.many().unicast()
.onBackpressureBuffer(new ArrayBlockingQueue<>(100));
// 生产者
ScheduledExecutorService producer = Executors.newSingleThreadScheduledExecutor();
producer.scheduleAtFixedRate(() -> {
Task task = generateTask();
Sinks.EmitResult result = taskQueue.tryEmitNext(task);
if (result == Sinks.EmitResult.FAIL_OVERFLOW) {
log.warn("Queue full, task dropped: {}", task.getId());
}
}, 0, 10, TimeUnit.MILLISECONDS);
// 消费者
taskQueue.asFlux()
.flatMap(task -> processTask(task).subscribeOn(Schedulers.boundedElastic()), 8)
.subscribe(result -> log.info("Processed: {}", result));
}
}
8.5 状态广播(最新值缓存)
java
public class SystemStatusBroadcaster {
// 只保留最新状态,新订阅者立即获得当前状态
private final Sinks.Many<SystemStatus> statusSink =
Sinks.many().replay().last();
public void updateStatus(SystemStatus newStatus) {
statusSink.tryEmitNext(newStatus);
}
// 新订阅者立即收到最新的 SystemStatus
public Flux<SystemStatus> getStatusStream() {
return statusSink.asFlux();
}
// 获取当前状态(一次性)
public Mono<SystemStatus> getCurrentStatus() {
return statusSink.asFlux().next();
}
}
8.6 多数据源合并
java
public Flux<MarketData> getMarketDataStream() {
// 多个数据源各自有独立的 Sink
Sinks.Many<MarketData> stockSink = Sinks.many().multicast().onBackpressureBuffer();
Sinks.Many<MarketData> forexSink = Sinks.many().multicast().onBackpressureBuffer();
Sinks.Many<MarketData> cryptoSink = Sinks.many().multicast().onBackpressureBuffer();
// 注册到不同的数据提供商
stockProvider.onData(stockSink::tryEmitNext);
forexProvider.onData(forexSink::tryEmitNext);
cryptoProvider.onData(cryptoSink::tryEmitNext);
// 合并所有数据源
return Flux.merge(
stockSink.asFlux(),
forexSink.asFlux(),
cryptoSink.asFlux()
);
}
九、发射结果处理最佳实践
9.1 生产环境的健壮发射
java
public class ReliableEmitter {
private final Sinks.Many<Event> sink;
public ReliableEmitter() {
this.sink = Sinks.many().multicast().onBackpressureBuffer(10_000);
}
public boolean emit(Event event) {
Sinks.EmitResult result = sink.tryEmitNext(event);
switch (result) {
case OK:
return true;
case FAIL_OVERFLOW:
log.warn("Backpressure overflow, event buffered or dropped: {}", event.getId());
return false;
case FAIL_TERMINATED:
log.error("Sink already terminated, cannot emit: {}", event.getId());
return false;
case FAIL_NON_SERIALIZED:
log.warn("Concurrent emission detected for: {}", event.getId());
// 可以使用重试策略
return retryEmit(event);
case FAIL_CANCELLED:
log.info("Sink cancelled, ignoring event: {}", event.getId());
return false;
case FAIL_ZERO_SUBSCRIBER:
log.debug("No subscriber, event dropped: {}", event.getId());
return false;
default:
return false;
}
}
private boolean retryEmit(Event event) {
// 使用 emitNext 带重试 handler
try {
sink.emitNext(event, Sinks.EmitFailureHandler.busyLooping(Duration.ofMillis(10)));
return true;
} catch (Sinks.EmissionException e) {
log.error("Emission failed after retry", e);
return false;
}
}
}
9.2 使用 emitNext + FailureHandler 的声明式风格
java
// 推荐:使用 emitNext 配合内置的 busyLooping 策略
sink.emitNext(value, Sinks.EmitFailureHandler.busyLooping(Duration.ofMillis(100)));
// 或者 FAIL_FAST:失败直接抛 EmissionException
sink.emitNext(value, Sinks.EmitFailureHandler.FAIL_FAST);
十、Sinks 与背压
10.1 有缓冲 Sink 的背压行为
java
// onBackpressureBuffer:内部有队列缓冲
// 当下游消费慢时,数据暂存在内部队列中
Sinks.Many<Integer> buffered = Sinks.many().multicast().onBackpressureBuffer(256);
// 如果队列满 → tryEmitNext 返回 FAIL_OVERFLOW
10.2 无缓冲 Sink 的背压行为
java
// directAllOrNothing:不缓冲
// 如果任何一个订阅者没有 request → 返回 FAIL_OVERFLOW
Sinks.Many<Integer> direct = Sinks.many().multicast().directAllOrNothing();
// directBestEffort:不缓冲
// 对没有 request 的订阅者跳过,其他订阅者正常接收
Sinks.Many<Integer> bestEffort = Sinks.many().multicast().directBestEffort();
10.3 下游配合背压
java
// 下游使用 limitRate 控制请求节奏
sink.asFlux()
.limitRate(32) // 每次只请求 32 个
.flatMap(this::process, 8) // 最多 8 个并发处理
.subscribe();
// 或使用 onBackpressureBuffer/Drop/Latest 在下游缓冲
sink.asFlux()
.onBackpressureBuffer(1000)
.subscribe(this::slowProcess);
十一、Sinks 与 Context 传播
11.1 在 Sink 中访问 Context
java
Sinks.Many<String> sink = Sinks.many().multicast().onBackpressureBuffer();
sink.asFlux()
.flatMap(value -> Mono.deferContextual(ctx -> {
String userId = ctx.getOrDefault("userId", "anonymous");
return Mono.just(value + " for user: " + userId);
}))
.contextWrite(Context.of("userId", "user-123"))
.subscribe(System.out::println);
11.2 注意事项
⚠️ Sinks.Many 的 tryEmitNext 方法本身不携带 Context。Context 是在订阅时由下游传递的。如果需要将 Context 信息附加到数据中,应在数据对象本身中携带。
十二、从 Processor 迁移到 Sinks
12.1 迁移对照表
| 旧 API(已废弃) | 新 API | 说明 |
|---|---|---|
| UnicastProcessor.create() | Sinks.many().unicast().onBackpressureBuffer() | 单订阅者 |
| EmitterProcessor.create() | Sinks.many().multicast().onBackpressureBuffer() | 多订阅者 |
| ReplayProcessor.create() | Sinks.many().replay().all() | 全量重放 |
| ReplayProcessor.create(n) | Sinks.many().replay().limit(n) | 限量重放 |
| ReplayProcessor.createTimeout(d) | Sinks.many().replay().limit(d) | 限时重放 |
| MonoProcessor.create() | Sinks.one() | 单值 |
| DirectProcessor.create() | Sinks.many().multicast().directAllOrNothing() | 无缓冲 |
12.2 迁移示例
java
// ❌ 旧写法(已废弃)
UnicastProcessor<String> processor = UnicastProcessor.create();
processor.onNext("hello");
processor.onComplete();
processor.subscribe(System.out::println);
// ✅ 新写法
Sinks.Many<String> sink = Sinks.many().unicast().onBackpressureBuffer();
sink.tryEmitNext("hello");
sink.tryEmitComplete();
sink.asFlux().subscribe(System.out::println);
java
// ❌ 旧写法
EmitterProcessor<Event> emitter = EmitterProcessor.create();
emmitter.onNext(event);
emmitter.subscribe(handler);
// ✅ 新写法
Sinks.Many<Event> emitter = Sinks.many().multicast().onBackpressureBuffer();
emitter.tryEmitNext(event);
emitter.asFlux().subscribe(handler);
十三、常见陷阱与最佳实践
13.1 ❌ 在 complete 后继续发射
java
Sinks.Many<String> sink = Sinks.many().multicast().onBackpressureBuffer();
sink.tryEmitComplete();
Sinks.EmitResult result = sink.tryEmitNext("too late");
// result == FAIL_TERMINATED ← 数据丢失!
13.2 ❌ Unicast 多订阅者
java
Sinks.Many<String> unicast = Sinks.many().unicast().onBackpressureBuffer();
Flux<String> flux = unicast.asFlux();
flux.subscribe(s -> System.out.println("A: " + s)); // OK
flux.subscribe(s -> System.out.println("B: " + s)); // onError!
13.3 ❌ Multicast 无订阅者时数据丢失
java
Sinks.Many<String> multicast = Sinks.many().multicast().onBackpressureBuffer();
multicast.tryEmitNext("lost message"); // 没有订阅者 → 数据丢弃!
// 之后才有订阅者
multicast.asFlux().subscribe(System.out::println); // 收不到 "lost message"
解决方案:如果需要保留数据,使用 replay() 模式。
13.4 ❌ 忽略 EmitResult
java
// ❌ 危险:不检查返回值
sink.tryEmitNext(importantData); // 可能静默失败!
// ✅ 正确:始终检查
Sinks.EmitResult result = sink.tryEmitNext(importantData);
if (result.isFailure()) {
handleEmitFailure(result, importantData);
}
13.5 ❌ 在单生产者 Sink 上多线程发射
java
Sinks.Many<String> unicast = Sinks.many().unicast().onBackpressureBuffer();
// ❌ 多线程同时发射 → FAIL_NON_SERIALIZED
executor.submit(() -> unicast.tryEmitNext("A"));
executor.submit(() -> unicast.tryEmitNext("B"));
// ✅ 使用多生产者安全的 Sink
Sinks.Many<String> safe = Sinks.many().multicast().onBackpressureBuffer();
13.6 ✅ 最佳实践清单
| # | 实践 | 说明 |
|---|---|---|
| 1 | 始终检查 EmitResult | 不要静默忽略发射失败 |
| 2 | 选择合适的 Sink 类型 | 单订阅者用 unicast,多订阅者用 multicast/replay |
| 3 | 多线程场景用 multicast/replay | 它们内部做了序列化 |
| 4 | 设置合理的缓冲大小 | 避免无界缓冲导致 OOM |
| 5 | 使用 emitNext + FailureHandler | 生产环境比 tryEmitNext 更健壮 |
| 6 | 在适当时机调用 complete | 释放资源,通知下游 |
| 7 | 避免在 complete/error 后发射 | 会返回 FAIL_TERMINATED |
| 8 | 考虑 replay 模式保留历史 | 新订阅者需要历史数据时 |
| 9 | 配合 limitRate 控制消费速率 | 下游慢时避免溢出 |
| 10 | 使用 Sinks.EmitFailureHandler.busyLooping | 处理偶发的并发冲突 |
十四、测试 Sinks
14.1 StepVerifier 测试
java
@Test
void testMulticastSink() {
Sinks.Many<String> sink = Sinks.many().multicast().onBackpressureBuffer();
StepVerifier.create(sink.asFlux())
.then(() -> sink.tryEmitNext("hello"))
.expectNext("hello")
.then(() -> sink.tryEmitNext("world"))
.expectNext("world")
.then(() -> sink.tryEmitComplete())
.verifyComplete();
}
@Test
void testUnicastSink() {
Sinks.Many<Integer> sink = Sinks.many().unicast().onBackpressureBuffer();
// 先发射,后订阅(unicast 会缓冲)
sink.tryEmitNext(1);
sink.tryEmitNext(2);
sink.tryEmitNext(3);
sink.tryEmitComplete();
StepVerifier.create(sink.asFlux())
.expectNext(1, 2, 3)
.verifyComplete();
}
@Test
void testReplaySink() {
Sinks.Many<String> sink = Sinks.many().replay().limit(2);
sink.tryEmitNext("a");
sink.tryEmitNext("b");
sink.tryEmitNext("c");
// 新订阅者只收到最近 2 个
StepVerifier.create(sink.asFlux())
.expectNext("b", "c")
.then(() -> sink.tryEmitComplete())
.verifyComplete();
}
@Test
void testOneSink() {
Sinks.One<String> sink = Sinks.one();
StepVerifier.create(sink.asMono())
.then(() -> sink.tryEmitValue("result"))
.expectNext("result")
.verifyComplete();
}
@Test
void testEmitFailure() {
Sinks.Many<String> sink = Sinks.many().multicast().onBackpressureBuffer();
sink.tryEmitComplete();
Sinks.EmitResult result = sink.tryEmitNext("too late");
assertEquals(Sinks.EmitResult.FAIL_TERMINATED, result);
}
14.2 并发测试
java
@Test
void testConcurrentEmission() throws InterruptedException {
Sinks.Many<Integer> sink = Sinks.many().multicast().onBackpressureBuffer();
int threadCount = 10;
int messagesPerThread = 1000;
CountDownLatch latch = new CountDownLatch(threadCount);
AtomicInteger successCount = new AtomicInteger(0);
for (int t = 0; t < threadCount; t++) {
final int threadId = t;
new Thread(() -> {
for (int i = 0; i < messagesPerThread; i++) {
Sinks.EmitResult result = sink.tryEmitNext(threadId * messagesPerThread + i);
if (result == Sinks.EmitResult.OK) {
successCount.incrementAndGet();
}
}
latch.countDown();
}).start();
}
latch.await(10, TimeUnit.SECONDS);
sink.tryEmitComplete();
// 验证所有消息都被成功发射
assertEquals(threadCount * messagesPerThread, successCount.get());
}
十五、选择决策树
java
需要从命令式代码向响应式流推送数据?
│
├── 推送多个值?
│ ├── 只有 1 个消费者?
│ │ └── Sinks.many().unicast().onBackpressureBuffer()
│ │
│ ├── 多个消费者?
│ │ ├── 需要缓冲(容忍背压)?
│ │ │ └── Sinks.many().multicast().onBackpressureBuffer()
│ │ │
│ │ ├── 不需要缓冲(低延迟)?
│ │ │ ├── 全或无(不容忍丢失)?
│ │ │ │ └── Sinks.many().multicast().directAllOrNothing()
│ │ │ └── 尽力投递(允许掉队)?
│ │ │ └── Sinks.many().multicast().directBestEffort()
│ │ │
│ │ └── 新订阅者需要历史数据?
│ │ ├── 全部历史 → replay().all()
│ │ ├── 最新一条 → replay().last()
│ │ ├── 最近 N 条 → replay().limit(n)
│ │ └── 最近时间窗口 → replay().limit(duration)
│ │
│ └── 生产者线程模型?
│ ├── 单线程 → unicast 或任何(注意并发)
│ └── 多线程 → multicast / replay(线程安全)
│
├── 只推送一个值?
│ └── Sinks.one()
│
└── 只推送完成/错误信号?
└── Sinks.empty()
十六、总结
java
┌──────────────────────────────────────────────────────────────────────────┐
│ Project Reactor Sinks --- 核心知识图谱 │
├──────────────────────────────────────────────────────────────────────────┤
│ │
│ 设计理念: │
│ • 替代已废弃的 Processor API │
│ • 发射端(Sink)与消费端(Flux/Mono)清晰分离 │
│ • 显式的发射结果反馈(EmitResult) │
│ • 明确的线程安全契约 │
│ │
│ 三大入口: │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ Sinks.many() → Sinks.Many<T> → .asFlux() → Flux<T> │ │
│ │ Sinks.one() → Sinks.One<T> → .asMono() → Mono<T> │ │
│ │ Sinks.empty() → Sinks.Empty<T> → .asMono() → Mono<Void> │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
│ Many 的三种模式: │
│ • unicast → 单订阅者,无订阅者时缓冲 │
│ • multicast → 多订阅者,无订阅者时丢弃 │
│ • replay → 多订阅者,保留历史数据 │
│ │
│ 发射 API: │
│ • tryEmitNext / tryEmitComplete / tryEmitError → 返回 EmitResult │
│ • emitNext / emitComplete / emitError → 接受 FailureHandler │
│ │
│ EmitResult: │
│ • OK / FAIL_TERMINATED / FAIL_OVERFLOW / │
│ FAIL_NON_SERIALIZED / FAIL_CANCELLED / FAIL_ZERO_SUBSCRIBER │
│ │
│ 黄金法则: │
│ • 始终检查 EmitResult │
│ • 多线程发射用 multicast/replay │
│ • 需要历史数据用 replay │
│ • 设置合理的缓冲大小 │
│ • 生产环境用 emitNext + FailureHandler │
│ │
└──────────────────────────────────────────────────────────────────────────┘
Sinks API 是 Reactor 3.4 引入的最重要的 API 演进之一。它将"从命令式世界向响应式世界注入数据"这一常见需求,从混乱的 Processor 继承体系中解放出来,提供了一个类型安全、语义清晰、线程安全契约明确的现代方案。
无论是构建事件总线、SSE 推送、WebSocket 桥接,还是简单的异步结果通知,Sinks 都是连接两个世界的最优雅桥梁。