Sinks API:Project Reactor 中程序化发射数据的现代方案

Project Reactor 是Java响应式编程库 ,提供MonoFlux 核心类型,支持非阻塞、背压及异步数据流处理 。它是Spring WebFlux 的基础,适用于构建高并发低延迟的微服务与事件驱动应用,遵循Reactive Streams规范。

1. 响应式编程入门:从阻塞困境到数据流之美

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 并发模型的核心引擎

8. 响应式流中的错误处理:Project Reactor 异常治理全体系

9. Sinks API: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 都是连接两个世界的最优雅桥梁

相关推荐
程序员贺加贝1 小时前
轻制造SaaS的生产闭环建模-BOM工单领料报工质检与入库
java·设计模式·架构
敲个大西瓜2 小时前
Spring Could Alibaba 核心面试题
java·后端·spring
骇客野人3 小时前
Java SSO 统一认证方案
java·开发语言
START_GAME3 小时前
MSSQL$SQL2016
java·服务器·前端
ydd1001003 小时前
字符串转换整数
java
会编程的吕洞宾3 小时前
DeepAgents In Action学习(Second)
android·java·学习
梦梦代码精4 小时前
基于UniApp+Vue3+ThinkPHP 8,这套知识付费系统的架构设计有点东西
java·低代码·docker·uni-app·开源·php
-凌凌漆-4 小时前
【freertos】Task创建(v2)
java·开发语言·算法
牛油果子哥q4 小时前
C++内存模型与深浅拷贝万字详解:栈堆静态内存布局、深浅拷贝底层差异、内存泄漏根治、拷贝崩溃踩坑、手写深拷贝实战
java·开发语言·c++
带多刺的玫瑰6 小时前
Leecode#4刷题之寻找两个正序数组的中位数
java·前端·算法