如何缓冲GroupedFlux的Flux?

时间:2017-06-19 17:34:54

标签: java rx-java reactive-programming rx-java2 project-reactor

我正在尝试在Rexor上实现一个Flux of Flux的缓冲过程。内部通量的每个发射按一些属性分组,并在缓冲持续时间到期后发出。

以下测试(简化为暴露问题)说明了所需的行为:

private static final long STEP_MILLIS = 50;

private static final Duration BUFFER_DURATION = Duration.ofMillis(STEP_MILLIS * 4);

@Test
public void testBuffer() throws Exception {
    List<List<String>> buffers = new ArrayList<>();

    UnicastProcessor<String> queue = UnicastProcessor.create();
    FluxSink<String> sink = queue.sink();

    Flux<Flux<String>> fluxOfFlux = queue.map(Flux::just);

    // Buffering
    fluxOfFlux.flatMap(Function.identity())
        .transform(this::buffer)
        .subscribe(buffers::add);

    sink.next("TEST 1");

    Thread.sleep(BUFFER_DURATION.toMillis() - STEP_MILLIS);  // One "step" before Buffer should close

    assertTrue(buffers.isEmpty());

    sink.next("TEST 2");

    assertTrue(buffers.isEmpty());

    Thread.sleep(STEP_MILLIS * 2); // One "step" after Buffer should close

    assertEquals(1, buffers.size());
    assertEquals(Arrays.asList("TEST 1", "TEST 2"), buffers.get(0));
}

public Flux<List<String>> buffer(Flux<String> publisher) {
    return publisher
        .groupBy(Object::getClass)
        .map(groupFlux -> groupFlux.take(BUFFER_DURATION).collectList())
        .flatMap(Function.identity());
}

此测试按书面形式进行;当我尝试将“flatMap”和“transform”操作组合到一个操作中时会出现问题:

    // Buffering
    fluxOfFlux.flatMap(flux -> flux.transform(this::buffer))
        .subscribe(buffers::add);

然后测试失败,因为放入队列的每个项目都会立即作为单个“缓冲区”发出。

这是不可取的,因为在缓冲之前必须使用flatMap会消除并行处理内部磁通的能力。

这是用Reactor编写的,但是应该使用Observable和类似名称的方法将RxJava与1对1映射。

有人想过我可能做错了吗?

2 个答案:

答案 0 :(得分:2)

我仍然从反应堆开始,但你的代码似乎没有副作用。保持状态(在你的情况下使用List&lt;&gt;缓冲区)可能会导致将来出现令人讨厌的错误。

你可以使用不可变的方法实现同样的目标,如下所示

List<String> tests = Arrays.asList("TEST 1", "TEST 2", "TEST 3", "INTEGRATION 1", "INTEGRATION 2")

@Test
public void test() {
    Flux<List<String>> bufferedFlux = Flux.fromIterable(tests).buffer(Duration.ofSeconds(5));

    StepVerifier.withVirtualTime(() -> bufferedFlux)
            .expectNext(tests)
            .verifyComplete();
}

@Test
public void group() {
    Flux<GroupedFlux<String, String>> flux = Flux
            .fromIterable(tests)
            .groupBy(test -> test.replaceAll("[^a-zA-Z]", ""));

    StepVerifier.create(flux)
            .expectNextMatches(groupedFlux -> groupedFlux.key().equals("TEST"))
            .expectNextMatches(groupedFlux -> groupedFlux.key().equals("INTEGRATION"))
            .verifyComplete();


    Flux<String> sum = flux
            .flatMap(groupedFlux -> groupedFlux
                    .map(test -> test.replaceAll("[^\\d.]", ""))
                    .map(Integer::valueOf)
                    .reduce((t1, t2) -> t1 + t2)
                    .map(total -> groupedFlux.key() + " TOTAL: " + total)
            ).sort();

    StepVerifier.create(sum)
            .expectNext("INTEGRATION TOTAL: 3")
            .expectNext("TEST TOTAL: 6")
            .verifyComplete();
}

答案 1 :(得分:1)

我想出了组合方法的问题:第一个问题就是我想要完成的内容是不可能使用所写的示例数据。

使用精简方法,实际上最终获得缓冲的是每个内部Flux本身。由于这些磁通量中的每一个都在发出其单例后完成,因此这会导致封闭缓冲区完成并被发射。这导致我的应用程序出现了问题。

我不得不通过改变我在内部通量的上游到第一concatMap然后组内部通量的内部通量的方式来解决我的应用程序设计中的这个缺陷。