Reactor - JVM 异步应用的基础

工程 | Jon Brisbin | 2013年5月13日 | ...

我们很高兴宣布,经过长时间的内部孵化,我们将发布一个用于 JVM 异步应用的基础框架,我们称之为 Reactor。它为 Java、Groovy 和其他 JVM 语言提供了抽象,使构建事件驱动和数据驱动的应用更加容易。它也非常快。在普通硬件上,使用最快的非阻塞 Dispatcher 可以每秒处理超过 15,000,000 个事件。还提供其他分发器(dispatcher),为开发者提供多种选择,从线程池风格、长时间运行的任务执行到非阻塞、大容量任务分发。GitHub 仓库在这里 https://github.com/reactor/reactor

正如其名称所示,Reactor 深受 著名的 Reactor 设计模式 的影响。但它也受到其他事件驱动设计实践以及多年来开发的几种优秀的基于 JVM 的解决方案的影响。Reactor 的目标是将这些思想和模式浓缩成一个简单且可重用的基础,使事件驱动编程变得更加容易。

Reactor 的抽象为开发者提供了一套工具,不仅可以开发,还可以以更高效地利用系统资源的方式 组合 应用——这在云环境中运行时尤为重要——并减少或消除目前困扰大多数异步应用的嵌套回调的“意大利面条式”代码(恰如其分地称为 “回调地狱”)。

Reactor 有什么用?

虽然由于其固有的灵活性,你可以让 Reactor 做很多事情,但它真正的设计目标是作为一个基础框架,用于那些在执行相对小的无状态异步处理块时需要高吞吐量的应用。现代应用中非人工生成的数据的巨大数量很容易超过传统的单线程阻塞设计模式。从生成位置信息流的移动应用到发送大量几何数据的自动化制造机器,再到挖掘实时日志以生成业务指标的大数据应用:现代数据驱动应用需要比传统的命令式、阻塞式应用通常能提供的更好的资源利用率和更高的吞吐量。

这就是为什么 Spring XD 项目(以及其他几个 Spring 生态系统项目,如 Spring Integration 和 Spring Batch)打算利用 Reactor。将 Reactor 的异步分发与 Spring Integration 基于 NIO 的 TCP 适配器结合使用,以提供高吞吐量的 syslog 和 MQTT 摄取,这只是一个例子。

Selector、Consumer 和 Event

Reactor 的 reactor-core 模块中最基础的三个组件是 SelectorConsumerEvent。可以使用 SelectorConsumer 分配给 ReactorSelector 是一种简单的抽象,可在查找要针对 Event 调用的 Consumer 时提供灵活性。提供了一系列默认的 Selector。从普通字符串到正则表达式,再到 Spring MVC 风格的 URL 模板

这里有一些示例代码,展示了使用 Reactor 创建事件驱动应用有多么容易


// This helper method is like jQuery’s.
// It creates a Selector instance so you don’t have 
// to construct one using 'new Selector("parse")'
import static reactor.Fn.$;

Reactor reactor = R.create();

// Register interest in events published to key "parse"
reactor.on($("parse"), new Consumer<Event<String>>() {
  public void call(Event<String> ev) {
    service.handleEvent(ev);
  }
});

// Send an event to this Reactor and trigger all actions 
// that match the given Selector
reactor.notify("parse", Fn.event("Hello World!"));
 

致 Groovy,带着爱

Reactor 分发版中包含一个名为 reactor-groovy 的模块。它包含 Groovy 绑定,提供了富有表现力的语法,使用 @CompileStatic 进行编译时检查,将 Closure 隐式转换为 Consumer,以及其他 Groovy 特有的节省时间的功能。


// Assign a Closure as a Consumer
reactor.on($('hello')) { Event<String> ev ->
  if(ev.headers['specialHeader']) { // Events can have metadata
    doSomethingWith(ev.data)
  }
}

// Use Groovy helpers for notify
reactor.notify for: 'hello', data: 'Hello World!', specialHeader: 'specialValue'
 

最好的部分是:我们无需牺牲性能来实现这一目标。适用于 Java 代码的 JVM 优化同样适用于 Groovy 代码。我们持续(有些人可能会说“执着地”)对分发代码进行微基准测试,使其尽可能快,并为 Java 和 Groovy 用户提供最高的吞吐量。

你准备好 Java 8 了吗?Reactor 随时待命

Reactor 的设计也对 Java SE 8 的 Lambda 表达式 友好,Reactor 中的许多组件可以用 Lambda 替换,使你的 Java 代码更简洁。我们还发现,使用 Java 8 的 Lambda(和方法引用)可以获得稍高的吞吐量。当 Java 8 正式发布时,你无需等待 Reactor 支持它。它会“just work”(开箱即用)。


// Use a POJO as an event handler
class Service {
  public <T> void handleEvent(Event<T> ev) {
    // handle the event data
  }
}

@Inject
Service service;

