Spring Web MVC は、Spring フレームワークに含まれるオリジナルの Web フレームワークであり、サーブレット API とサーブレット コンテナー専用に設計されました。その後、リアクティブ スタック Web フレームワーク Spring WebFlux がバージョン 5.0 に追加されました。完全にノンブロッキングで、Reactive Streams バックプレッシャーをサポートし、Netty、Undertow、Servlet コンテナなどのサーバー上で実行されます。
どちらの Web フレームワークも、ソース モジュール (spring-webmvc および spring-webflux) の名前を反映しており、Spring フレームワーク内に共存します。各モジュールはオプションです。アプリケーションは、どちらかのモジュールを使用することも、場合によっては両方を使用することもできます (たとえば、Reactive WebClient を備えた Spring MVC コントローラー)。
1。概要
Spring WebFlux を作成する理由
その答えの 1 つは、少数のスレッドで同時実行を処理し、より少ないハードウェア リソースで拡張できるノンブロッキング Web スタックの必要性です。サーブレットのノンブロッキング I/O は、サーブレット API の他の部分につながります。この部分では、規則が同期 (Filter、Servlet) またはブロッキング (getParameter、getPart) になります。これが、ノンブロッキング ランタイム全体の基盤として機能する新しい共通 API の動機です。サーバー (Netty など) は非同期、ノンブロッキングの世界ではすでに非常に成熟しているため、これは重要です。
答えのもう 1 つの部分は関数型プログラミングです。Java 5 でのアノテーションの追加によって (アノテーション付き REST コントローラーや単体テストなど) 機会が生み出されたのと同様に、Java 8 でのラムダ式の追加によって、Java での関数 API の機会が生まれます。これは、非同期ロジックの宣言的構成を可能にするノンブロッキング アプリケーションや継続スタイルの API (CompletableFuture や ReactiveX によって普及した API など) にとって有益です。プログラミング モデル レベルでは、Java 8 により、Spring WebFlux がアノテーション付きコントローラーとともに機能的な Web エンドポイントを提供できるようになります。
2.「リアクティブ」(応答性)を定義する
「ノンブロッキング」と「機能的」について説明しましたが、リアクティブとは何を意味するのでしょうか?
「リアクティブ」という用語は、I/O イベントに反応するネットワーク コンポーネント、マウス イベントに反応する UI コントローラーなど、変更への反応を中心としたプログラミング モデルを指します。この意味で、非ブロッキングはリアクティブです。ブロックされるのではなく、操作が完了したとき、またはデータが利用可能になったときに通知に反応するようになるからです。
私たちの Spring チームが「応答性」と関連付けているもう 1 つの重要なメカニズムがあります。それはノンブロッキング バックプレッシャーです。同期の命令型コードでは、呼び出しのブロックはバックプレッシャーの自然な形であり、呼び出し元に待機を強います。ノンブロッキング コードでは、高速プロデューサーがその「コンシューマー」を圧倒しないように、イベントの速度を制御することが重要になります。
Reactive Streams は、バックプレッシャーを伴う非同期コンポーネント間の対話を定義する小規模な仕様 (Java 9 でも採用) です。たとえば、データ リポジトリ (パブリッシャとして機能) がデータを生成し、次に HTTP サーバー (サブスクライバとして機能) が応答を書き込むことができます。Reactive Streams の主な目的は、サブスクライバーがパブリッシャーがデータを生成する速度を制御できるようにすることです。
よくある質問: サイト運営者がペースを緩められない場合はどうなりますか? |
三、Reactive API
リアクティブ ストリームは相互運用性において重要な役割を果たします。これはライブラリやインフラストラクチャ コンポーネントにとっては意味がありますが、低レベルすぎるため、アプリケーションの API としてはあまり有用ではありません。アプリケーションには、非同期ロジックを作成するための、より高レベルで機能豊富な API が必要です。Java 8 の Stream API に似ていますが、コレクションだけではありません。これがリアクティブライブラリの役割です。
Reactor は、Spring WebFlux に推奨されるリアクティブ ライブラリです。ReactiveX 演算子ボキャブラリと一致する豊富な演算子のセットを通じて、0..1 (Mono) および 0..N (Flux) のデータ シーケンスを操作する Mono および Flux API タイプを提供します。Reactor は Reactive Streams ライブラリであるため、そのすべての演算子はノンブロッキング バックプレッシャーをサポートしています。Reactor はサーバーサイド Java に重点を置いています。Spring と緊密に協力して開発されています。
WebFlux はコア依存関係として Reactor を必要としますが、Reactive Streams を通じて他のリアクティブ ライブラリと相互運用できます。一般に、WebFlux API はプレーンな Publisher を入力として受け入れ、内部でそれを Reactor タイプに調整してそのタイプを使用し、出力として Flux または Mono を返します。したがって、任意の Publisher を入力として渡し、出力に操作を適用できますが、別のリアクティブ ライブラリで動作するように出力を調整する必要があります。可能な場合 (コントローラーにアノテーションを付けるなど)、WebFlux は RxJava またはその他のリアクティブ ライブラリの使用に透過的に適応します。詳細については、「リアクティブ ライブラリ」を参照してください。
WebFlux は、Reactive API に加えて、より命令型のプログラミング スタイルを提供する Kotlin の Coroutines API とともに使用することもできます。次の Kotlin コード サンプルは、Coroutines API を提供します。 |
4. プログラミングモデル
spring-web モジュールには、HTTP 抽象化、サーバー支援型 Reactive Streams アダプター、コーデック、サーブレット API に相当するがノンブロッキング規約を持つコア WebHandler API など、Spring WebFlux を強化するリアクティブ基盤が含まれています。
これに基づいて、Spring WebFlux は 2 つのプログラミング モデル オプションを提供します。
- アノテーション付きコントローラー: Spring MVC と一貫性があり、spring-web モジュールの同じアノテーションに基づいています。Spring MVC と WebFlux コントローラーは両方ともリアクティブ (Reactor と RxJava) 戻り値の型をサポートしているため、それらを区別するのは簡単ではありません。明らかな違いは、WebFlux がリアクティブな @RequestBody パラメーターもサポートしていることです。
- 機能エンドポイント: Lambda ベースの軽量の関数プログラミング モデル。これは、アプリケーションがリクエストのルーティングと処理に使用できる小さなライブラリまたはユーティリティのセットと考えることができます。アノテーション ベースのコントローラーとの最大の違いは、アノテーションを通じて意図を宣言してコールバックされるのではなく、アプリケーションが最初から最後までリクエストを処理する責任を負うことです。
5. 適用性
Spring MVC または WebFlux ですか?
これは当然の疑問ですが、不健全な二分法を生み出します。実際、両方が連携して利用可能なオプションの範囲を拡大します。どちらも相互の継続性と一貫性を考慮して設計されており、並べて使用することができ、それぞれからのフィードバックが両方にメリットをもたらします。以下の図は、この 2 つの関係、共通点、およびそれぞれがサポートする独自の機能を示しています。
次の具体的な点を考慮することをお勧めします。
- Spring MVC アプリケーションが正常に動作している場合は、変更する必要はありません。命令型プログラミングは、コードを作成、理解、デバッグする最も簡単な方法です。歴史的にほとんどのライブラリはブロックされてきたため、ライブラリの選択を最大限に高めることができます。
- すでにノンブロッキング Web スタックを「購入」している場合、Spring WebFlux は、この分野の他のスタックと同じ実行モデルの利点に加えて、サーバー (Netty、Tomcat、Jetty、Undertow、および Servlet コンテナー) の選択肢を提供し、プログラミングを提供します。モデル (アノテーション付きコントローラーと機能的な Web エンドポイント)、およびリアクティブ ライブラリ (Reactor、RxJava など) の選択。
- Java 8 ラムダまたは Kotlin 用の軽量の関数型 Web フレームワークに興味がある場合は、Spring WebFlux 関数型 Web エンドポイントを使用できます。また、透明性と制御性の向上によりメリットが得られる、要件が複雑ではない小規模なアプリケーションやマイクロサービスにも適したオプションです。
- マイクロサービス アーキテクチャでは、アプリケーションを Spring MVC または Spring WebFlux コントローラー、あるいは Spring WebFlux の機能エンドポイントと混合できます。両方のフレームワークで同じアノテーションベースのプログラミング モデルをサポートすることで、知識の再利用が容易になると同時に、適切なジョブに適切なツールを選択できるようになります。
- アプリケーションを評価する簡単な方法は、その依存関係を調べることです。ブロッキング永続性 API (JPA、JDBC) またはネットワーク API を使用する必要がある場合、少なくとも通常のアーキテクチャでは Spring MVC が最良の選択です。Reactor と RxJava を使用して別のスレッドでブロッキング呼び出しを実行することは技術的には可能ですが、ノンブロッキング Web スタックを最大限に活用することはできません。
- リモート サービスを呼び出す Spring MVC アプリケーションがある場合は、リアクティブ WebClient を試してください。Spring MVC コントローラーのメソッドから直接リアクティブ型 (Reactor、RxJava など) を返すことができます。各通話の遅延が大きいほど、または通話間の相互依存性が大きいほど、利点は大きくなります。Spring MVC コントローラーは他のリアクティブ コンポーネントを呼び出すこともできます。
- 大規模なチームの場合、ノンブロッキング、関数型、宣言型プログラミングに移行するときは、学習曲線が急峻であることに留意してください。完全な変換が行われない場合の実際的なアプローチは、リアクティブ WebClient を使用することです。さらに、小規模から始めてメリットを測定してください。幅広いアプリケーションでは、この移行は必要ないと予想されます。探している利点がわからない場合は、ノンブロッキング I/O の仕組み (シングルスレッド Node.js での同時実行性など) とその影響を理解することから始めてください。
6. サーバー
Spring WebFlux は、Tomcat、Jetty、サーブレット コンテナ、および Netty や Undertow などの非サーブレット ランタイムをサポートします。すべてのサーバーは低レベルの共通 API に適応し、サーバー全体で高レベルのプログラミング モデルをサポートできるようになります。
Spring WebFlux には、サーバーの起動または停止のサポートが組み込まれていません。ただし、Spring 構成と WebFlux インフラストラクチャを通じてアプリケーションをアセンブルし、数行のコードで実行するのは簡単です。
Spring Boot には、これらの手順を自動化する WebFlux スターターが含まれています。デフォルトでは、スターターは Netty を使用しますが、Maven または Gradle の依存関係を変更することで、Tomcat、Jetty、または Undertow に簡単に切り替えることができます。Spring Boot はデフォルトで Netty を使用します。これは、Netty が非同期のノンブロッキング領域でより広く使用されており、クライアントとサーバーがリソースを共有できるためです。
Tomcat と Jetty はどちらも Spring MVC および WebFlux で使用できます。ただし、使用方法が大きく異なることに注意してください。Spring MVC はサーブレット ブロッキング I/O に依存しており、必要に応じてアプリケーションがサーブレット API を直接使用できるようにします。Spring WebFlux は、サーブレットのノンブロッキング I/O に依存し、低レベルのアダプターの背後でサーブレット API を使用します。直接使用するために公開されていません。
Undertow の場合、Spring WebFlux はサーブレット API を使用する代わりに Undertow の API を直接使用します。
7. パフォーマンス
パフォーマンスには多くの特徴と意味があります。一般に、リアクティブおよびノンブロッキングによってアプリケーションの実行が高速化されるわけではありません。場合によっては、それが可能です (たとえば、WebClient を使用してリモート呼び出しを並行して実行する場合)。全体として、ノンブロッキングな方法で処理を行うとより多くの作業が必要となり、必要な処理時間がわずかに増加する可能性があります。
リアクティブとノンブロッキングの期待される主な利点は、少数の固定スレッドと少ないメモリで拡張できることです。これにより、アプリケーションはより予測可能な方法でスケーリングされるため、負荷がかかってもアプリケーションの回復力が高まります。ただし、これらの利点を享受するには、ある程度の遅延 (低速で予測不可能なネットワーク I/O の混在を含む) が必要です。ここでリアクティブスタックの利点が現れ始め、その違いは非常に大きくなる可能性があります。
8. 同時実行モデル
Spring MVC と Spring WebFlux はどちらもアノテーション付きコントローラーをサポートしていますが、同時実行モデルと、ブロックとスレッドに関するデフォルトの前提に大きな違いがあります。
Spring MVC (およびサーブレット アプリケーション一般) では、アプリケーションが現在のスレッドをブロックできると想定されています (たとえば、リモート呼び出しの場合)。このため、サーブレット コンテナは、リクエスト処理中に発生する可能性のあるブロックを吸収するために、大きなスレッド プールを使用します。
Spring WebFlux (および一般に非ブロッキングサーバー) では、アプリケーションはブロックしないと想定されています。したがって、ノンブロッキングサーバーは、小さな固定サイズのスレッドプール (イベントループワーク) を使用してリクエストを処理します。
「規模」と「少数のスレッド」は矛盾しているように聞こえるかもしれませんが、現在のスレッドを決してブロックしない (むしろコールバックに依存する) ということは、吸収するブロック呼び出しがないため、追加のスレッドが必要ないことを意味します。 |
1. コールブロッキングAPI
本当にブロッキング ライブラリを使用する必要がある場合はどうすればよいでしょうか? Reactor と RxJava は両方とも、別のスレッドで処理を続行するためのpublishOn オペレーターを提供します。つまり、シンプルなライフラインがあるということです。ただし、ブロッキング API はこの同時実行モデルには適していないことに注意してください。
2. 変数の状態
Reactor と RxJava では、演算子を通じてロジックを宣言します。実行時には、データが段階的に順次処理されるリアクティブ パイプラインが形成されます。これを行う主な利点は、パイプライン内のアプリケーション コードが同時に呼び出されないため、アプリケーションが可変状態を保護する必要がなくなることです。
3. ネジモデル
Spring WebFlux で実行されているサーバーではどのようなスレッドが表示されるでしょうか?
- 「標準的な」Spring WebFlux サーバー (つまり、データ アクセスやその他のオプションの依存関係がない) では、サーバーにはリクエスト処理用に 1 つのスレッドとその他のいくつかのスレッド (通常は CPU コアと同じ数) があることが予想されます。ただし、サーブレット コンテナは、サーブレット (ブロッキング) I/O およびサーブレット 3.1 (ノンブロッキング) I/O の使用をサポートするために、より多くのスレッド (Tomcat では 10 個など) で開始される場合があります。
- リアクティブ WebClient はイベント ループで実行されます。そのため、それに関連付けられた少数の固定処理スレッド (reactor-http-nio- や Reactor Netty コネクタなど) が確認できます。ただし、Reactor Netty がクライアントとサーバーの両方で使用されている場合、デフォルトで 2 つはイベント ループ リソースを共有します。
- Reactor と RxJava は、処理を別のスレッド プールに切り替えるために、publishOn オペレーターで使用するスケジューラと呼ばれるスレッド プールの抽象化を提供します。スケジューラの名前は、特定の同時実行戦略を暗示します。たとえば、「並列」(限られた数のスレッドによる CPU バウンドの作業の場合) または「エラスティック」(多数のスレッドによる I/O バウンドの作業の場合) です。このようなスレッドが表示された場合は、一部のコードが特定のスレッド プールのスケジューラ戦略を使用していることを意味します。
- データ アクセス ライブラリやその他のサードパーティの依存関係も、独自のスレッドを作成して使用できます。
4. 構成
Spring フレームワークは、サーバーの起動と停止をサポートしていません。サーバーのスレッド モデルを構成するには、サーバー固有の構成 API を使用するか、Spring Boot を使用する場合は各サーバーの Spring Boot 構成オプションを確認する必要があります。WebClient を直接設定できます。他のすべてのライブラリについては、それぞれのドキュメントを参照してください。