Reactor响应式编程中Flux和FluxSink

从「基础定义→核心关系→实战示例→使用场景」四个维度,用通俗易懂的方式讲解,介绍Reactor响应式编程中FluxFluxSink的核心概念、关系及实际用法。

一、核心概念:先搞懂「是什么」

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消息后发射)

总结

  1. 核心定位Flux是「响应式流容器」,负责元素传输和处理;FluxSink是「手动发射器」,负责向Flux中动态发射元素/信号;
  2. 创建方式FluxSink只能通过Flux.create()/Flux.generate()获取,是操作Flux的「手动接口」;
  3. 关键特性:Flux支持惰性执行、背压、链式操作;FluxSink支持异步发射、背压策略配置、错误/完成手动触发;
  4. 使用原则 :固定数据源用Flux.fromXxx()(如fromIterable/fromSupplier),动态/手动发射用Flux.create()+FluxSink。

这个组合是Reactor中「自定义响应式流」的核心,在实时数据推送、异步事件处理、非响应式代码适配等场景中必不可少。

相关推荐
以太浮标2 小时前
华为eNSP模拟器综合实验之- 通过流策略实现Vlan内二/三层隔离
网络·网络协议·华为
flashier2 小时前
ESP32学习笔记_WiFi(3)——HTTP
网络·笔记·单片机·网络协议·学习·http·esp32
桌面运维家2 小时前
vDisk考场环境网络瓶颈怎么定位?快速排查指南
运维·服务器·网络
Cisco_hw_zte2 小时前
挂载大容量磁盘【Linux系统】
linux·运维·服务器
谢怜822 小时前
计算机网络第四章网络层
网络·计算机网络
腾科IT教育2 小时前
华为认证报考常见问题解析
运维·服务器·华为认证·hcia
_运维那些事儿2 小时前
GitLabCI-CD入门
运维·ci/cd·容器·云计算·k8s·运维开发
杜子不疼.2 小时前
【Linux】Ext系列文件系统(一):文件系统的初识
linux·运维·服务器
zhangzeyuaaa2 小时前
SQL Server删除重复数据
运维·sqlserver