Reactor-Core 3.2 版本里程碑 1 预览

发布 | Simon Baslé | 2018年1月16日 | ...

致响应式 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)
      );
}
  1. 我们使用`flatMap`而不是`map`,为每个值生成一个小的内部`Mono`。

  2. 该`Mono`基本上是旧的`map`操作……

  3. ……增加了错误恢复。

  4. 首先,我们确保在“恢复”之前处理(例如,记录)`ArithmeticException`(并且只有这些)。

  5. 然后,我们使用带有`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)
}
  1. 返回简单的`map`

  2. 我们只从`ArithmeticException`中恢复。

  3. 我们将此类异常传递给我们的内部处理程序(请注意,如果有的化,我们还可以访问导致异常的原始值)。

使用里程碑 1 进行测试

我们刚刚发布了`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 版本中的所有修复。

获取 Spring Newsletter

通过 Spring Newsletter 保持联系

订阅

领先一步

VMware 提供培训和认证,以加速您的进步。

了解更多

获取支持

Tanzu Spring在一个简单的订阅中提供对 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件。

了解更多

即将举行的活动

查看 Spring 社区中所有即将举行的活动。

查看全部