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 的原因。(譯註:原文為intend to take advantage of Reactor,考量上下文,翻譯為打算利用Reactor較為自然)。將 Reactor 的非同步分派與 Spring Integration 中基於 NIO 的 TCP 配接器結合,以提供高吞吐量的 syslog 和 MQTT 攝取只是一個例子。

Selectors、Consumers 和 Events

Reactor 的 reactor-core 模組中最基礎的三個組件是 SelectorConsumerEventConsumer 可以通過使用 Selector 分配給 ReactorSelector 是一個簡單的抽象化,用於在尋找要為 Event 叫用的 Consumer 時提供彈性。一系列預設的選擇器是可用的。從純字串到正規表示式到 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 也被設計為與 Java SE 8 的 lambda 表達式 友好相處,Reactor 中的許多組件都可以用 lambda 表達式替換,使您的 Java 程式碼更加簡潔。我們也發現,使用 Java 8 lambda 表達式 (和方法參考) 會產生稍微更高的吞吐量。當 Java 8 正式發佈 (GA) 時,您不必等待 Reactor 來支援它。它將 Just Work (tm)。


// 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、Distributed - 事件驅動程式設計中最重要用例之一:任務分派,有很多種形式。Reactor 支援多種風格的事件驅動程式設計。除了傳統的面向回呼的 Consumer 介面之外,Reactor 還對 Promises/A+ 規範 進行了解釋,這使得處理延遲值和消費者變得非常容易。

巢狀回呼雖然在像 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 的呼叫會分配任務,以便在前一步驟的值可用時執行任務 - 無需回呼地獄。

分派

對於任何分派問題都沒有銀彈。Reactor 提供了不同樣式的 Dispatcher,因為每個非同步應用程式在應用程式的不同部分都有不同的分派需求。例如,當攝取潮水般的資料時,Reactor 會希望使用基於 著名的 Disruptor RingBuffer 的高速非阻塞 Dispatcher。但是,如果 Reactor 正在向資料庫伺服器發出阻塞呼叫或在 S3 中儲存資料 blob,它會希望利用吞吐量較低的 worker pool Dispatcher。Reactor 提供了多種選項,因此您可以為工作選擇合適的工具。

如果內建的 Dispatcher 實現不符合您的需求,那麼 Reactor 提供了一個堅實的基礎,您可以在此基礎上建構自己的 Dispatcher,使其針對您的問題領域量身定制。

Grails,遇見 Events,Events,遇見 Grails

Grails 是一個適用於 JVM 的全堆疊 Web 應用程式框架。憑藉其成熟的程式碼庫和蓬勃發展的社群,Grails 仍然面臨 新的架構挑戰。事件通過 platform-core 外掛程式 被引入 Grails。但是事件非常強大,以至於此功能真的應該屬於核心;因此從 2.3 版開始,Grails 應用程式將具有內建、極其強大但易於使用、基於慣例的 Events API,它看起來與 platform-core 外掛程式中的目前實作非常相似。此 Events API 將建立在 Reactor 基礎之上。

將事件整合到 Grails 中的目標是針對新型開發 - 特別是「即時 Web」和高規模、非阻塞應用程式開發。結合非同步 GORM 功能,Events API 將被證明是一個強大的盟友。存取大數據儲存區的複雜查詢 (因此需要很長時間才能處理) 可以在結果準備就緒時做出反應,方法是將它們直接推送到瀏覽器。

充滿熱情的社群至關重要

在接下來的幾個月裡,我們將努力為 SpringOne 做準備,屆時我們許多大型、快速且可擴展的資料解決方案將發揮主導作用。如果您還沒有計劃參加,您絕對應該參加!我們將舉辦 關於 Reactor 的會議,以及您如何使用它來創建高規模、高吞吐量的事件驅動應用程式。

但是沒有您,我們做不到!只有當您幫助我們為 JVM 上大型、快速、事件驅動的應用程式開發創建一個充滿熱情和積極參與的社群時,這項努力才會成功。如果您有興趣,請查看 GitHub 上的原始程式碼,在 reactor-quickstart 中查看一些範例程式碼,回報您發現的任何問題,在 StackOverflow 上使用 hashtag #reactor 詢問有關 Reactor 的問題,加入 reactor-framework Google Groups 電子郵件列表 上的討論,或 fork 儲存庫 以幫助添加功能、調整事物以獲得更高的吞吐量,並貢獻新的想法。

我們很期待在那裡見到您!

取得 Spring 電子報

隨時掌握 Spring 電子報的最新資訊

訂閱

領先一步

VMware 提供培訓和認證,以加速您的進展。

了解更多

取得支援

Tanzu Spring 在一個簡單的訂閱中提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位檔案。

了解更多

即將到來的活動

查看 Spring 社群中所有即將到來的活動。

檢視全部