// Use a method reference to create a Consumer<Event<T>>
reactor.on($("parse"), service::handleEvent);

// Notify consumers of the 'parse' topic that data is ready
// by passing a Supplier<Event<T>> in the form of a lambda
reactor.notify("parse", () -> {
  slurpNextEvent()
});
 

函数式、命令式、回调式或 Promise:你来选择

Executor、Event Loop、Actor、分布式——在事件驱动编程中最重要的用例之一(任务分发)有很多实现形式。Reactor 支持多种风格的事件驱动编程。除了传统的面向回调的 Consumer 接口外,Reactor 还对 Promises/A+ 规范 进行了诠释,使得处理延迟值(deferred values)和消费者(consumers)变得非常容易。

嵌套回调虽然在像 Java 这样的命令式语言中直观易用,但当应用复杂度增加时,维护起来就会变得困难。Reactor 的 ComposablePromise 的核心是轻松组合操作。你可以将一个 Composable 链接成一系列操作,用于转换值、将数据保存到数据存储、聚合值等等。而且由于它们可以链式调用,你可以在纯 Java 中以类型安全的方式完成所有这些操作。这里有一个快速示例,展示了如何使用 Composable 轻松链接一系列异步执行的任务,这些任务在数据流通过 Composable 时对其进行转换和过滤


Composable<Integer> c = new Composable<>()
  .map(new Function<Integer, Integer>() {
    public Integer apply(Integer i) {
      return i % 2;
    }
  })
  .filter(new Function<Integer, Boolean>() {
    public Boolean apply(Integer i) {
      return i == 0;
    }
  })
  .consume(new Consumer<Integer>() {
    public void accept(Integer eveni) {
      // work with only even numbers here
    }
  });
 

Composable 的每个步骤都是一个潜在的异步任务。对 mapfilterconsume 的调用会指定在前一步骤的值可用时执行的任务——无需陷入回调地狱。

分发(Dispatching)

对于任何分发问题都没有万能的解决方案。Reactor 提供了不同风格的 Dispatcher,因为每个异步应用在不同部分有不同的分发需求。例如,在摄取海量数据时,Reactor 会希望使用基于 著名的 Disruptor RingBuffer 的高速非阻塞 Dispatcher。但如果 Reactor 正在向数据库服务器发出阻塞调用或将大量数据存储到 S3 中,它会希望利用吞吐量较低的工作线程池 Dispatcher。Reactor 提供了多种选择,以便你可以为特定任务选择合适的工具。

如果内置的 Dispatcher 实现不符合你的需求,那么 Reactor 提供了一个坚实的基础,你可以在此基础上构建自己的、针对特定问题领域定制的 Dispatcher

Grails,遇见事件;事件,遇见 Grails

Grails 是一个用于 JVM 的全栈 Web 应用框架。尽管其代码库成熟且拥有蓬勃发展的社区,但 Grails 仍然面临 新的架构挑战。事件通过 platform-core 插件 引入 Grails。但事件功能如此强大,以至于它真正应该属于核心;因此,从 2.3 版本开始,Grails 应用将内置一个极其强大但易于使用、基于约定的事件 API,其外观和功能与当前 platform-core 插件中的实现非常相似。此事件 API 将建立在 Reactor 的基础之上。

将事件集成到 Grails 中的目标是针对新型开发——特别是“实时 Web”和大规模、非阻塞应用开发。结合异步 GORM 特性,事件 API 将证明是一个强大的盟友。访问大数据存储的复杂查询——因此需要很长时间处理——可以在结果准备好时通过直接将结果推送到浏览器来响应。

一个充满热情的社区至关重要

在接下来的几个月里,我们将努力为 SpringOne 做准备,届时我们将重点展示我们的大数据、快速、可扩展的数据解决方案。如果你还没有计划参加,你绝对应该参加!我们将举办一个 关于 Reactor 的专题讨论会,以及如何使用它创建大规模、高吞吐量的事件驱动应用。

但没有你,我们无法做到!这项努力只有在你帮助我们为 JVM 上的大数据、快速、事件驱动应用开发创建一个充满热情和活力的社区才能成功。如果你感兴趣,请查看 GitHub 上的源代码,查看 reactor-quickstart 中的一些示例代码,报告你发现的任何问题,在 StackOverflow 上使用 #reactor 标签 提问有关 Reactor 的问题,加入 reactor-framework Google Groups 邮件列表 的讨论,或者 fork 仓库 帮助添加功能、调整以获得更高吞吐量以及贡献新想法。

我们期待在那里见到你!

订阅 Spring 新闻通讯

订阅 Spring 新闻通讯,保持联系

订阅

保持领先

VMware 提供培训和认证,助你加速进步。

了解更多

获得支持

Tanzu Spring 通过一项简单的订阅,为 OpenJDK™、Spring 和 Apache Tomcat® 提供支持和二进制文件。

了解更多

即将举办的活动

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

查看全部