领先一步
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`(并且只有这些)。
然后,我们使用带有`Mono.empty()`的`onErrorResume`来有效地忽略结果序列中的异常。
这有效,但编写起来有点麻烦(尽管`compose`和`transform`可以帮助将此类代码通用化)。此外,我们从单个`map`操作符转换为了具有多个内部操作符的`flatMap`。
由于需要协调多个数据源,`flatMap`比`map`具有更高的开销。即使像操作符融合这样的技术降低了这种成本,但它仍然存在。
如果我们想进一步降低这种处理的开销,那么困难在于我们现在必须在每个操作符的实现级别工作。
链中的每个操作符都必须以某种方式意识到应该捕获异常,但不应通过`onError`传播,而应以不同的方式处理。这是一个相当大的变化,而且是横向的!
请注意,从概念上讲,这类似于一个`filter`,但是针对错误。像`filter`一样,这意味着在其中一个`onNext`抛出异常后继续处理其源的操作符也应该从其源请求至少一个元素。
即使它可以隔离到一个特殊的执行路径,这仍然是对操作符的复杂核心实现更改。
然后是 API 的问题:将其作为构造函数参数或在`Flux`中添加带有“错误恢复”布尔值的额外重载将非常繁琐……我们真的需要将`Flux` API 中的方法数量加倍来支持此功能吗?
幸运的是,不需要:自从 3.1 版本以来,我们有了`Context`,这是将此类信息传播到链中每个(Reactor)操作符的好方法。
这就是我们为错误策略功能所采用的方法。
仅为**特定操作符**添加了支持(`map`、`filter`、`flatMap`、`handle`等等)。这些操作符具有特殊的 javadoc 标记来记录这一事实。
通过将特殊的键放入其`Context`中来为给定的`Flux`激活此功能。
每个受支持的操作符在其`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. 附注:此里程碑版本是在 3.1.3.RELEASE 仍在开发中时提前发布的,因此请注意,它不包含 3.1.3 和后续 3.1.x 版本中的所有修复。