WebFlux onErrorContinue 和 onErrorResume使用详解

独立使用

在 Project Reactor 中,onErrorResumeonErrorContinue 都是处理流中异常的常用方法,但它们在流的生命周期控制应用场景上有本质区别:

  1. onErrorResume (降级/备选方案)

当流中发生错误时,onErrorResume 会捕获异常并切换 到一个新的 Publisher(备选流),原有的流会在此处终止

  • 行为 :类似于 Java 中的 try-catch 块中 catch 后的 return fallbackValue

  • 流的状态:上游流发生错误后不再发送后续元素,转而由你提供的备选流发送数据并正常结束。

  • 示例代码

    复制代码
    Flux.just(1, 2, 0, 4)
        .map(i -> 10 / i) // 当 i 为 0 时触发异常
        .onErrorResume(e -> Flux.just(-1, -2)) // 发生错误后,原有流结束,开始发送 -1, -2
        .subscribe(System.out::println); 
    // 输出:10, 5, -1, -2
  1. onErrorContinue (跳过/继续执行)

onErrorContinue 允许你在发生错误时丢弃 导致错误的元素,并继续处理流中的后续元素。

  • 行为 :类似于在 for 循环中使用 try-catch 并执行 continue

  • 流的状态 :流不会终止。它会针对当前错误元素执行回调逻辑(如打日志),然后请求上游发送下一个元素。

  • 示例代码

    复制代码
    Flux.just(1, 2, 0, 4)
        .map(i -> 10 / i)
        .onErrorContinue((e, val) -> System.err.println("Error on " + val)) // 仅跳过错误元素
        .subscribe(System.out::println);
    // 输出:10, 5, (打印错误日志), 2.5

核心对比总结

特性 onErrorResume onErrorContinue
主要目的 错误恢复/降级(Fallback) 容错处理/跳过(Skip)
流的命运 终止原有的流,切换到新流 维持原有的流,处理后续元素
适用场景 数据库查询失败返回缓存数据 批量处理中某一条记录损坏,不影响其他记录
层级影响 仅影响当前位置之后的链式调用 具有"上溯"特性,会影响上游的操作算子

⚠️ 使用建议

  1. 谨慎使用 onErrorContinue:它会改变上游算子的默认行为(让本该终止的流强行继续),有时会导致难以排查的副作用或资源泄漏。

  2. 局部处理优先 :如果你只想在 flatMap 内部某次调用出错时不影响整个流,建议在 flatMap 内部使用 onErrorResume 返回 Mono.empty(),这比全局使用 onErrorContinue 更安全、更符合响应式语义。

    nurkiewicz.com +1

onErrorResume() 然后 onErrorContinue()

复制代码
public static void main(String... args) {
    Flux.range(1,5)
            .doOnNext(i -> System.out.println("input=" + i))
            .map(i -> i == 2 ? i / 0 : i)
            .map(i -> i * 2)
            .onErrorResume(err -> {
                log.info("onErrorResume");
                return Flux.empty();
            })
            .onErrorContinue((err, i) -> {log.info("onErrorContinue={}", i);})
            .reduce((i,j) -> i+j)
            .doOnNext(i -> System.out.println("sum=" + i))
            .block();
}

输出如下:

复制代码
input=1
input=2
17:47:05.789 [main] INFO com.example.demo.config.TestRunner - onErrorContinue=2
input=3
input=4
input=5
sum=26

这样的结果,你想到了吗?onErrorContinue() 会在 onErrorResume() 得到错误之前处理这个错误。当两个错误处理函数在同一个函数中的时候很明显,但是当你的函数中只有 onErrorResume(),而一些调用者实际上有 onErrorContinue() 时,你的 onErrorResume() 没有被调用的原因可能就不那么明显了。

使用 onErrorResume() 模拟 onErrorContinue()

有些博客建议我们完全不用 onErrorContinue(),且在所有场景中仅用 onErrorResume()。但是上述示例已经展示了它们会产生不同的结果。那我们怎么实现呢?

复制代码
public static void main(String... args) {
    Flux.range(1,5)
            .doOnNext(i -> System.out.println("input=" + i))
            .flatMap(i -> Mono.just(i)
                    .map(j -> j == 2 ? j / 0 : j)
                    .map(j -> j * 2)
                    .onErrorResume(err -> {
                        System.out.println("onErrorResume");
                        return Mono.empty();
                    })
            )
            .reduce((i,j) -> i+j)
            .doOnNext(i -> System.out.println("sum=" + i))
            .block();
}

因此,本质上是将可能在 flatMap 或 concatMap 中抛出错误的操作包装起来,并在其上使用 onErrorResume()。这样,它会产生相同的结果:

复制代码
input=1
input=2
onErrorResume
input=3
input=4
input=5
sum=26

使用 onErrorResume() 和下游的 onErrorContinue() 模拟 onErrorContinue()

有时候,onErrorContinue() 放在调用程序中,您无法控制它。但你仍然需要 onErrorResume()。你该怎么办?

复制代码
public static void main(String... args) {
    Flux.range(1,5)
            .doOnNext(i -> System.out.println("input=" + i))
            .flatMap(i -> Mono.just(i)
                    .map(j -> j == 2 ? j / 0 : j)
                    .map(j -> j * 2)
                    .onErrorResume(err -> {
                        System.out.println("onErrorResume");
                        return Mono.empty();
                    })
                    .onErrorStop()
            )
            .onErrorContinue((err, i) -> {log.info("onErrorContinue={}", i);})
            .reduce((i,j) -> i+j)
            .doOnNext(i -> System.out.println("sum=" + i))
            .block();
}

秘诀是在 onErrorResume() 代码块的末尾添加 onErrorStop() ------这会阻塞 onErrorContinue(),这样它就不会在 onErrorResume() 之前占用错误。尝试删除 onErrorStop(),你会看到 onErrorContinue() 在 onErrorResume 之前弹出。

输出为:

复制代码
input=1
input=2
onErrorResume
input=3
input=4
input=5
sum=26
相关推荐
caishenzhibiao几秒前
期货先行者主图 同花顺期货通指标
java·c语言·c#
BEOL贝尔科技9 分钟前
样本安全存储中还有哪些重要因素需要考虑?如何设置样本安全?
人工智能·安全·数据分析
史呆芬24 分钟前
分布式事务实战:微服务跨服务数据一致性解决方案
java·后端·spring cloud
IT界的老黄牛29 分钟前
限流命中后该怎么办:直接丢、阻塞等待、延迟重投三种姿势的取舍
java·rocketmq·redisson·令牌桶·削峰·分布式限流
thesky12345638 分钟前
27届大模型岗面试准备(十四):多模态大模型 VLM——从视觉编码器到多模态推理的完整链路
人工智能·ai·大模型
一根数据线39 分钟前
BIM建模效率低?试试和AI工具配合使用
人工智能·ai·3d建模·3d模型·bim·ai建模·造形家
怕浪猫1 小时前
2840亿参数只卖白菜价:DeepSeek V4 Flash 正式版上线,Agent 能力暴涨6倍
人工智能·算法
不爱记笔记1 小时前
多模态AI如何理解视频内容?从视觉、语音到语义的三层技术拆解
人工智能·自然语言处理·nlp·音视频·多模态
让学习成为一种生活方式1 小时前
苄基异喹啉生物碱糖基转移酶UGT74AN1晶体--Journal of Agricultural and Food Chemistry
人工智能·算法
犀利豆1 小时前
Claude code tools 研究系列(二)EnterPlanMode
人工智能·后端