Combinaison d’éditeurs dans Project Reactor

Combinaison d'éditeurs dans le réacteur de projet

1. Vue d'ensemble

Dans cet article, nous allons examiner différentes manières de combinerPublishers enProject Reactor.

2. Dépendances Maven

Configurons notre exemple avec les dépendancesProject Reactor:


    io.projectreactor
    reactor-core
    3.1.4.RELEASE


    io.projectreactor
    reactor-test
    3.1.4.RELEASE
    test

3. Association d'éditeurs

Étant donné un scénario où l'on doit travailler avecFlux<T> ouMono<T>, il existe différentes manières de combiner des flux.

Créons quelques exemples pour illustrer l’utilisation de méthodes statiques dans la classeFlux<T> telles queconcat, concatWith, merge, zip etcombineLatest.

Nos exemples utiliseront deux éditeurs de typeFlux<Integer>, à savoirevenNumbers, qui est unFlux deInteger et contient une suite de nombres pairs commençant par 1 (min variable) et limité par 5 (max variable).

Nous allons créeroddNumbers, également unFlux de typeInteger de nombres impairs:

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()

La méthodeconcat exécute une concaténation des entrées, retransmettant les éléments émis par les sources en aval.

La concaténation est réalisée en souscrivant séquentiellement à la première source, puis en attendant qu'elle se termine avant de s'abonner à la suivante, et ainsi de suite jusqu'à la fin de la dernière source. Toute erreur interrompt immédiatement la séquence et est transmise en aval.

Voici un exemple rapide:

@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 ()

En utilisant la méthode statiqueconcatWith, nous allons produire une concaténation de deux sources de typeFlux<T> comme résultat:

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

    // same stepVerifier as in the concat example above
}

3.3. combineLatest()

La méthode statique FluxcombineLatest générera des données fournies par la combinaison de la valeur la plus récemment publiée de chacune des sources de l'éditeur.

Voici un exemple d'utilisation de cette méthode avec deux sourcesPublisher et unBiFunction comme paramètres:

@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();
}

Nous pouvons voir ici que la fonctioncombineLatest applique la fonction «a + b» en utilisant le dernier élément deevenNumbers (4) et les éléments deoddNumbers (1,3,5), générant ainsi le séquence5,7,9.

3.4. merge()

La fonctionmerge exécute une fusion des données des séquencesPublisher contenues dans un tableau en une séquence fusionnée entrelacée:

@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();
}

Une chose intéressante à noter est que, contrairement àconcat (lazy subscription), les sources sont abonnées avec enthousiasme.

Ici, nous pouvons voir un résultat différent de la fonctionmerge si nous insérons un délai entre les éléments des éditeurs:

@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()

La méthodemergeSequential fusionne les données des séquencesPublisher fournies dans un tableau dans une séquence fusionnée ordonnée.

Contrairement àconcat, les sources sont abonnées avec enthousiasme.

De plus, contrairement àmerge, leurs valeurs émises sont fusionnées dans la séquence finale dans l'ordre d'abonnement:

@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()

LemergeDelayError fusionne les données des séquences Publisher contenues dans un tableau en une séquence fusionnée entrelacée.

Contrairement àconcat, les sources sont abonnées avec enthousiasme.

Cette variante de la méthode statiquemerge retardera toute erreur jusqu'à ce que le reste du backlog de fusion ait été traité.

Voici un exemple demergeDelayError:

@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. fusionner avec()

La méthode statiquemergeWith fusionne les données de ceFlux et d'unPublisher dans une séquence fusionnée entrelacée.

Encore une fois, contrairement àconcat, les sources internes sont abonnées avec enthousiasme:

@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()

La méthode statiquezip agglutine plusieurs sources ensemble, c'est-à-dire attend que toutes les sources émettent un élément et combine ces éléments en une valeur de sortie (construite par la fonction combinateur fournie).

L’opérateur continuera à le faire jusqu’à ce que l’une des sources soit complétée:

@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();
}

Comme il n'y a plus d'élément deevenNumbers à appairer, l'élément 5 deoddNumbers Publisher est ignoré.

3.9. zipWith()

LezipWith exécute la même méthode quezip, mais uniquement avec deux éditeurs:

@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. Conclusion

Dans ce rapide didacticiel, nous avons découvert plusieurs façons de combinerPublishers.

Comme toujours, les exemples sont disponibles enover on GitHub.