领先一步
VMware 提供培训和认证,助您加速进步。
了解更多致响应式 Spring 社区的朋友们!
虽然团队仍在大量开发 3.1 系列,但我们也希望让社区有机会抢先了解未来 3.2 系列的计划。
特别是,3.2.0.RELEASE 的一大亮点是增加了我们一直称之为“错误模式”、“继续模式”或最近更正式的“错误策略”。
这很简单,真的:如果操作符中执行的用户代码中的异常可以恢复,允许序列继续,那会怎么样?
让我们举个例子,假设您有以下方法
public Flux<Integer> divide100By(Flux<Integer> dividers) {
return dividers.map(div -> 100 / div);
}
如果 dividers 源在某个时刻发出 0,那么结果 Flux 将立即因 ArithmeticException 的 onError 信号而终止。
如果源恰好是 Flux.range(0, 10),那么仍然有 9 个完全有效的值可以被映射。
您如何才能做到只忽略这种瞬时异常(且仅忽略这种异常),并给进一步的有效值处理的机会?
在 Reactor 3.1 中,您可以采用一种权宜之计,即使用 flatMap 为每个元素创建一个内部序列,然后将错误恢复操作符应用于这些细粒度序列
public Flux<Integer> divide100By(Flux<Integer> dividers) {
return dividers.flatMap(div -> (1)
Mono.just(100 / div) (2)
.doOnError(e -> { (3)
if (e instanceof ArithmeticException) process(e); (4)
})
.onErrorResume(ArithmeticException.class, e -> Mono.empty()) (5)
);
}
我们使用 flatMap 而不是 map,为每个值生成一个小的内部 Mono
那个 Mono 基本上就是旧的 map 操作……
……并添加了错误恢复。
首先,我们确保在“恢复”之前处理(例如记录)ArithmeticException(且仅处理这些异常)
然后我们使用 onErrorResume 和 Mono.empty() 来有效地忽略结果序列中的异常
这可行,但编写起来有点麻烦(尽管 compose 和 transform 可以帮助共享这类代码)。此外,我们从一个 map 操作符转向了带有几个内部操作符的 flatMap。
由于需要协调多个源,flatMap 比 map 有更多的开销。尽管操作符融合等技术可以减少这种开销,但它仍然存在。
如果我们想进一步减少这种处理的开销,那么困难在于我们现在必须在每个操作符的实现层面进行工作。
链中的每个操作符都必须以某种方式被告知异常应该被捕获但不能通过 onError 传播,而是以不同的方式处理。这是一个很大的改变,而且是横向的!
请注意,这在概念上听起来像是一个 filter,但用于错误。就像 filter 一样,这意味着在一个 onNext 抛出异常后继续处理其源的操作符也应该从其源请求至少一个额外的元素。
尽管它可以隔离到一个特殊的执行路径中,但这仍然是对操作符的复杂核心实现更改。
然后是 API 的问题:作为构造函数参数或 Flux 中带有“错误恢复”布尔值的额外重载来设置它会非常繁琐……我们真的需要将 Flux API 中的方法数量翻倍以支持该功能吗?
幸运的是,不需要:从 3.1 开始,我们有了 Context,它是将此类信息传播到链中每个(Reactor)操作符的好方法。
所以这就是我们为错误策略功能所采取的道路
支持只添加到**特定操作符**(map、filter、flatMap、handle 等)。这些操作符有一个特殊的 javadoc 标签来记录这一事实。
通过在给定 Flux 的 Context 中放置一个特殊键来激活该功能
每个支持的操作符在其 onNext 实现中都有一个特殊路径,该路径会检查该键,如果找到,则会改变其处理错误的方式。
该功能通过 errorStrategyContinue() API 暴露给用户
它可以更细粒度:可以过滤哪些异常可以恢复,并为这些已恢复的异常设置自定义处理程序。
重要
需要记住的一点是,由于这是通过 Context 激活的,因此该功能遵循与 Context 相同的传播规则。例如,它将在 flatMap 中的内部序列上激活。如果不需要,请在 flatMap 中使用 errorStrategyStop() 返回默认行为(这不会超出 flatMap 的范围)。**它也会向后传播,在 errorStrategyXXX 之前激活操作符**。
这是我们的上一个示例在使用错误策略后的样子
public Flux<Integer> divide100By(Flux<Integer> dividers) {
return dividers.map(div -> 100 / div) (1)
.errorStrategyContinue(ArithmeticException.class, (2)
(error, value) -> process(error)); (3)
}
回到简单的 map
我们只从 ArithmeticException 中恢复
我们将此类异常传递给我们的内部处理程序(请注意,我们还可以访问导致异常的原始值,如果有的话)
我们刚刚发布了一个 3.2.0.M1 里程碑 [1],主要包含错误策略功能,我们希望您进行测试?
注意
这是一个非常横向的更改,即使您不打算使用它,使用该构件运行您的测试也很有价值,以验证如果您没有显式使用 errorStrategyContinue(),您应该不会看到任何行为变化(因为该功能包含在特定的执行路径中)。
为了获取里程碑版本,将 repo.spring.io/milestone 仓库添加到您的 Maven 或 Gradle 构建配置中,并获取 reactor-core 3.2.0.M1 构件
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>3.2.0.M1</version>
</dependency>
对于该功能的 API,目前还没有定论。因此,如果您有任何反馈,请通过在 GitHub 上提出问题 或在 Gitter 上讨论该功能来告诉我们。
与此同时,祝您编码愉快!
1. PS:此里程碑提前发布,而 3.1.3.RELEASE 仍在开发中,因此请注意,它不包含 3.1.3 和后续 3.1.x 版本中的所有修复。