用Reactor抛出异常的正确方法
我是刚接触Reactor和反应式编程的新手。
我目前正在编写类似于以下代码:
Mono.just(userId) .map(repo::findById)
.map(user-> {
if(user == null){
throw new UserNotFoundException();
}
return user;
})
// ... other mappings
这个例子可能很愚蠢,确实有更好的方法来实现这种情况,但是重点是:
throw new
在map
块中使用异常是错误的还是应该将其替换为return Mono.error(new
UserNotFoundException())?
这两种方式有什么实际区别?
回答:
有几种方法可以被视为方便的异常抛出方法:
使用处理元素 Flux/Mono.handle
可以简化可能导致错误或空流的元素处理的一种方法是operator handle
。
以下代码显示了如何使用它来解决问题:
Mono.just(userId) .map(repo::findById)
.handle((user, sink) -> {
if(!isValid(user)){
sink.error(new InvalidUserException());
} else if (isSendable(user))
sink.next(user);
}
else {
//just ignore element
}
})
如我们所见,.handle
操作员需要通过BiConsumer<T,
SynchronousSink<>才能处理元素。在这里,我们的BiConsumer中有两个参数。第一个元素是上游元素,第二个元素是上游元素,SynchronousSink
它帮助我们向下游同步提供元素。这样的技术扩展了提供元素处理的不同结果的能力。例如,如果元素无效,我们可以向该元素提供错误,SycnchronousSync
这将取消上游onError
并向下游产生信号。反过来,我们可以使用相同的handle
运算符来“过滤”
。一旦手柄BiConsumer
被执行并且没有提供任何元素,Reactor会将其视为一种过滤,并将为我们请求其他元素。最后,在元素有效的情况下,我们可以简单地在SynchronousSink#next
下游调用和传播我们的元素,或者在其上应用一些映射,因此这里将handle
作为map
操作符。此外,我们可以安全地使用该运算符,而不会影响性能,并提供复杂的元素验证,例如元素验证或向下游发送错误。
使用#concatMap
+ 抛出Mono.error
在映射期间引发异常的选项之一是替换map
为concatMap
。从本质上讲,concatMap
它的作用几乎相同flatMap
。唯一的区别是concatMap
一次仅允许一个子流。这种行为大大简化了内部实现,并且不影响性能。因此,我们可以使用以下代码来以更实用的方式引发异常:
Mono.just(userId) .map(repo::findById)
.concatMap(user-> {
if(!isValid(user)){
return Mono.error(new InvalidUserException());
}
return Mono.just(user);
})
在上述示例中,如果用户无效,我们将使用返回异常Mono.error
。我们可以使用Flux.error
以下方法对通量执行相同的操作:
Flux.just(userId1, userId2, userId3) .map(repo::findById)
.concatMap(user-> {
if(!isValid(user)){
return Flux.error(new InvalidUserException());
}
return Mono.just(user);
})
注意 ,在两种情况下,我们都返回仅包含一个元素的 冷流 。在Reactor中,在返回的流是冷 标量
流的情况下,有一些优化可以提高性能。因此,建议使用流量/单声道concatMap
+
.just
,empty
,error
其结果是,当我们需要更复杂的映射,这可能与最终return null
或throw new ...
。
因此,如果
repo.findById
返回null,Reactor将为您抛出NullPointerException。
等等,为什么concatMap
比这更好flatMap
?
从本质上讲,flatMap
它旨在合并一次执行的多个子流中的元素。这意味着flatMap在其下应具有异步流,因此它们可能在多个线程上处理数据,也可能是多个网络调用。随后,这种期望对实现产生了很大的影响,因此flatMap
应该能够处理来自多个流的数据Thread
(意味着并发数据结构的使用),如果另一个流耗尽了,则使元素排队(意味着Queue
为每个s
分配额外的内存子流),并且不违反反应式流规范规则(意味着确实非常复杂的实现)。计算所有这些事实以及我们取代平原的事实map
使用Flux/Mono.error
(不改变执行的同步性)引发异常的更方便的方式(同步操作)导致我们不需要这样一个复杂的运算符,并且可以使用更简单concatMap
的异步处理对象一次只能处理一个流,并进行了一些优化以处理标量冷流。
使用抛出异常 switchOnEmpty
因此,在结果为空时引发异常的另一种方法是switchOnEmpty
operator。以下代码演示了如何使用该方法:
Mono.just(userId) .flatMap(repo::findById)
.switchIfEmpty(Mono.error(new UserNotFoundExeception()))
如我们所见,在这种情况下,repo::findById
应该将Mono
of
User
作为返回类型。因此,在User
找不到实例的情况下,结果流将为空。因此,Reactor将调用Mono
指定为switchIfEmpty
parameter
的Alternative 。
照原样抛出您的异常
可以将其视为可读性差的代码或不良做法( ),但是您可以像使用Project
Reactor一样引发异常。即使这样做在某种程度上可能会违反Reactive Streams规范( 从语义的角度来看是
的,因为在幕后的运算符是s Subscriber
链中Subscriber
的a,因此-
从语义上讲,在lambda中引发异常可以映射为引发onNext
违反规范规则2.13的方法中的异常)。但是,由于Reactor会为您捕获引发的异常,然后将其作为onError
信号传播到下游,因此不禁止这样做。
外卖
- 使用
.handle
运算符以提供复杂的元素处理 - 当我们需要在映射过程中引发异常时,请使用
concatMap
+Mono.error
,但是这种技术最适合异步元素处理的情况。 - 当我们已经到位时使用
flatMap
+Mono.error``flatMap
Null
因为返回类型是禁止的,所以您将null
在下游而不是下游使用map``onError``NullPointerException
- 使用
switchIfEmpty
在所有情况下,当你需要发送一个错误信号,如果在调用一些特定的函数的结果与完成 空 流
以上是 用Reactor抛出异常的正确方法 的全部内容, 来源链接: utcz.com/qa/434992.html