少し前に、最も人気のあるJavaフレームワークの新しいバージョンであるSpring Framework 5がリリースされました。 新しいバージョンは多くの新しいものをもたらしました。 最大の革新の1つは、リアクティブプログラミングモデルです。 Spring Boot 2は間もなくリリースされ、このアプローチによりマイクロサービスの作成が大幅に簡素化されます。
私のように、それが何であり、どのように使用されるかをより詳細に理解したい場合は、catへようこそ。 この記事は、理論と実用の2つの部分に分かれています。 次に、リアクティブになることの意味を理解しようとします。 その後、取得した知識を使用して、独自のマイクロサービスを作成します( パート2 )。
反応性とは何ですか?
まず、反応性の概念を検討します。 そして、ここですぐに定義を明確に区別する必要があります。
リアクティブシステム
リアクティブシステムは、特定のルールセット( リアクティブマニフェスト )を満たすアーキテクチャパターンです。 このマニフェストは、不確実性を排除するために2013年に開発されました。 事実、当時のヨーロッパとアメリカでは、「リアクティブ」という言葉は余りにも冗長でした。 誰もが自分のやり方で、どのシステムがリアクティブと呼ばれるかを理解していました。 これにより大きな混乱が生じ、結果として、リアクティブシステムの明確な基準を設定するマニフェストが作成されました。
マニフェストの画像を見て、各アイテムの意味をさらに詳しく分析してみましょう。
- 責任者 この原則は、開発されたシステムが特定の所定時間内に迅速に応答する必要があることを示しています。 さらに、システムは自己診断と修復のために十分な柔軟性を備えている必要があります。
これは実際にはどういう意味ですか? 従来、特定のサービスをリクエストする場合、データベースにアクセスし、必要な量の情報を取り出してユーザーに提供します。 システムが十分に高速であり、データベースがそれほど大きくない場合は、ここですべて問題ありません。 しかし、応答時間が予想よりはるかに長い場合はどうでしょうか? さらに、ユーザーは数ミリ秒間インターネットを失った可能性があります。 その後、データをサンプリングして応答を定式化するためのすべての努力がなくなります。 GmailまたはFacebookを考えてください。 インターネットに問題がある場合、エラーは発生しませんが、通常よりも結果が出るのを待ちます。 さらに、この段落は、応答と要求が合理化され、一貫している必要があることを示しています。 - 弾力性のある 。 コンポーネントの1つが故障しても、システムは動作し続けます。 言い換えれば、システムのコンポーネントは十分に柔軟で、互いに分離されている必要があります。 これは複製によって実現されます。 たとえば、1つのPostgreSQLレプリカに障害が発生した場合、別のレプリカが常に利用可能であることを確認する必要があります。 さらに、アプリケーションは複数のインスタンスで動作するはずです。
- 弾性 この原則は、システムが各期間で最適な量のリソースを占有する必要があることを示唆しています。 負荷が高い場合は、アプリケーションのインスタンスの数を増やす必要があります。 軽負荷の場合、空きマシンのリソースをクリーニングする必要があります。 この原則を実装するための典型的なツール: Kubernetes
- メッセージドリブン これが、Java開発者にとって最も重要なポイントの始まりです。 これは、アプリで処理する必要がある問題です。 サービス間の通信は、非同期メッセージを介して発生する必要があります。 つまり、システムの各要素は別の要素から情報を要求しますが、すぐに結果を受け取ることは期待していません。 代わりに、彼はタスクを実行し続けます。 これにより、システムリソースの利点を高め、発生するより柔軟なエラーを管理できます。 通常、この結果はリアクティブプログラミングによって実現されます。
リアクティブプログラミング
ウィキペディアによると、リアクティブプログラミングはデータフローに焦点を当てたプログラミングパラダイムです。 すぐに実際の動作を分析します。 しかし、最初に、このパラダイムが基づいているものを見てみましょう。
リアクティブプログラミングの基本概念は、ノンブロッキングI / Oに基づいています。 通常、特定のリソース(データベース、ディスク上のファイル、リモートサーバーなど)にアクセスすると、結果がすぐに(多くの場合同じ行で)取得されます。 リソースへの非ブロッキング呼び出しでは、スレッドは呼び出しで停止せず、実行を継続します。 結果は後で取得し、必要に応じて取得します。
練習する
いいね! それでは、 Javaでリアクティブプログラミングの実装を始めましょう。 唯一注意することは、 Spring WebFluxを使用することです 。 これは新しいリアクティブプログラミングフレームワークです。 問題は、 Springチームがこれらの目的でSpring Web MVCを使用しなかった理由は何ですか? 実際、このフレームワークのすべてのモジュールをリアクティブモードで使用できるわけではありません。 宣言型プログラミングとスレッドに基づく、 Tomcatなどの多くのコードとサードパーティライブラリが残っています。
フレームワークの作業中に、非同期作業の小さな仕様が開発されました。 後に彼らはこの仕様をJava 9に含めることにしました。しかし、簡単にするためにJava 8とSpring Boot 2を使用します。
基本的な概念
新しいアプローチでは、リアクティブモードで作業するための2つの主要なクラスがあります。
- モノ
Monoクラスは、単一のオブジェクトを操作するために必要です。 Monoを使用した簡単なアプリケーションがどのようになるかを見てみましょう。 これを行うには、プロジェクトとその中にユーザーエンティティを作成します(すべての設定と例は、 githubのプロファイルにあります)。
@Data @NoArgsConstructor @AllArgsConstructor public class User { private String firstName; private String lastName; }
次に、テストと訓練されたユーザーでクラスを作成します。
public class HabrreactiveApplicationTests { private User peter = new User("Peter", "Griffin"); private User lois = new User("Lois", "Griffin"); private User brain = new User("Brain", "Griffin"); }
テストを書きましょう:
@Test public void mono() { // Mono<User> monoPeter = Mono.just(peter); // User peter2 = monoPeter.block(); // , assertEquals(peter, peter2); }
例からわかるように、リアクティブアプローチの使用は非常に簡単です。
さらに、 Monoクラスには、あらゆる機会に対応する多くのメソッドがあります。 たとえば、あるタイプを別のタイプに変換するためのよく知られたマップメソッドがあります。
@Test public void blockMono() { Mono<User> monoPeter = Mono.just(peter); // String name = monoPeter.map(User::getFirstName).block(); assertEquals(name, "Peter"); }
- フラックス
このクラスはMonoに似ていますが、複数のオブジェクトを非同期で操作する機能を提供します。
@Test public void flux() { // Flux<User> fluxUsers = Flux.just(peter, lois, brain); // fluxUsers.subscribe(System.out::println); }
Monoと同様に、 Fluxには便利なメソッドのセットがあります:
@Test public void fluxFilter() { Flux<User> userFlux = Flux.just(peter, lois, brain); // userFlux .filter(user -> user.getFirstName().equals("Peter")) .subscribe(user -> assertEquals(user, peter)); } @Test public void fluxMap() { Flux<User> userFlux = Flux.just(peter, lois, brain); // User String userFlux .map(User::getFirstName) .subscribe(System.out::println); }
ここでは、1つの機能を強調する必要があります。 標準(非デーモン)スレッドとは異なり、実行のメインスレッドが完了すると、データの収集が停止し、プログラムが終了します。 これは簡単に実証できます。 次のコードはコンソールに何も出力しません。
@Test public void fluxDelayElements() { Flux<User> userFlux = Flux.just(peter, lois, brain); // 1 userFlux.delayElements(Duration.ofSeconds(1)) .subscribe(System.out::println); }
これは、 CountDownLatchクラスを使用することで回避できます。
@Test public void fluxDelayElementsCountDownLatch() throws Exception { // CountDownLatch countDownLatch = new CountDownLatch(1); Flux<User> userFlux = Flux.just(peter, lois, brain); // userFlux // userFlux .delayElements(Duration.ofSeconds(1)) .doOnComplete(countDownLatch::countDown) .subscribe(System.out::println); // // countDownLatch.await(); }
これらはすべて非常にシンプルで、リソース効率に優れています。 ストリームメソッドへの呼び出しを組み合わせたときに何が得られるか想像してみてください。
この記事では、 リアクティブシステムとリアクティブプログラミングの概念を検証しました。 さらに、これらの概念がどのように関連しているかを理解しました。 次のパートでは、さらに進んで、知識に基づいてサービスを構築しようとします。
PS mail.ruからメッセージングシステムを分解することを提案します。 そのようなアプリケーションは、マニフェストに従ってメッセージシステムと呼ぶことができると思いますか? コメントにあなたの考えを書いてください。 とても興味深い。