如何处理 Reactive Spring webflux 中的错误

How to handle errors in Reactive Spring webflux

因为有很多方法,例如 onErrorReturn、onErrorResume 等,所以在 Reactive Spring webflux for mono 和 flux 中,应该使用哪个方法来处理错误?

分享我的知识:

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

  • onErrorReturn:return 整个流的回退值 (mono/flux)。例如。如果有 10 个元素的 flux,并且元素 3 发生错误,则 rest 4,5,6... 不会执行,而是考虑 fallback 值。

  • onErrorResume:return 整个流的 Mono/Flux 回退值 (mono/flux)。例如。如果有 10 个元素的 flux,并且元素 3 发生错误,则 rest 4,5,6... 不会执行,而是考虑 fallback 值。

  • onErrorContinue:消耗(错误,数据)并且不会将其拆分。它为错误元素考虑消费者,并为好的元素离开下游链。例如。如果有 10 个元素的通量,并且元素 3 发生错误,则除 3 之外的所有元素(1 到 10)将正常执行,但元素 3 将有不同的执行,如 onErrorContinue

    的消费者中所述
  • doOnError:消耗错误并将其溢出。停止执行流中的其他元素。

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

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

  • 简单:直接考虑预期参数
  • With Exception:考虑预期的参数,如果异常匹配class提供的异常
  • With Predicate: 如果谓词为真,则考虑预期的参数

示例:

  1. onErrorReturn:return回退值
@Test
public void onErrorReturnDirectly_Mono() {
    Mono.just(2)
        .map(i -> i/0) // will produce ArithmeticException
        .onErrorReturn(4)
        .subscribe(num -> log.info("Number: {}", num ));
}

@Test
public void onErrorReturnIfArithmeticException_Mono() {
    Mono.just(2)
        .map(i -> i/0) // will produce ArithmeticException
        .onErrorReturn(ArithmeticException.class, 4)
        .subscribe(num -> log.info("Number: {}", num ));
}

@Test
public void onErrorReturnIfPredicatePasses_Mono() {
    Mono.just(2)
        .map(i -> i/0) // will produce ArithmeticException
        .onErrorReturn(error -> error instanceof ArithmeticException, 4)
        .subscribe(num -> log.info("Number: {}", num ));
}
  1. onErrorResume:return 回退值 Mono/Flux
@Test
public void onErrorResume_Mono() {
    Mono.just(2)
        .map(i -> i/0) // will produce ArithmeticException
        .onErrorResume(error -> Mono.just(4))
        .subscribe(num -> log.info("Number: {}", num ));
}

@Test
public void onErrorResumeIfArithmeticException_Mono() {
    Mono.just(2)
        .map(i -> i/0) // will produce ArithmeticException
        .onErrorResume(
            ArithmeticException.class,
            error -> Mono.just(4)
        )
        .subscribe(num -> log.info("Number: {}", num ));
}

@Test
public void onErrorResumeIfPredicatePasses_Mono() {
    Mono.just(2)
        .map(i -> i/0) // will produce ArithmeticException
        .onErrorResume(
            error -> error instanceof ArithmeticException,
            error -> Mono.just(4)
        )
        .subscribe(num -> log.info("Number: {}", num ));
}
  1. onErrorContinue:消耗(错误,数据)并且不会将其拆分。
@Test
public void onErrorContinue_Mono() {
    Mono.just(2)
        .map(i -> i/0) // will produce ArithmeticException
        .onErrorContinue((error, obj) -> log.info("error:[{}], obj:[{}]", error, obj ))
        .subscribe(num -> log.info("Number: {}", num ));
}

@Test
public void onErrorContinueIfArithmeticException_Mono() {
    Mono.just(2)
        .map(i -> i/0) // will produce ArithmeticException
        .onErrorContinue(
            ArithmeticException.class,
            (error, obj) -> log.info("error:[{}], obj:[{}]", error, obj )
        )
        .subscribe(num -> log.info("Number: {}", num ));
}

@Test
public void onErrorContinueIfPredicatePasses_Mono() {
    Mono.just(2)
        .map(i -> i/0) // will produce ArithmeticException
        .onErrorContinue(
            error -> error instanceof ArithmeticException,
            (error, obj) -> log.info("error:[{}], obj:[{}]", error, obj )
        )
        .subscribe(num -> log.info("Number: {}", num ));
}
  1. doOnError:消耗错误并将其溢出
@Test
public void doOnError_Mono() {
    Mono.just(2)
        .map(i -> i/0) // will produce ArithmeticException
        .doOnError(error -> log.info("caught error"))
        .subscribe(num -> log.info("Number: {}", num ));
}

@Test
public void doOnErrorIfArithmeticException_Mono() {
    Mono.just(2)
        .map(i -> i/0) // will produce ArithmeticException
        .doOnError(
            ArithmeticException.class,
            error -> log.info("caught error")
        )
        .subscribe(num -> log.info("Number: {}", num ));
}

@Test
public void doOnErrorIfPredicatePasses_Mono() {
    Mono.just(2)
        .map(i -> i/0) // will produce ArithmeticException
        .doOnError(
            error -> error instanceof ArithmeticException,
            error -> log.info("caught error")
        )
        .subscribe(num -> log.info("Number: {}", num ));
}
  1. onErrorMap:将一个错误转化为另一个错误
@Test
public void OnErrorMap_Mono() {
    Mono.just(2)
        .map(i -> i/0) // will produce ArithmeticException
        .onErrorMap(error -> new RuntimeException("SomeMathException"))
        .subscribe(num -> log.info("Number: {}", num ));
}

@Test
public void OnErrorMapIfArithmeticException_Mono() {
    Mono.just(2)
        .map(i -> i/0) // will produce ArithmeticException
        .onErrorMap(
            ArithmeticException.class,
            error -> new RuntimeException("SomeMathException")
        )
        .subscribe(num -> log.info("Number: {}", num ));
}

@Test
public void OnErrorMapIfPredicatePasses_Mono() {
    Mono.just(2)
        .map(i -> i/0) // will produce ArithmeticException
        .onErrorMap(
            error -> error instanceof ArithmeticException,
            error -> new RuntimeException("SomeMathException")
        )
        .subscribe(num -> log.info("Number: {}", num ));
}