Объединение издателей в Project Reactor
1. обзор
В этой статье мы рассмотрим различные способы комбинированияPublishers вProject 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>, существуют разные способы объединения потоков.
Давайте создадим несколько примеров, чтобы проиллюстрировать использование статических методов в классеFlux<T>, таких какconcat, concatWith, merge, zip иcombineLatest..
В наших примерах будут использоваться два издателя типаFlux<Integer>, а именноevenNumbers, который являетсяFlux изInteger и содержит последовательность четных чисел, начинающуюся с 1 (min) и ограничивается 5 (переменнаяmax).
Мы создадимoddNumbers, а такжеFlux типаInteger нечетных чисел:
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> в результате:
@Test
public void givenFluxes_whenConcatWithIsInvoked_thenConcatWith() {
Flux fluxOfIntegers = evenNumbers.concatWith(oddNumbers);
// same stepVerifier as in the concat example above
}
3.3. combineLatest()
Статический метод FluxcombineLatest будет генерировать данные, предоставляемые комбинацией самых последних опубликованных значений из каждого из источников Publisher.
Вот пример использования этого метода с двумя источниками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();
}
Мы можем видеть здесь, что функцияcombineLatest применила функцию «a + b», используя последний элементevenNumbers (4) и элементыoddNumbers (1,3,5), таким образом создавая последовательность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 subscription), источники подписываются охотно.
Здесь мы можем увидеть другой результат функцииmerge, если мы вставим задержку между элементами Publishers:
@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 объединяет данные из этогоFlux иPublisher в чередующуюся объединенную последовательность.
Опять же, в отличие от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 агглютинирует несколько источников вместе, то есть ожидает, пока все источники испускают один элемент, и объединяет эти элементы в выходное значение (созданное предоставленной функцией комбинатора).
Оператор будет продолжать делать это до тех пор, пока какой-либо из источников не завершит:
@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 не осталось элемента для создания пары, элемент 5 от издателяoddNumbers игнорируется.
3.9. zipWith()
zipWith выполняет тот же метод, что иzip, но только с двумя издателями:
@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.