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

Project Reactor 是Java响应式编程库 ,提供Mono 和Flux 核心类型,支持非阻塞、背压及异步数据流处理 。它是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 都是连接两个世界的最优雅桥梁。

相关推荐
一条小小yu5 小时前
Spring IoC的理解
java·后端·spring
落魄实习生5 小时前
Agent Scope Java 2.x 系列【7】工具使用
java·开发语言·ai
旺仔学长 哈哈5 小时前
springboot钓鱼爱好者交流平台APP设计与实现
java·spring boot·mysql·充电桩管理系统
自强的小白7 小时前
核心功能(Service接口)
java·mybatis
乌暮8 小时前
深入理解 Java 泛型:把「万能盒子」用对、用稳
java·开发语言·后端·学习
大猫和小黄8 小时前
深入理解 Java Optional:从设计哲学到工程实战
java
数据库百宝箱8 小时前
rum&gin索引对比
java·数据库·gin
yychen_java9 小时前
第六篇:Spring AI 实战:将 Java 业务接口封装成企业级 MCP Server
java·人工智能·spring
_upupup9 小时前
异常(C++)
java·开发语言·jvm