Spring Web Reactive | 1. Spring WebFlux | 1.1. 概要
Spring WebFluxはなぜ作られたのでしょうか。
理由の一つは、少ないスレッドとハードウェア資源で同時実行を処理するために、ノンブロッキングWebスタックが必要だったことです。Servlet 3.1はノンブロッキングI/O APIを提供しましたが、同期的なFilterやServlet、ブロッキングするgetParameterやgetPartとは異なります。Nettyなどのノンブロッキングランタイムに共通する新しいAPIが必要になりました。
もう一つの理由は関数型プログラミングです。Java 5のアノテーションとJava 8のラムダ式により、CompletableFutureやReactiveXのように非同期ロジックを宣言的に構成できます。Spring WebFluxはアノテーション付きコントローラーに加えて関数型Webエンドポイントも提供します。
1.1.1. Reactiveの定義
Reactiveとは変化に反応するプログラミングモデルです。ノンブロッキング処理は、待機せずに処理完了やデータ到着の通知に反応します。
重要な仕組みの一つがノンブロッキングのバックプレッシャーです。同期コードではブロッキング呼び出しが自然に呼び出し元を待機させます。ノンブロッキングコードでは、高速なproducerがconsumerを圧倒しないようにイベント速度を制御する必要があります。
Reactive Streamsはバックプレッシャーを持つ非同期コンポーネント間の対話を定義する小さな仕様で、Java 9にも採用されています。例えば、Publisherであるデータストアは、SubscriberであるHTTPサーバーが応答へ書き込めるデータを生成します。
FAQ: Publisherが速度を落とせない場合はどうしますか。
Reactive Streamsは仕組みと境界だけを定義します。バッファリング、破棄、失敗のいずれかを選択する必要があります。
1.1.2. Reactive API
Reactive Streamsは相互運用性に重要ですが、アプリケーションAPIとしては低水準です。アプリケーションには、コレクションに限らず非同期ロジックを構成できる豊富なAPIが必要です。
ReactorはSpring WebFluxが採用するリアクティブライブラリです。MonoとFluxは0..1と0..Nのシーケンスを扱い、ReactiveXのoperator vocabularyに対応する演算子を提供します。
WebFluxは内部でReactorを必要としますが、Reactive Streamsを介して他のライブラリと相互運用できます。一般的なPublisherを入力として受け取り、内部でReactor型へ変換し、FluxまたはMonoを返します。詳細はReactive Librariesを参照してください。
WebFluxはKotlin Coroutinesもサポートします。詳細はCoroutines APIを参照してください。
1.1.3. プログラミングモデル
spring-webモジュールはHTTP抽象化、Reactive Streamsアダプター、codec、WebHandler APIを提供します。WebFluxには二つのモデルがあります。
- アノテーション付きコントローラー: Spring MVCと同じアノテーションを使用します。MVCとWebFluxはリアクティブな戻り値をサポートし、WebFluxはリアクティブな
@RequestBody引数もサポートします。 - 関数型エンドポイント: ラムダ式を使用する軽量モデルです。アプリケーションがルーティングと処理を直接担当します。
1.1.4. 適用性
Spring MVCとWebFluxは併用でき、一貫性を保つように設計されています。

- 動作しているMVCアプリケーションは、理由がなければ変更不要です。命令型コードは理解しやすく、多くのライブラリはブロッキングです。
- ノンブロッキングスタックが必要ならWebFluxが適しています。Netty、Tomcat、Jetty、Undertow、Servlet 3.1+と複数のリアクティブライブラリを選択できます。
- 関数型エンドポイントは軽量なJava 8ラムダまたはKotlinのWebフレームワーク、小規模アプリケーション、マイクロサービスに適しています。
- マイクロサービスではMVC、WebFluxコントローラー、関数型エンドポイントを混在できます。
- JPA、JDBC、ネットワークAPIなどのブロッキングAPIへ依存する場合、通常はMVCが適しています。
- MVCでもリアクティブな
WebClientを利用し、コントローラーからリアクティブ型を返せます。 - 移行には学習コストがあります。まず
WebClientから小さく始め、効果を測定してください。
1.1.5. サーバー
WebFluxは共通の低水準APIを介してTomcat、Jetty、Servlet 3.1+、Netty、Undertowをサポートします。
Spring Framework自身はサーバーを起動、停止しませんが、アプリケーションを簡単に構成して実行できます。Spring Bootはこれを自動化し、標準でNettyを使用します。
TomcatとJettyはMVCとWebFluxの両方に対応します。MVCはブロッキングServlet I/O、WebFluxはアダプターの背後でServlet 3.1のノンブロッキングI/Oを使用します。UndertowではServlet APIを介さずUndertow APIを直接使用します。
1.1.6. パフォーマンス
リアクティブなノンブロッキングアプリケーションが常に高速になるわけではありません。主な利点は少数の固定スレッドと少ないメモリで拡張でき、負荷時の回復力が高まることです。この利点は遅延や予測しにくいネットワークI/Oを含む処理で明確になります。
1.1.7. 同時実行モデル
MVCは現在のスレッドがブロックされることを想定し、大きなスレッドプールを使います。WebFluxはブロックしないことを想定し、少数の固定イベントループworkerを使います。
ブロッキング呼び出しを吸収する追加スレッドが不要なため、少ないスレッドで拡張できます。
ブロッキングAPIの呼び出し
ReactorとRxJavaは別スレッドへ処理を切り替えるpublishOn演算子を提供します。ただし、ブロッキングAPIはこのモデルに適しません。
可変状態
ReactorとRxJavaの演算子はデータを段階的に処理するパイプラインを作ります。パイプライン内のコードは同時に呼び出されないため、可変状態を保護する必要が減ります。
スレッドモデル
- 基本的なWebFluxサーバーは通常、一つのサーバースレッドとCPUコア数程度の処理スレッドを使います。Servletコンテナーはより多くのスレッドで起動する場合があります。
- リアクティブ
WebClientは固定数のイベントループスレッドを使います。Reactor Nettyのclientとserverは標準で資源を共有します。 - ReactorとRxJavaのschedulerはスレッドプールを抽象化します。
publishOnでCPU-bound向けのparallel、I/O-bound向けのelasticなどへ切り替えます。 - データアクセスライブラリなども独自のスレッドを作成できます。
設定
サーバー固有APIまたはSpring Bootオプションでサーバーを設定します。WebClientはbuilderで直接設定できます。