前回、メッセージキューを使ったアプリケーション間の非同期連携を学びました。これはシステム間の連携を効率化する技術でした。
今回は、単一のアプリケーション内で、特にI/O処理(ネットワーク通信やDBアクセス)における性能を限界まで引き出すための設計思想、**リアクティブプログラミング(Reactive Programming)**の基本を学びます。
Spring Frameworkでは、このリアクティブプログラミングを実現するために Spring WebFlux や Project Reactor が中核を担っています。
1. なぜリアクティブプログラミングが必要か?
これまでのSpring MVCで扱ってきた処理は、ほとんどが**同期処理(ブロッキング)**でした。
- ブロッキング(同期I/O): 外部APIへのリクエストやDBアクセスが発生すると、その処理が完了して結果が返ってくるまで、**アプリケーションのスレッドは待機(ブロック)**します。スレッドがブロックされている間、そのスレッドは他のリクエストを処理できず、多数の同時接続に弱くなります。
**リアクティブプログラミング(ノンブロッキングI/O)は、スレッドを待機させません。I/O処理中にスレッドがブロックされる代わりに、そのスレッドは別のリクエスト処理に切り替わります。I/O処理が完了したら、「データが利用可能になった」という通知(イベント)**が届き、処理を再開します。
- メリット: スレッドの利用効率が極限まで高まり、少数のスレッドで大量の同時接続を処理できるようになります。
2. リアクティブプログラミングの核:Project Reactor
Spring WebFlux(リアクティブなWebフレームワーク)の基盤となっているのが、Project Reactorというライブラリです。Reactorは、非同期で発生するデータを扱うための2つの主要な型を提供します。
2-1. Mono(モノ)
- 意味: 0件または1件のデータ、またはエラーを非同期的に処理するコンテナ。
- 用途: 単一のオブジェクトや、処理の完了通知(Void)を扱う場合に主に使用されます(例:単一のユーザーデータ取得、POSTリクエストのレスポンス)。
2-2. Flux(フラックス)
- 意味: 0件以上の複数件のデータストリーム、またはエラーを非同期的に処理するコンテナ。
- 用途: リストやコレクション、継続的なストリームデータ(例:複数のユーザーリスト、WebSocketからの継続的なデータ)を扱う場合に主に使用されます。
2-3. リアクティブの基本操作
MonoやFluxは、データを**ストリーム(流れ)**として扱い、メソッドチェーンで処理を繋げていくのが特徴です。
Java
import reactor.core.publisher.Flux;
public void reactiveExample() {
// 1. Fluxの生成 (データの発行元/Publisher)
Flux<String> names = Flux.just("Alice", "Bob", "Charlie")
// 2. 処理の定義 (データが流れてきたら実行される操作)
.map(name -> name.toUpperCase()) // 全て大文字に変換
.filter(name -> name.startsWith("A")) // 'A'で始まるものだけ残す
// 3. 購読(Subscription/Consumer)
// 実際にデータが流れ始めるのは、subscribe()が呼ばれた後
.subscribe(
name -> System.out.println("受信データ: " + name), // データ受信時の処理
error -> System.err.println("エラーが発生: " + error), // エラー発生時の処理
() -> System.out.println("ストリーム完了") // 完了時の処理
);
}
3. Spring WebFluxとの関係
第33回で学んだ WebClient の戻り値が Mono や Flux であったのは、WebClientがこのリアクティブな仕組み(Project Reactor)の上に構築されているからです。
Spring WebFlux環境では、Controllerの戻り値もMonoやFluxになります。
Java
// Spring WebFlux環境のController
@RestController
public class ItemController {
// Mono<Item>を返す。クライアントへのレスポンスは、データが到着次第送信される(ノンブロッキング)
public Mono<Item> getItemDetail(Long id) {
// ... (Repositoryからノンブロッキングでデータを取得する処理)
}
}
Spring WebFluxは、従来のサーブレットコンテナ(Tomcatなど)ではなく、NettyなどのノンブロッキングI/Oに特化したサーバーをデフォルトで使用します。
4. バックプレッシャー(Backpressure)
リアクティブプログラミングの重要な概念にバックプレッシャーがあります。
- 問題点: データの発行元(プロデューサー)が、データの処理速度が追いつかないほど大量のデータを送りつけた場合、処理側(コンシューマー)がパンクしてしまう可能性があります。
- バックプレッシャー: コンシューマーがプロデューサーに対して「これ以上送らないで。あとN個だけなら処理できるよ」と信号を送り、データの流量を調整する仕組みです。
これにより、システムが過負荷によって停止するのを防ぎ、処理能力を超えないように安定した動作が保証されます。
✅ 本日のまとめ
- リアクティブプログラミングは、I/O処理中にスレッドを待機させないノンブロッキングI/Oを実現し、少数のスレッドで大量の同時リクエストを処理する。
- Springのリアクティブ基盤は Project Reactor であり、データを扱う主要な型としてMono(0〜1件)とFlux(0件以上)がある。
- MonoとFluxは、メソッドチェーンによって非同期の処理フローを定義し、**subscribe()**が呼ばれるまでデータは流れ始めない(怠惰な実行)。
- バックプレッシャーは、データの流れの速度をコンシューマー側が制御する仕組みであり、システムの安定性を保つ。
🔔 次回予告
これで、Javaのリアクティブプログラミングの核心に触れることができました。
次回は、全50回講座の総仕上げとして、これまで学んだ知識を統合し、Spring Bootアプリケーションの最終的な性能チューニングと改善に関する総まとめを行います。
次回:【第49回】総仕上げ!〜Spring Bootの最終性能チューニングと改善ポイント〜 にご期待ください!


コメント