プロジェクトリアクタでパブリッシャを結合する

Project Reactorでパブリッシャーを結合する

1. 概要

この記事では、PublishersProject Reactorに組み合わせるさまざまな方法を見ていきます。

2. Mavenの依存関係

Project Reactorの依存関係を使用して例を設定しましょう。


    io.projectreactor
    reactor-core
    3.1.4.RELEASE


    io.projectreactor
    reactor-test
    3.1.4.RELEASE
    test

3. 出版社を組み合わせる

Flux<T>またはMono<T>を操作する必要があるシナリオを考えると、ストリームを組み合わせるさまざまな方法があります。

concat, concatWith, merge, zipcombineLatest.などのFlux<T>クラスの静的メソッドの使用法を説明するためにいくつかの例を作成しましょう

この例では、タイプFlux<Integer>の2つのパブリッシャー、つまりevenNumbersを使用します。これはIntegerFluxであり、1(%(min変数)および5によって制限されます(max変数)。

oddNumbersと、奇数のタイプIntegerFluxを作成します。

Flux evenNumbers = Flux
  .range(min, max)
  .filter(x -> x % 2 == 0); // i.e. 2, 4

Flux oddNumbers = Flux
  .range(min, max)
  .filter(x -> x % 2 > 0);  // ie. 1, 3, 5

3.1. concat()

concatメソッドは、入力の連結を実行し、ソースによって放出された要素をダウンストリームに転送します。

連結は、最初のソースを順番にサブスクライブし、次に完了するのを待ってから次のソースにサブスクライブし、最後のソースが完了するまで同様に続きます。 エラーが発生すると、シーケンスが直ちに中断され、ダウンストリームに転送されます。

これは簡単な例です。

@Test
public void givenFluxes_whenConcatIsInvoked_thenConcat() {
    Flux fluxOfIntegers = Flux.concat(
      evenNumbers,
      oddNumbers);

    StepVerifier.create(fluxOfIntegers)
      .expectNext(2)
      .expectNext(4)
      .expectNext(1)
      .expectNext(3)
      .expectNext(5)
      .expectComplete()
      .verify();
}

3.2. concatWith()

静的メソッドconcatWithを使用して、結果としてタイプFlux<T>の2つのソースの連結を生成します。

@Test
public void givenFluxes_whenConcatWithIsInvoked_thenConcatWith() {
    Flux fluxOfIntegers = evenNumbers.concatWith(oddNumbers);

    // same stepVerifier as in the concat example above
}

3.3. combineLatest()

Flux静的メソッドcombineLatestは、各パブリッシャーソースから最後に公開された値の組み合わせによって提供されるデータを生成します。

次に、2つのPublisherソースとBiFunctionをパラメーターとして使用したこのメソッドの使用例を示します。

@Test
public void givenFluxes_whenCombineLatestIsInvoked_thenCombineLatest() {
    Flux fluxOfIntegers = Flux.combineLatest(
      evenNumbers,
      oddNumbers,
      (a, b) -> a + b);

    StepVerifier.create(fluxOfIntegers)
      .expectNext(5) // 4 + 1
      .expectNext(7) // 4 + 3
      .expectNext(9) // 4 + 5
      .expectComplete()
      .verify();
}

ここで、関数combineLatestevenNumbersの最新要素(4)とoddNumbers (1,3,5)の要素を使用して関数「a + b」を適用し、シーケンス5,7,9

3.4. merge()

merge関数は、配列に含まれるPublisherシーケンスからのデータのインターリーブされたマージされたシーケンスへのマージを実行します。

@Test
public void givenFluxes_whenMergeIsInvoked_thenMerge() {
    Flux fluxOfIntegers = Flux.merge(
      evenNumbers,
      oddNumbers);

    StepVerifier.create(fluxOfIntegers)
      .expectNext(2)
      .expectNext(4)
      .expectNext(1)
      .expectNext(3)
      .expectNext(5)
      .expectComplete()
      .verify();
}

注意すべき興味深い点は、concat (lazyサブスクリプション)とは対照的に、ソースが熱心にサブスクライブされていることです。

ここで、パブリッシャーの要素間に遅延を挿入すると、merge関数の異なる結果を確認できます。

@Test
public void givenFluxes_whenMergeWithDelayedElementsIsInvoked_thenMergeWithDelayedElements() {
    Flux fluxOfIntegers = Flux.merge(
      evenNumbers.delayElements(Duration.ofMillis(500L)),
      oddNumbers.delayElements(Duration.ofMillis(300L)));

    StepVerifier.create(fluxOfIntegers)
      .expectNext(1)
      .expectNext(2)
      .expectNext(3)
      .expectNext(5)
      .expectNext(4)
      .expectComplete()
      .verify();
}

3.5. mergeSequential()

mergeSequentialメソッドは、配列で提供されたPublisherシーケンスからのデータを順序付けられたマージされたシーケンスにマージします。

concatとは異なり、ソースは熱心にサブスクライブされます。

また、mergeとは異なり、それらの出力値はサブスクリプション順に最終シーケンスにマージされます。

@Test
public void testMergeSequential() {
    Flux fluxOfIntegers = Flux.mergeSequential(
      evenNumbers,
      oddNumbers);

    StepVerifier.create(fluxOfIntegers)
      .expectNext(2)
      .expectNext(4)
      .expectNext(1)
      .expectNext(3)
      .expectNext(5)
      .expectComplete()
      .verify();
}

3.6. mergeDelayError()

mergeDelayErrorは、配列に含まれるPublisherシーケンスのデータをインターリーブされたマージシーケンスにマージします。

concatとは異なり、ソースは熱心にサブスクライブされます。

静的mergeメソッドのこのバリアントは、残りのマージバックログが処理されるまでエラーを遅らせます。

これはmergeDelayError:の例です

@Test
public void givenFluxes_whenMergeWithDelayedElementsIsInvoked_thenMergeWithDelayedElements() {
    Flux fluxOfIntegers = Flux.mergeDelayError(1,
      evenNumbers.delayElements(Duration.ofMillis(500L)),
      oddNumbers.delayElements(Duration.ofMillis(300L)));

    StepVerifier.create(fluxOfIntegers)
      .expectNext(1)
      .expectNext(2)
      .expectNext(3)
      .expectNext(5)
      .expectNext(4)
      .expectComplete()
      .verify();
}

3.7. と合併()

静的メソッドmergeWithは、このFluxPublisherからのデータをインターリーブされたマージされたシーケンスにマージします。

繰り返しますが、concatとは異なり、内部ソースは熱心にサブスクライブされます。

@Test
public void givenFluxes_whenMergeWithIsInvoked_thenMergeWith() {
    Flux fluxOfIntegers = evenNumbers.mergeWith(oddNumbers);

    // same StepVerifier as in "3.4. Merge"
    StepVerifier.create(fluxOfIntegers)
      .expectNext(2)
      .expectNext(4)
      .expectNext(1)
      .expectNext(3)
      .expectNext(5)
      .expectComplete()
      .verify();
}

3.8. zip()

静的メソッドzipは、複数のソースを一緒に凝集します。つまり、すべてのソースが1つの要素を放出するのを待ち、これらの要素を出力値に結合します(提供されたコンビネータ関数によって構築されます)。

オペレーターは、いずれかのソースが完了するまでこれを続けます。

@Test
public void givenFluxes_whenZipIsInvoked_thenZip() {
    Flux fluxOfIntegers = Flux.zip(
      evenNumbers,
      oddNumbers,
      (a, b) -> a + b);

    StepVerifier.create(fluxOfIntegers)
      .expectNext(3) // 2 + 1
      .expectNext(7) // 4 + 3
      .expectComplete()
      .verify();
}

ペアリングするevenNumbersの要素が残っていないため、oddNumbersPublisherの要素5は無視されます。

3.9. zipWith()

zipWithは、zipと同じメソッドを実行しますが、パブリッシャーが2つしかない場合のみです。

@Test
public void givenFluxes_whenZipWithIsInvoked_thenZipWith() {
    Flux fluxOfIntegers = evenNumbers
     .zipWith(oddNumbers, (a, b) -> a * b);

    StepVerifier.create(fluxOfIntegers)
      .expectNext(2)  // 2 * 1
      .expectNext(12) // 4 * 3
      .expectComplete()
      .verify();
}

4. 結論

このクイックチュートリアルでは、Publishers.を組み合わせる複数の方法を発見しました

いつものように、例はover on GitHubで利用できます。