本书定位
你很可能已经用过 Flux.just(1, 2, 3).map(x -> x * 2).subscribe(),甚至能在生产环境把 WebFlux 跑起来。但如果你被问到下面这些问题,还能给出可验证的答案吗?
request(n)信号是在哪条线程被处理的?上一个onNext还没处理完,下一个request会不会把它覆盖掉?flatMap的无序合并和concatMap的顺序保证,到底是在订阅时决定的还是运行时决定的?onBackpressureDrop()丢下元素时,onNext总共被调用了几次?limitRate(10)发送的请求序列是什么?- 你自己写的
Publisher能不能通过 Reactive Streams TCK(Technology Compatibility Kit)全部 38 个测试用例?
如果答不上来,说明你停留在「会用」阶段。本书的目标是把「会用」推进到「知道每条规则在什么条件下成立」——从 Reactive Streams 规范的四接口 43 条规则出发,用手写 Publisher 验证每一条信号契约,再用 Project Reactor 的内建能力解决工程问题。
本书不承诺什么:
- 不是框架使用说明书——我们不展开 Spring WebFlux 的自动配置、Reactor Netty 的 Channel 实现(留给卷二)。
- 不是分布式流处理入门——我们不展开 Kafka Streams 的拓扑设计、Flink 的 Checkpoint 机制(留给卷三)。
- 不是 JMM 或 AQS 源码解析——已有《Java 并发编程》三卷覆盖。
本书许诺什么:读完能对一条数据在响应式流中的完整生命周期做出可验证的判断,而不是背操作符名称。
读者画像与前置知识
- 前置:Java 语法、Lambda、Stream API、
ExecutorService用法;了解 NIO 的 Channel/Buffer/Selector 概念更佳。 - 读者画像:
- 写过业务代码但背压总调不对的后端开发
- 想了解响应式原理而非只记操作符的人
- 其他语言(JavaScript/Rust/Kotlin)转 Java、想对照响应式实现的工程师
- 准备面试需要串起 RS 规范 → Reactor 实现 → 背压策略知识链的人
三卷路线
| 卷 | 书名 | 核心能力 | 输出 |
|---|---|---|---|
| 卷一 | 《Reactive Streams 与背压原理》 | 规范 + 实现 + 策略 | 能手写合规 Publisher,能判断背压策略适用场景 |
| 卷二 | 《系统构建与数据访问》 | Reactor Netty + WebFlux + R2DBC | 能建响应式网关与数据管道 |
| 卷三 | 《流处理引擎与实时计算》 | Kafka Streams + Flink | 能搭实时指标与事件处理系统 |
三卷共享同一个贯穿项目 eventd(事件驱动演示系统),v1、v2、v3 分别对应三卷的验收目标。
环境清单
| 组件 | 版本 |
|---|---|
| JDK | Temurin 25.0.5+8(javac --release 25) |
| Maven | 3.9.12 |
| JUnit Jupiter | 5.14.4 |
| Project Reactor | BOM 2025.0.8 |
| RxJava | 3.1.12 |
| RS TCK | 1.0.4 |
本机口径:
Apple M1 Pro,10 核,32 GB,macOS 26.3 (25D125)
Temurin 25.0.5+8
学习路径
路径 A:零基础顺序通读(推荐)
按章顺序阅读,每章内部按 1→2→3 顺序。预计 3–4 周完成。
- 先读 序 → 前言,确认环境可运行
eventd-v1骨架。 - 第一部分(第 1 章) 建立规范概念:RS 四个接口 + 43 条规则。
- 第二部分(第 2–3 章) 手写实现:同步/异步 Publisher、Subscriber、Processor、操作符链。
- 第三部分(第 4–7 章) 进入 Project Reactor:Mono/Flux 创建、变换、组合、错误处理、调度器、背压策略。
- 第四部分(第 8–10 章) 延伸:Hot/Cold、Sinks、RxJava 3 对照。
- 第五部分(第 11 章) 验证:RS TCK 合规测试。
- 第六部分(第 12 章) 调试:生命周期钩子、Hooks、装配时 vs 订阅时。
- 第七部分(第 13–14 章) 验收:
eventd v1完整演示 + 性能基线。 - 附录 作为速查手册。
路径 B:有 Reactor 经验,想补原理
跳过第 4–7 章的操作符,直接从 第 1 章(RS 规范) → 第 2–3 章(手写实现) → 第 7 章(背压策略) → 第 8–10 章(Hot/Cold + Sinks + RxJava) → 第 11 章(TCK) → 第 13–14 章(eventd 验收)。预计 2 周。
路径 C:只看代码与实验
读 前言(环境)→ 第 2–3 章(手写实现)→ 第 13 章(eventd 架构 + 传感器模拟器 + 背压面板)→ 第 14 章(性能基线)。只看可运行的代码,跳过理论推导。预计 1 周。
全书目录
前置
| # | 篇 | 标题 | 一句话 | 页 | 说明 |
|---|---|---|---|---|---|
| 1 | 序 | 从回调地狱到背压契约 | 响应式不是「更好的回调」,而是一组有约束的信号契约 | 无代码 | 立场声明 |
| 2 | 前言 | 环境、项目与读法 | JDK 25、Reactor BOM 2025.0.8、eventd-v1 骨架 | mvn -version | 环境确认 |
| 3 | 目录 | 目录与学习路径(本页) | 三条路径、十四章依赖、与卷二卷三衔接 | 无代码 | 你在这里 |
第一部分:Reactive Streams 规范(第 1 章)
| 节 | 篇 | 标题 | 一句话 | 页 |
|---|---|---|---|---|
| 1.1 | 异步编程的演进 | 回调、Promise、CompletableFuture 的局限;为什么需要「流」 | CompletableFuture vs Flux 对照 | |
| 1.2 | RS 规范:四个接口 | Publisher/Subscriber/Subscription/Processor;43 条规则概述 | JDK Flow 接口源码 | |
| 1.3 | 规范规则逐条分析 | onSubscribe 先调用、request 信号、取消与完成、空 Publisher | 规则表 + 违反示例 |
第二部分:从零实现 Publisher(第 2–3 章)
| 节 | 篇 | 标题 | 一句话 | 页 |
|---|---|---|---|---|
| 2.1 | 同步 Publisher 最小实现 | 单值同步 Publisher、subscribe 流程、request(n) 同步响应 | SyncSinglePublisher 完整类 | |
| 2.2 | 异步 Publisher 与背压信号 | 异步生产、request 累积、Executor 调度、取消传播 | AsyncRangePublisher 完整类 | |
| 2.3 | Subscriber 实现与信号契约 | onSubscribe 保存 Subscription、request/cancel 线程安全 | LoggingSubscriber 完整类 | |
| 3.1 | Processor:桥接 | Processor 同时实现两者;编码转换、日志、指标 | MapProcessor 完整类 | |
| 3.2 | 操作符链:map、filter、take | 懒订阅、操作符返回新 Publisher、request 信号转发 | 手写 map/filter/take | |
| 3.3 | 调度切换:subscribeOn 与 publishOn | 订阅线程 vs 发射线程;信号不能乱序 | 实跑证明差异 |
第三部分:Project Reactor 核心(第 4–7 章)
| 节 | 篇 | 标题 | 一句话 | 页 |
|---|---|---|---|---|
| 4.1 | Mono 与 Flux | Mono | Mono.just vs Flux.just 对照 | |
| 4.2 | 转换操作符:map、flatMap、concatMap | 同步变换 vs 异步展开;flatMap 无序、concatMap 有序 | 完整代码 + 输出顺序 | |
| 4.3 | 组合操作符:zip、merge、combineLatest | 配对 vs 先 emit 先赢 vs 最新值;空流与错误传播 | 三种组合场景 | |
| 5.1 | 错误处理 | onErrorReturn、onErrorResume、retry 策略 | 模拟网络失败 + 重试日志 | |
| 5.2 | 指数退避重试 | retryWhen + Flux.interval;固定 vs 指数退避 | Retry.backoff 配置与输出 | |
| 5.3 | 超时与取消传播 | timeout(Duration)、取消信号向上游传播、doOnCancel | 慢 Publisher + timeout | |
| 6.1 | 调度器 | single/elastic/parallel/immediate;线程名与边界 | 四种调度器线程名输出 | |
| 6.2 | BoundedElastic | 有界弹性调度器;与 ExecutorService 的关系 | 大量 sleep 任务行为 | |
| 6.3 | 自定义调度器 | Scheduler.Worker 生命周期、任务排队、取消粒度 | LoggingScheduler 自定义 | |
| 7.1 | 背压策略:Buffer | 无界/有界队列、溢出策略、钩子 | 快生产 + 慢消费 + buffer 溢出 | |
| 7.2 | 背压策略:Drop | onBackpressureDrop / onBackpressureLatest | drop 与 latest 元素数量对比 | |
| 7.3 | 背压策略:Error 与速率控制 | onBackpressureError、limitRate(n)、limitRequest(n) | limitRate(10) 请求序列分析 |
第四部分:Hot 与 Cold、Sinks、多播(第 8–10 章)
| 节 | 篇 | 标题 | 一句话 | 页 |
|---|---|---|---|---|
| 8.1 | Cold Publisher | 每次订阅重放;多次订阅独立数据流 | 两次订阅同一 Flux.range | |
| 8.2 | Hot Publisher | publish().refCount()、share();late subscriber 只能收后续 | ConnectableFlux connect 时机 | |
| 8.3 | refCount、autoConnect 与 cache | 内存与重放权衡 | 三种模式对照 | |
| 9.1 | Sinks:程序化发射 | Sinks.many().unicast/multicast/replay;线程安全发射 | Sinks.Many 发射与订阅 | |
| 9.2 | Sinks 多播 | onBackpressureBuffer / .directBestEffort | 多播 + 不同背压策略 | |
| 9.3 | Sinks 重放 | replay().limit(n) / .limit(Duration) / .latest() | 重放缓存命中与失效 | |
| 10.1 | RxJava Subject | Publish/Behavior/Replay/Async Subject;与 Sinks 对照 | 四种 Subject 行为对比 | |
| 10.2 | ConnectableObservable | RxJava publish + connect / refCount;与 Reactor 对照 | 两个库并行展示 | |
| 10.3 | RxJava 3 与 Reactor 迁移 | API 映射表、共同陷阱、何时选哪个 | 同一段逻辑双写 |
第五部分:RS TCK 合规验证(第 11 章)
| 节 | 篇 | 标题 | 一句话 | 页 |
|---|---|---|---|---|
| 11.1 | TCK 环境搭建 | Publisher/Subscriber/Processor Verification;TestNG 7.3.0 驱动 | mvn test 跑通 TCK | |
| 11.2 | Publisher 合规测试 | 38 个用例(22 个 required);信号顺序、背压、空 Publisher | Flux.range 包装实测 29 通过、9 跳过 | |
| 11.3 | Subscriber 与 Processor 合规测试 | Whitebox/Blackbox/IdentityProcessor Verification | PublishProcessor 实测 30 通过、14 失败、24 跳过 |
第六部分:调试与生命周期(第 12 章)
| 节 | 篇 | 标题 | 一句话 | 页 |
|---|---|---|---|---|
| 12.1 | 生命周期钩子 | doOnSubscribe/Next/Error/Complete/Cancel/Request | 完整链路上的钩子输出 | |
| 12.2 | 调试:Hooks 与检查点 | onOperatorDebug、checkpoint 订阅点追踪 | 异常栈定位操作符 | |
| 12.3 | 装配时 vs 订阅时 | 操作符链是蓝图、订阅才是启动 | log() 装配 vs 订阅打印 |
第七部分:eventd v1 验收(第 13–14 章)
| 节 | 篇 | 标题 | 一句话 | 页 |
|---|---|---|---|---|
| 13.1 | eventd v1 架构 | eventd-core + eventd-app 模块划分、依赖关系 | 模块图 + dependency:tree | |
| 13.2 | 传感器模拟器 | Poisson 到达、可配置速率、request(n) 响应 | 不同速率下消费速率对比 | |
| 13.3 | 背压演示面板 | 统计 request 累积、onNext 处理时间、buffer/drop 数量 | 运行后输出统计表 | |
| 14.1 | Project Reactor 重写 | 同一套逻辑:Flux.create + BoundedElastic + onBackpressureBuffer | 双版本并行展示 | |
| 14.2 | 性能基线 | 吞吐量、订阅开销、调度延迟分位数、资源测量方法 | System.nanoTime 自测量 | |
| 14.3 | v1 验收清单 | TCK 全绿、三种背压策略演示、模块 README | 检查清单 |
附录
| 附录 | 篇 | 标题 | 用途 |
|---|---|---|---|
| A | 操作符速查 | 分类表、操作符对照、适用场景 | 日常速查 |
| B | 调度器选择指南 | 四种场景 → 推荐调度器 → 反例 | 选型决策 |
| C | 背压规则清单 | RS 规范背压规则 + Reactor 实现映射 | 规范对照 |
| D | CompletableFuture 迁移指南 | CompletableFuture → Mono/Flux 映射 | 迁移参考 |
| E | RS TCK 方法参考 | 全部测试方法列表与说明 | TCK 开发 |
关于代码与实验
卷一所有代码在 eventd-v1 多模块 Maven 工程中:
eventd-v1/
├── eventd-core/ # RS 规范实现(自写 Publisher/Subscriber/Processor)
└── eventd-app/ # 背压演示应用(依赖 eventd-core + reactor)
- 卷一只引入 reactor-core 和 rxjava,不引入 Spring、Netty、R2DBC(留给卷二/卷三)。
- 每节代码用
javac --release 25编译,完整项目用mvn -q -DskipTests compile。 - 所有「本机输出」来自同一台机器:
Apple M1 Pro,10 核,32 GB,macOS 26.3 (25D125),Temurin 25.0.5+8。
章节依赖关系
以下依赖图说明「读第 N 章前必须先读哪些章」。箭头表示「依赖」。
序 → 前言 → 目录(本页)
↓
第 1 章(RS 规范)
↓
第 2 章(Publisher)→ 第 3 章(Processor/操作符)
↓ ↓
第 4 章(Mono/Flux)←───────────┘
↓
第 5 章(错误处理)→ 第 6 章(调度器)→ 第 7 章(背压策略)
↓
第 8 章(Hot/Cold)← 第 9 章(Sinks)← 第 10 章(RxJava)
↓
第 11 章(TCK) 第 12 章(调试)
↓ ↓
第 13 章(eventd 架构)
↓
第 14 章(性能基线 + 验收)
- 第 1 章是根:不读第 1 章,第 2–3 章的「Subscriber 必须先收到 onSubscribe」等规则会缺乏上下文。
- 第 4–7 章是主干:掌握 Reactor 核心后才能理解 Hot/Cold 的「订阅即启动」语义。
- 第 10 章(RxJava)可跳过:如果你只用 Reactor、不关心 RxJava 对照,直接从第 9 章跳到第 11 章。
- 第 11 章(TCK)可独立:有 TestNG 或其他测试框架基础即可直接跳着读,但自己写测试时需要回头参考第 2–3 章的实现。
与已有 Java 书稿的关系
| 已有书稿 | 本卷如何衔接 | 不重复什么 |
|---|---|---|
| 《Java 编程入门》 | Lambda、Stream API 是基础;本卷把 Stream 的「惰性求值」推进到「背压控制」 | Stream 的基础用法(filter/map/collect) |
| 《Java 高级编程》 | JMM、虚拟线程是理解调度器的铺垫;本卷在 §6.1 引用 | JMM happens-before、AQS 源码 |
| 《Java 并发编程·线程与内存模型》 | ExecutorService、线程池是 §6.2 BoundedElastic 的原型;ForkJoin 与 parallelStream 提到但不展开 | 线程状态迁移、锁优化 |
| 《Spring Boot 高级》第 8 章 | WebFlux 背压概述(3 节);本卷从 RS 规范写到实现细节(14 章) | Spring 容器启动、自动配置 |
| 《Netty 高性能网络编程》 | Reactor 模式是 Netty EventLoop 的设计来源;本卷只引用、不拆源码 | ByteBuf、ChannelPipeline 源码 |
| 《Java 服务端通信工程》 | gRPC 流式调用(留到卷三衔接);本卷只给一句话边界 | 服务发现、限流熔断 |
| 《Java 消息中间件》三卷 | Kafka Streams(卷三);本卷不涉及 | Producer/Consumer API、分区日志 |
验收标准
读完卷一并完成全部实验后,你应该能:
- 手写一个通过 RS TCK 的 Publisher:Flux.range 包装或手写 Publisher,38 个用例中 21 个 required 通过、1 个 required 因未提供失败 Publisher 跳过(默认总计 29 通过、9 跳过)。
- 解释任意一条 RS 规范规则:例如「§3.6:Subscription 取消后,额外的 request(long n) 必须是 NOP」为什么必须成立。
- 为给定场景选择合适的背压策略: Producer 比 Consumer 快 100 倍时选 drop还是 buffer?内存紧张时选 error 还是 latest?
- 阅读一段 Reactor 链式代码并预测输出顺序:给定
Flux.just(1,2).flatMap(i -> Mono.just(i).delayElement(Duration.ofMillis(i*10))).subscribeOn(Schedulers.boundedElastic()),说出 onNext 的顺序和所在线程。 - 将一段 CompletableFuture 代码迁移为响应式流:保留错误恢复、超时、重试语义。
术语速查(阅读前必知)
| 术语 | 英文 | 含义 |
|---|---|---|
| 背压 | Backpressure | 消费者通过 request(n) 控制生产者发射速率的机制 |
| 冷发布者 | Cold Publisher | 每次订阅独立启动数据流(如 Flux.range) |
| 热发布者 | Hot Publisher | 数据流独立于订阅存在,late subscriber 只能收到后续元素 |
| 装配期 | Assembly time | 操作符链的构建阶段(还未订阅) |
| 订阅期 | Subscription time | subscribe() 被调用后的执行阶段 |
| 信号 | Signal | onSubscribe/onNext/onError/onComplete/onCancel/request 的统称 |
阅读导航
上一篇:前言:环境、项目与读法
下一篇:卷一从 1.1 异步编程的演进 开始,或返回书稿书架 选择其他卷。
相关阅读
| 主题 | 推荐阅读 |
|---|---|
| Reactive Streams 规范 | reactive-streams.org |
| Project Reactor 文档 | projectreactor.io/docs |
| RxJava 文档 | github.com/ReactiveX/RxJava |
| Reactor 背压指南 | Project Reactor Reference Guide §8 |
| 《Java 并发编程》 | 本站「Java 并发基础」系列 |
| 《Netty 高性能网络编程》 | 本站「java-netty」系列 |
| 《Java 消息中间件》 | 本站「java-mq-*」系列 |
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。