
在 reactor 编程中,当链式操作发生异常时,若需基于原始上游数据(如 fileinfo)执行补偿逻辑(如移动文件到 error 目录),直接传递 mono 会导致重复订阅或空值问题;正确做法是通过 flatmap 提前解包数据,使错误处理能直接访问已获取的稳定对象。
在 reactor 编程中,当链式操作发生异常时,若需基于原始上游数据(如 fileinfo)执行补偿逻辑(如移动文件到 error 目录),直接传递 mono 会导致重复订阅或空值问题;正确做法是通过 flatmap 提前解包数据,使错误处理能直接访问已获取的稳定对象。
Reactors 的 Mono 是冷序列(cold publisher),每次订阅都会触发一次上游计算。你原代码中将 fileInfoMono 作为参数传入 onErrorResume,并在 handleAndReturnEmpty 内再次订阅它——这不仅可能重复调用 getFileInfo()(引发竞态、资源泄漏或不一致状态),还因 Mono.empty() 的提前终止导致日志与错误处理逻辑无法按预期执行。
✅ 正确模式:用 flatMap 提前“落地”数据,将异步流转化为基于确定值的后续处理
return getFileInfo(fileName, fileLocation)
.flatMap(fileInfo -> fileReader.read(fileInfo)
.doOnNext(this::log)
.flatMap(this::processFile)
.onErrorResume(throwable -> handleAndReturnEmpty(throwable, fileInfo)));
对应地,handleAndReturnEmpty 方法应接收解包后的 FileInfo 实例,而非 Mono
private Mono<void> handleAndReturnEmpty(Throwable cause, FileInfo fileInfo) {
return Mono.fromRunnable(() -> logError("Failed to process file: " + cause.getMessage()))
.then(errorService.handleUnexpectedError(fileInfo)) // 假设该方法返回 Mono<void>
.then(Mono.empty()); // 返回空完成信号,表示该文件流程终止
}</void></void>
⚠️ 关键注意事项:
- 禁止在错误处理中重新订阅原始 Mono:fileInfoMono 若含副作用(如 HTTP 调用、文件 I/O),重复订阅将导致不可预测行为;
-
确保 handleUnexpectedError 是纯响应式操作:它应返回 Mono
或 Mono extends T>,并能安全消费 fileInfo(例如调用 moveToErrorFolder(fileInfo.getPath())); - doOnNext 不适用于错误路径:它仅在正常数据到达时触发;异常发生时需依赖 onErrorResume/doOnError 配合已解包的数据;
- 如需统一审计日志,可在 flatMap 外层添加 doOnSuccess 或 doFinally,但注意其作用域不覆盖 onError 分支。
? 进阶建议:若错误处理本身也可能失败,可嵌套 onErrorResume 或使用 retryWhen 策略增强健壮性;对于关键业务文件,还可结合 checkpoint() 添加调试标识,快速定位异常源头。
总之,Reactor 的设计哲学是“数据驱动流”,而非“流驱动数据”。让 flatMap 成为你解构和重用上游结果的核心枢纽,既符合响应式契约,又保障了逻辑清晰与执行安全。











