如何处理 Reactive Spring webflux 中的错误

Rav*_*oni 23 java project-reactor spring-webflux

由于有很多方法onErrorReturn,例如onErrorResume等,那么哪一个是正确的方法来处理 Reactive Spring WebFlux 中单声道和通量的错误?

Rav*_*oni 71

分享我的知识:

\n

总共提供了六种方法来处理错误,下面讨论其中五种:

\n
    \n
  • onErrorReturn:返回整个流(mono/flux)的后备值。例如,如果有\xe2\x80\x99s 10个元素的通量,并且元素3上发生错误,则剩余的4,5,6\xe2\x80\xa6将不会执行\xe2\x80\x99,而是将执行回退值予以考虑。

    \n
  • \n
  • onErrorResume:返回整个流(mono/flux)的 Mono/Flux 的后备值。例如,如果有\xe2\x80\x99s 10个元素的通量,并且元素3上发生错误,则剩余的4,5,6\xe2\x80\xa6将不会执行\xe2\x80\x99,而是将执行回退值予以考虑。

    \n
  • \n
  • onErrorContinue:消耗(错误,数据)并且不将其拆分。它会考虑错误元素的消费者,而将下游链保留为好的元素。例如,如果有\xe2\x80\x99s 10个元素的通量,并且元素3上发生错误,则除3之外的所有元素(1到10)都将正常执行,但元素3将具有消费者中提到的不同执行onErrorContinue 的

    \n
  • \n
  • doOnError:消耗错误并将其溢出。停止执行流中的其他元素。

    \n
  • \n
  • onErrorMap:将一个错误转换为另一个错误。停止执行流中的其他元素。

    \n
  • \n
\n

所有这五种方法都有 3 种变体,

\n
    \n
  • 简单:直接考虑预期的参数
  • \n
  • 有异常:如果异常与提供的异常类匹配,则考虑预期参数
  • \n
  • 使用谓词:如果谓词产生 true,则考虑预期的参数
  • \n
\n

例子:

\n
    \n
  1. onErrorReturn:返回后备值
  2. \n
