从「基础定义→核心关系→实战示例→使用场景」四个维度,用通俗易懂的方式讲解,介绍Reactor响应式编程中Flux和FluxSink的核心概念、关系及实际用法。
一、核心概念:先搞懂「是什么」
1. Flux:响应式流的「容器」
Flux是Reactor框架中最核心的类型之一,代表0到N个元素的异步序列(可以理解为「响应式版的List/Stream」),支持:
- 异步发射元素(next)、完成信号(complete)、错误信号(error);
- 链式调用操作符(map/filter/flatMap等)处理元素;
- 遵循Reactive Streams规范,支持背压(Backpressure),解决生产/消费速率不匹配问题。
核心特点:
- 「惰性执行」:定义好的Flux不会立即执行,只有调用
subscribe()订阅后才会触发元素发射; - 「声明式编程」:关注「做什么」而非「怎么做」,比如
flux.map(x -> x*2)只声明要对元素乘2,不关心执行时机。
2. FluxSink:手动控制流的「阀门」
FluxSink是创建Flux时的手动发射器 (也叫「下沉」),是连接「数据源」和「Flux流」的桥梁。当你需要手动、动态地向Flux中发射元素 (而非通过固定数据源如fromIterable/fromSupplier)时,就需要用FluxSink。
核心作用:
- 手动发射元素:
next(T value); - 手动触发完成:
complete(); - 手动触发错误:
error(Throwable e); - 支持背压策略配置,控制元素发射速率。
二、核心关系:Flux和FluxSink的配合逻辑
FluxSink只能通过Flux.create()或Flux.generate()创建 ,是Flux暴露给开发者的「手动操作接口」,两者的关系可以用一个比喻理解:
Flux= 一根「水管」,负责传输元素(水);FluxSink= 水管的「水龙头」,由你手动控制何时放水(发射元素)、关水(完成)、或触发水管故障(错误)。
调用next/complete/error
发射信号/元素
订阅后执行
开发者
FluxSink
Flux流
消费者subscribe
三、实战示例:从基础到进阶
以下示例基于Reactor 3.5+(主流版本),结合「云边奶茶铺」场景讲解,更贴近实际开发。
前置依赖(Maven)
xml
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>3.5.11</version>
</dependency>
示例1:基础用法(手动发射奶茶新品)
通过Flux.create()创建Flux,用FluxSink手动发射元素:
java
import reactor.core.publisher.Flux;
import reactor.core.publisher.FluxSink;
public class FluxSinkDemo {
public static void main(String[] args) {
// 1. 创建Flux,通过FluxSink手动控制元素发射
Flux<String> newProductFlux = Flux.create(sink -> {
// sink就是FluxSink实例,手动发射元素
sink.next("云桃乌龙");
sink.next("桂花云露");
sink.next("云雾观音");
// 可选:触发错误(注释掉演示正常流程)
// sink.error(new RuntimeException("查询新品失败"));
// 发射完成信号(必须,否则流会一直处于活跃状态)
sink.complete();
// 完成后再发射元素无效
sink.next("云谷燕麦"); // 不会被消费
});
// 2. 订阅Flux(触发流执行)
newProductFlux.subscribe(
product -> System.out.println("收到新品:" + product), // 消费元素
error -> System.err.println("出错:" + error.getMessage()), // 处理错误
() -> System.out.println("新品发射完成") // 处理完成
);
}
}
执行结果:
收到新品:云桃乌龙
收到新品:桂花云露
收到新品:云雾观音
新品发射完成
示例2:进阶用法(异步动态发射)
模拟「实时接收奶茶订单」场景,FluxSink在多线程中异步发射元素:
java
import reactor.core.publisher.Flux;
import reactor.core.publisher.FluxSink;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class AsyncFluxSinkDemo {
public static void main(String[] args) throws InterruptedException {
// 1. 创建Flux,保存FluxSink引用(用于异步发射)
FluxSink<String>[] sinkHolder = new FluxSink[1]; // 数组保存引用(lambda中只能引用final变量)
Flux<String> orderFlux = Flux.create(sink -> {
sinkHolder[0] = sink; // 保存Sink引用
// 配置背压策略:BUFFER(默认,缓存所有元素)
sink.onRequest(n -> System.out.println("消费者请求了" + n + "个元素"));
});
// 2. 订阅Flux(消费订单)
orderFlux.subscribe(
order -> System.out.println("处理订单:" + order),
error -> System.err.println("订单处理出错:" + error),
() -> System.out.println("订单流结束")
);
// 3. 异步线程中动态发射订单(模拟实时下单)
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
executor.scheduleAtFixedRate(() -> {
String order = "订单" + System.currentTimeMillis() + ":云桃乌龙(少糖)";
sinkHolder[0].next(order); // 异步发射元素
}, 0, 1, TimeUnit.SECONDS);
// 4. 运行5秒后停止
Thread.sleep(5000);
executor.shutdown();
sinkHolder[0].complete(); // 触发完成
}
}
执行结果(每1秒输出一个订单):
消费者请求了9223372036854775807个元素
处理订单:订单1735632000000:云桃乌龙(少糖)
处理订单:订单1735632001000:云桃乌龙(少糖)
处理订单:订单1735632002000:云桃乌龙(少糖)
处理订单:订单1735632003000:云桃乌龙(少糖)
处理订单:订单1735632004000:云桃乌龙(少糖)
订单流结束
示例3:背压策略配置(解决生产消费速率不匹配)
FluxSink支持配置背压策略,避免生产者发射过快导致内存溢出:
java
Flux<String> backpressureFlux = Flux.create(sink -> {
// 模拟高速生产元素
for (int i = 0; i < 1000; i++) {
sink.next("奶茶" + i);
}
sink.complete();
}, FluxSink.OverflowStrategy.DROP); // 背压策略:丢弃超出消费者处理能力的元素
// 慢消费(每100ms处理一个元素)
backpressureFlux
.delayElements(java.time.Duration.ofMillis(100))
.subscribe(product -> System.out.println("慢消费:" + product));
常用背压策略:
| 策略 | 作用 |
|---|---|
BUFFER |
缓存所有元素(默认,可能OOM) |
DROP |
丢弃超出消费能力的新元素 |
LATEST |
只保留最新的元素,丢弃旧元素 |
ERROR |
超出时抛出IllegalStateException |
IGNORE |
忽略背压,继续发射(不推荐) |
四、核心使用场景
| 组件 | 核心使用场景 |
|---|---|
Flux |
1. 处理批量异步数据(如查询多个奶茶新品) 2. 链式处理元素(过滤/转换/聚合) 3. 整合多个数据源(concat/merge) |
FluxSink |
1. 手动/动态发射元素(如实时订单、消息推送) 2. 适配非响应式数据源(如回调函数转响应式) 3. 异步事件驱动场景(如监听MQ消息后发射) |
总结
- 核心定位 :
Flux是「响应式流容器」,负责元素传输和处理;FluxSink是「手动发射器」,负责向Flux中动态发射元素/信号; - 创建方式 :
FluxSink只能通过Flux.create()/Flux.generate()获取,是操作Flux的「手动接口」; - 关键特性:Flux支持惰性执行、背压、链式操作;FluxSink支持异步发射、背压策略配置、错误/完成手动触发;
- 使用原则 :固定数据源用
Flux.fromXxx()(如fromIterable/fromSupplier),动态/手动发射用Flux.create()+FluxSink。
这个组合是Reactor中「自定义响应式流」的核心,在实时数据推送、异步事件处理、非响应式代码适配等场景中必不可少。