onErrorResume适用于外部服务临时不可用时fallback到缓存或降级值等场景,不适用于仅记录日志、抛异常或终止流;它只响应上游onError信号,不捕获subscribe中unchecked异常。

onErrorResume 什么时候该用,什么时候不该用
它不是 try-catch 的响应式平替,而是「错误发生后继续发新序列」的声明式补救手段。如果你只是想记录日志、抛出包装异常或终止流,onErrorResume 就是错的选择——该用 doOnError 或 onErrorMap。
典型适用场景:外部服务临时不可用时 fallback 到缓存、降级值或另一个 Mono;用户权限校验失败时 fallback 到游客视图。
- 想吞掉错误并发出默认值?用
onErrorResume+Mono.just(...) - 想根据错误类型走不同 fallback?必须显式判断
instanceof或用onErrorResume(Throwable.class, ...)重载 - 想在 fallback 里再发网络请求?确保 fallback 的 Mono 本身也做了超时和错误防护,否则可能链式雪崩
为什么 onErrorResume 不捕获所有异常
它只对上游 Publisher 主动发出的 onError 信号生效,不处理 subscribe 过程中抛出的 unchecked exception(比如 lambda 里空指针),也不处理 onSubscribe 阶段的异常。这类问题会直接炸掉整个订阅链,根本进不到 onErrorResume。
- 常见现象:
java.lang.NullPointerException出现在 stack trace 顶部,但下游onErrorResume完全没触发 - 原因:lambda 执行时抛异常 → Reactor 调用
subscriber.onError()→ 但此时可能还没走到你写的onErrorResume操作符位置 - 解决办法:把易错逻辑包进
Mono.fromCallable(...)或加doOnNext前置校验,别让异常穿透到操作符外层
onErrorResume 和 onErrorResumeWith 的关键区别
名字只差一个 With,行为却完全不同:onErrorResume 接收的是固定值或简单函数,而 onErrorResumeWith 接收的是返回 Mono 或 Flux 的函数——它允许你动态构造替代流,且这个流可以异步、可组合、可再错误处理。
立即学习“Java免费学习笔记(深入)”;
-
onErrorResume(e -> Mono.just("fallback")):同步返回确定值,轻量,适合简单降级 -
onErrorResumeWith(e -> callCacheService().timeout(Duration.ofSeconds(1))):真正做一次异步 fallback 调用,支持超时、重试、嵌套错误处理 - 注意:如果
onErrorResumeWith返回的 Mono 自己又报错,默认会向上传播,不会自动再进当前的onErrorResumeWith,得自己套一层
容易被忽略的线程上下文丢失问题
onErrorResume 及其变体默认在上游失败时所处的线程上执行 fallback 逻辑。如果你的 fallback 依赖 ThreadLocal(比如 MDC 日志上下文、事务传播、用户认证信息),它大概率拿不到——因为错误可能发生在 IO 线程池(如 elastic),而你的主线程上下文早没了。
- 现象:fallback 里打的日志没有 traceId,或 SecurityContext 为空
- 解法一:用
publishOn(Schedulers.boundedElastic())显式切回有上下文的调度器(需提前把上下文传进去) - 解法二:改用
onErrorResumeWith+Mono.subscriberContext()拿上下文,再手动注入到 fallback 流中 - 最稳妥的做法:别在 fallback 里依赖 ThreadLocal,改用 Reactor 的
Context传递必要信息
复杂点从来不在怎么写 onErrorResume,而在于错误发生那一刻,你是否还知道「谁触发的」、「在什么环境下失败的」、「fallback 是否可信」——这些信息一旦断掉,降级就变成了盲降。