\n
@Test\npublic void onErrorReturnDirectly_Mono() {\n    Mono.just(2)\n        .map(i -> i/0) // will produce ArithmeticException\n        .onErrorReturn(4)\n        .subscribe(num -> log.info("Number: {}", num ));\n}\n\n@Test\npublic void onErrorReturnIfArithmeticException_Mono() {\n    Mono.just(2)\n        .map(i -> i/0) // will produce ArithmeticException\n        .onErrorReturn(ArithmeticException.class, 4)\n        .subscribe(num -> log.info("Number: {}", num ));\n}\n\n@Test\npublic void onErrorReturnIfPredicatePasses_Mono() {\n    Mono.just(2)\n        .map(i -> i/0) // will produce ArithmeticException\n        .onErrorReturn(error -> error instanceof ArithmeticException, 4)\n        .subscribe(num -> log.info("Number: {}", num ));\n}\n
Run Code Online (Sandbox Code Playgroud)\n
    \n
  1. onErrorResume:返回 Mono/Flux 的后备值
  2. \n
\n
@Test\npublic void onErrorResume_Mono() {\n    Mono.just(2)\n        .map(i -> i/0) // will produce ArithmeticException\n        .onErrorResume(error -> Mono.just(4))\n        .subscribe(num -> log.info("Number: {}", num ));\n}\n\n@Test\npublic void onErrorResumeIfArithmeticException_Mono() {\n    Mono.just(2)\n        .map(i -> i/0) // will produce ArithmeticException\n        .onErrorResume(\n            ArithmeticException.class,\n            error -> Mono.just(4)\n        )\n        .subscribe(num -> log.info("Number: {}", num ));\n}\n\n@Test\npublic void onErrorResumeIfPredicatePasses_Mono() {\n    Mono.just(2)\n        .map(i -> i/0) // will produce ArithmeticException\n        .onErrorResume(\n            error -> error instanceof ArithmeticException,\n            error -> Mono.just(4)\n        )\n        .subscribe(num -> log.info("Number: {}", num ));\n}\n
Run Code Online (Sandbox Code Playgroud)\n
    \n
  1. onErrorContinue:消耗(错误,数据)并且不将其拆分。
  2. \n
\n
@Test\npublic void onErrorContinue_Mono() {\n    Mono.just(2)\n        .map(i -> i/0) // will produce ArithmeticException\n        .onErrorContinue((error, obj) -> log.info("error:[{}], obj:[{}]", error, obj ))\n        .subscribe(num -> log.info("Number: {}", num ));\n}\n\n@Test\npublic void onErrorContinueIfArithmeticException_Mono() {\n    Mono.just(2)\n        .map(i -> i/0) // will produce ArithmeticException\n        .onErrorContinue(\n            ArithmeticException.class,\n            (error, obj) -> log.info("error:[{}], obj:[{}]", error, obj )\n        )\n        .subscribe(num -> log.info("Number: {}", num ));\n}\n\n@Test\npublic void onErrorContinueIfPredicatePasses_Mono() {\n    Mono.just(2)\n        .map(i -> i/0) // will produce ArithmeticException\n        .onErrorContinue(\n            error -> error instanceof ArithmeticException,\n            (error, obj) -> log.info("error:[{}], obj:[{}]", error, obj )\n        )\n        .subscribe(num -> log.info("Number: {}", num ));\n}\n
Run Code Online (Sandbox Code Playgroud)\n
    \n
  1. doOnError:消耗错误并将其溢出
  2. \n
\n
@Test\npublic void doOnError_Mono() {\n    Mono.just(2)\n        .map(i -> i/0) // will produce ArithmeticException\n        .doOnError(error -> log.info("caught error"))\n        .subscribe(num -> log.info("Number: {}", num ));\n}\n\n@Test\npublic void doOnErrorIfArithmeticException_Mono() {\n    Mono.just(2)\n        .map(i -> i/0) // will produce ArithmeticException\n        .doOnError(\n            ArithmeticException.class,\n            error -> log.info("caught error")\n        )\n        .subscribe(num -> log.info("Number: {}", num ));\n}\n\n@Test\npublic void doOnErrorIfPredicatePasses_Mono() {\n    Mono.just(2)\n        .map(i -> i/0) // will produce ArithmeticException\n        .doOnError(\n            error -> error instanceof ArithmeticException,\n            error -> log.info("caught error")\n        )\n        .subscribe(num -> log.info("Number: {}", num ));\n}\n
Run Code Online (Sandbox Code Playgroud)\n
    \n
  1. onErrorMap:将一个错误转换为另一个错误
  2. \n
\n
@Test\npublic void OnErrorMap_Mono() {\n    Mono.just(2)\n        .map(i -> i/0) // will produce ArithmeticException\n        .onErrorMap(error -> new RuntimeException("SomeMathException"))\n        .subscribe(num -> log.info("Number: {}", num ));\n}\n\n@Test\npublic void OnErrorMapIfArithmeticException_Mono() {\n    Mono.just(2)\n        .map(i -> i/0) // will produce ArithmeticException\n        .onErrorMap(\n            ArithmeticException.class,\n            error -> new RuntimeException("SomeMathException")\n        )\n        .subscribe(num -> log.info("Number: {}", num ));\n}\n\n@Test\npublic void OnErrorMapIfPredicatePasses_Mono() {\n    Mono.just(2)\n        .map(i -> i/0) // will produce ArithmeticException\n        .onErrorMap(\n            error -> error instanceof ArithmeticException,\n            error -> new RuntimeException("SomeMathException")\n        )\n        .subscribe(num -> log.info("Number: {}", num ));\n}\n
Run Code Online (Sandbox Code Playgroud)\n

  • 轻微的措辞问题,“onErrorReturn”将返回一个后备“值”,而“onErrorResume”将返回一个后备“Producer”,可以是“Flux”或“Mono”或任何已实现的“Producer”。 (5认同)
  • 但要小心 onErrorContinue,当上游通量无法抵御错误时,它可能会产生奇怪的行为(请参阅 [apidoc](https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Flux.html #onErrorContinue-java.util.function.BiConsumer-)了解详细信息) (2认同)