响应式流(Reactor)最佳实践指南

这是基于工程实际用法与 Reactor 核心逻辑整合的响应式流(Reactor)最佳实践指南

核心原则:数据在流动,算子在加工

在响应式编程中,不要把它看作方法的调用,而要看作数据流经一条流水线,每个算子都是流水线上的一个加工站。


1. 核心转换算子:处理"数据本身"

算子 最佳实践场景 关键点
map 同步对象转换、字段提取、简单计算。 必须是纯内存操作,严禁 IO 或阻塞。
flatMap 异步操作串联(查库、HTTP、调用其他 Mono)。 "拆包"操作,将嵌套的 Mono<Mono<T>> 压平为 Mono<T>
cast 类型强转。 map 强转更安全,类型不符时自动抛出异常中断。
defaultIfEmpty 流为空时提供默认值。 类似 SQL 的 NVL,用于处理可选配置缺失。

2. 组合与并行:处理"多路数据汇合"

算子 最佳实践场景 关键点
zipWith / zipWhen 并行获取多个异步结果并组合。 避免嵌套 flatMap,常用于同时查主表和关联表。
collectList Flux<T> 聚合成 Mono<List<T>> 需要一次性拿到所有结果(如批量校验、列表返回)时使用。

3. 副作用与观察:处理"非数据逻辑"

算子 最佳实践场景 关键点
doOnNext 打印日志、调试、给可变对象设值(Fill)。 不改变流中的数据,只"偷看"一眼。
doOnError 记录异常日志、触发告警。 异常依然会向下传递,除非配合 onErrorResume
doOnSuccess 操作成功后的通知。 常用于 Mono<Void> 场景,如保存成功后发通知。

4. 流程控制:处理"分支与过滤"

算子 最佳实践场景 关键点
filter 根据条件丢弃数据。 条件不满足时,流会变成"空流"(Empty)。
switchIfEmpty 兜底逻辑。上游无数据时切换到另一个 Mono。 常用于"查不到就抛异常"或"查不到就返回默认对象"。
take(1) / next() 只要第一个结果。 类似 SQL 的 LIMIT 1,减少不必要的计算和传输。

5. 错误处理:响应式的"Try-Catch"

  • onErrorResume(e -> ...) : 捕获并恢复。比如查库失败,返回一个空对象或降级方案,让流程继续。
  • onErrorMap(e -> ...) : 异常转换 。把底层技术异常(如 SQLException)转换成业务异常(BusinessException)。
  • retry(n) : 重试。网络抖动或临时性故障时使用。

6. 终结算子:处理"结束与返回"

  • then() : 忽略前面的数据,等前面执行完后返回 Mono<Void>。常用于保存操作后直接结束。
  • thenReturn(value): 忽略前面的数据,等前面执行完后返回一个指定值。常用于保存后返回 ID。
  • thenMany(Flux): 将一个 Mono 转换为 Flux,常用于"先执行初始化,再返回流"的场景。

💡 避坑指南与高频疑问

  1. 什么时候用 just
    • 当你手里有一个普通对象,想把它塞进响应式流开始时,用 Mono.just(obj)
  2. 为什么有时候要用 defer
    • 如果你希望每次订阅时才执行创建逻辑(如获取当前时间、生成随机数),用 Mono.defer(() -> Mono.just(...))
  3. 遇到 Mono<Void> 怎么办?
    • Void 表示"只关心完成,不关心结果"。如果你想接着干别的事,用 .then(Mono.newTask()) 衔接。
  4. 调试技巧:
    • 在链式调用中插入 .log(),它会把流的生命周期(订阅、请求、onNext、onComplete)全部打印到控制台,是排查问题的神器。

🚀 终极口诀(建议背诵)

同步转换用 map,异步交互 flatMap
并行组合 zipWith,类型强转用 cast
偷看设值 doOnNext,判空兜底 switchIfEmpty
可选缺省 defaultIfEmpty,异常降级 onErrorResume
只要结果用 then,收集列表 collectList
调试跟踪加个 log,遇到 Voidthen 接。

🛠️ 典型业务组合拳示例

  • 查不到就报错
    .findById(id).switchIfEmpty(Mono.error(new BusinessException("不存在")))
  • 保存后返回 ID
    .save(entity).thenReturn(entity.getId())
  • 并行查两个表并合并
    .zipWhen(user -> roleService.findByUserId(user.getId()))
  • 补全默认值并保存
    .doOnNext(req -> { if(req.getStatus()==null) req.setStatus(0); }).flatMap(this::save)
相关推荐
何雷 — 智能网联汽车1 天前
Harness Engineering学习七 —— AGENTS.md文件编写的最佳实践
markdown·最佳实践·harness·agents.md·智能体编程
刚子编程4 天前
C# Join 深度解析:参数顺序、多表关联与空值处理最佳实践
开发语言·c#·最佳实践·join·多表关联·空值处理
小杰3124 天前
网络框架源码阅读技巧
服务器·网络·c++·reactor·zlmediakit·zltoolkit
UrSpecial13 天前
基于C语言与Epoll的Reactor模型
c语言·网络编程·reactor·epoll
猫吻鱼17 天前
【笔记03】【Reactor 响应式编程② - 事务编程】
笔记·reactor·webflux·jooq
zs宝来了1 个月前
Netty Reactor 模型:Boss、Worker 与 EventLoop
reactor·netty·源码解析·线程模型·eventloop
BIBI20491 个月前
VirtualBox 7.x 安装 Ubuntu 24 及增强功能配置、克隆虚拟机教程
linux·windows·ubuntu·环境搭建·安装教程·最佳实践·virtualbox
李庆政3701 个月前
Reactor-core 响应式编程 spring-boot-starter-webflux
java·spring boot·reactor·响应式编程·reactor-core
沉木渡香1 个月前
【AI协作开发实践指南:从25%到50%+效率提升的实战方法论】编程领域
人工智能·ai编程·最佳实践·工程化·开发效率·前后端协作