为什么由BroadcastHub使用Sink.asPublisher创建的Publisher无法正常工作?

时间:2019-04-05 10:14:12

标签: java akka-stream

我们有一个多组件应用程序,在组件之间提供了Reactive Streams API。某些组件是使用Akka Streams实现的,而其他组件是使用例如反应堆。

在一个组件中,我们注意到尽管提供的发布者提供了记录,但Streams并未处理任何消息。我将问题归结为以下情况:

Publisher<String> stringPublisher = Source
    .from(Lists.newArrayList("Hello", "World", "!"))
    .runWith(Sink.asPublisher(AsPublisher.WITH_FANOUT), materializer);

Source<String, NotUsed> allMessages = Source
    .fromPublisher(stringPublisher)
    .toMat(BroadcastHub.of(String.class, 256), Keep.right())
    .run(materializer);

allMessages
    .runForeach(System.out::println, materializer)
    .toCompletableFuture()
    .get();

一个组件提供了一个发布者(由于API使用的是Reactive Streams API,而不是Akka Streams API,因此它必须是发布者)。该发布者是从另一个Akka Streams源创建的,并使用Sink.asPublisher变成了发布者。

现在我们从发布商开始使用BroadcastHub实现流时,根本不会处理任何记录。

我在Reactor Publisher上尝试了相同的操作

Publisher<String> stringPublisher = Flux.fromIterable(Sink.asPublisher(AsPublisher.WITH_FANOUT), materializer);

这按预期工作。不幸的是,我不能排除其他组件从Akka流源创建其发布者的情况。

有人知道哪里出了问题吗?

1 个答案:

答案 0 :(得分:0)

我现在知道如何解决它,如果我开始使用mapMaterializedValue中的resultH广播源的话,它会起作用:

Publisher<String> stringPublisher = Source
    .from(Lists.newArrayList("Hello", "World", "!"))
    .runWith(Sink.asPublisher(AsPublisher.WITH_FANOUT), materializer);

Source
    .fromPublisher(stringPublisher)
    .alsoToMat(BroadcastHub.of(String.class, 256), Keep.right())
    .mapMaterializedValue(source -> source
         .runWith(Sink.foreach(System.out::println, materializer))
    .run(materializer)
    .toCompletableFuture()
    .get();

编辑: TL; DR:解释在Lightbend Forum中说明:

  

这里发生的是,当您附加另一个流时,主流已经完成。有时候,在完成之前看到一些元素可能足够快。

---

因此,看来,在将使用者附加到BroadcastHub创建的Source之前,BroadcastHub实际上已经删除了元素。

文档说它不会不会掉落:

  

如果没有订阅者连接到该集线器,则它将不丢弃任何元素,而是对上游生产者施加反压,直到订阅者到达为止。

https://doc.akka.io/docs/akka/current/stream/stream-dynamic.html

实际上,在大多数情况下都是如此,但是我发现某些情况下行为不正确:

public void testBH3() throws ExecutionException, InterruptedException {
    Publisher<String> stringPublisher = Source
        .from(Lists.newArrayList("Hello", "World", "!"))
        .runWith(Sink.asPublisher(AsPublisher.WITH_FANOUT), materializer);

    Source<String, NotUsed> allMessages = Source
        .fromPublisher(stringPublisher)
        .toMat(BroadcastHub.of(String.class, 256), Keep.right())
        .run(materializer);

    allMessages
        .runForeach(System.out::println, materializer)
        .toCompletableFuture()
        .get();
}

public void repeat() throws ExecutionException, InterruptedException {
    for (int i = 0; i < 100; i++) {
        testBH3();
        System.out.println("------");
    }
}

这在100例案例中的3例中有效。但是以下情况在所有情况下都有效(我只是添加了一个油门以使元素生成速度更慢):

public void testBH3() throws ExecutionException, InterruptedException {
    Publisher<String> stringPublisher = Source
        .from(Lists.newArrayList("Hello", "World", "!"))
        .runWith(Sink.asPublisher(AsPublisher.WITH_FANOUT), materializer);

    Source<String, NotUsed> allMessages = Source
        .fromPublisher(stringPublisher)
        .throttle(1, Duration.ofSeconds(1))
        .toMat(BroadcastHub.of(String.class, 256), Keep.right())
        .run(materializer);

    allMessages
        .runForeach(System.out::println, materializer)
        .toCompletableFuture()
        .get();
}

因此,在我看来,当没有接收器连接时,BroadcastHub有时会丢弃元素。