简体   繁体   English

处理 Java Akka 流中更改的源数据

[英]Processing changing source data in Java Akka streams

2 threads are started.启动了 2 个线程。 dataListUpdateThread adds the number 2 to a List . dataListUpdateThread将数字 2 添加到List processFlowThread sums the values in the same List and prints the summed list to the console. processFlowThread对同一List中的值求和,并将求和后的列表打印到控制台。 Here is the code:这是代码:

import akka.NotUsed;
import akka.actor.ActorSystem;
import akka.stream.javadsl.Sink;
import akka.stream.javadsl.Source;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutionException;

import static java.lang.Thread.sleep;


public class SourceExample {

    private final static ActorSystem system = ActorSystem.create("SourceExample");

    private static void delayOneSecond() {
        try {
            sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private static void printValue(CompletableFuture<Integer> integerCompletableFuture) {
        try {
            System.out.println("Sum is " + integerCompletableFuture.get().intValue());
        } catch (ExecutionException | InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {

        final List dataList = new ArrayList<Integer>();
        final Thread dataListUpdateThread = new Thread(() -> {
            while (true) {
                dataList.add(2);
                System.out.println(dataList);
                delayOneSecond();
            }
        });
        dataListUpdateThread.start();

        final Thread processFlowThread = new Thread(() -> {
            while (true) {
                final Source<Integer, NotUsed> source = Source.from(dataList);

                final Sink<Integer, CompletionStage<Integer>> sink =
                        Sink.fold(0, (agg, next) -> agg + next);

                final CompletionStage<Integer> sum = source.runWith(sink, system);

                printValue(sum.toCompletableFuture());

                delayOneSecond();
            }
        });

        processFlowThread.start();
    }
}

I've tried to create the simplest example to frame the question.我试图创建一个最简单的例子来构建问题。 dataListUpdateThread could be populating the List from a REST service or Kafka topic instead of just adding the value 2 to the List. dataListUpdateThread可以从 REST 服务或 Kafka 主题填充列表,而不仅仅是将值 2 添加到列表中。 Instead of using Java threads how should this scenario be implemented?而不是使用 Java 线程应该如何实现这种场景? In other words, how to share dataList to the Akka Stream for processing?也就是说,如何将dataList共享给Akka Stream进行处理?

Mutating the collection passed to Source.from is only ever going to accomplish this by coincidence: if the collection is ever exhausted, Source.from will complete the stream.改变传递给Source.from的集合只是巧合:如果集合用尽, Source.from将完成 stream。 This is because it's intended for finite, strictly evaluated data (the use cases are basically: a) simple examples for the docs and b) situations where you want to bound resource consumption when performing an operation for a collection in the background (think a list of URLs that you want to send HTTP requests to)).这是因为它适用于有限的、严格评估的数据(用例基本上是:a)文档的简单示例和 b)在后台执行集合操作时想要限制资源消耗的情况(想想列表您想要发送 HTTP 请求到的 URL))。

NB: I haven't written Java to any great extent since the Java 7 days, so I'm not providing Java code, just an outline of approaches.注意:自从 Java 7 天以来,我没有在很大程度上写过 Java,所以我没有提供 Java 代码,只是一个概述。

As mentioned in a prior answer Source.queue is probably the best option (besides using something like Akka HTTP or an Alpakka connector).正如前面的回答中提到的, Source.queue可能是最好的选择(除了使用 Akka HTTP 或 Alpakka 连接器之类的东西)。 In a case such as this, where the stream's materialized value is a future that won't be completed until the stream completes, that Source.queue will never complete the stream (because there's no way for it to know that its reference is the only reference), introducing a KillSwitch and propagating that through viaMat and toMat would give you the ability to decide outside of the stream to complete the stream.在这种情况下,流的具体化值是一个未来,直到 stream 完成后才会完成, Source.queue将永远不会完成 stream(因为它无法知道它的引用是唯一的参考),引入一个KillSwitch并通过viaMattoMat传播它将使您能够在 stream 之外决定完成 stream。

An alternative to Source.queue , is Source.actorRef , which lets you send a distinguished message ( akka.Done.done() in the Java API is pretty common for this). Source.actorRef Source.queue它可以让您发送一条可区分的消息( akka.Done.done()很常见)。 That source materializes as an ActorRef to which you can tell messages, and those messages (at least those which match the type of the stream) will be available for the stream to consume.该源具体ActorRef ,您可以向其tell消息,并且这些消息(至少与流类型匹配的消息)将可供 stream 使用。

With both Source.queue and Source.actorRef , it's often useful to prematerialize them: the alternative in a situation like your example where you also want the materialized value of the sink, is to make heavy use of the Mat operators to customize materialized values (in Scala, it's possible to use tuples to at least simplify combining multiple materialized values, but in Java, once you got beyond a pair (as you would with queue ), I'm pretty sure you'd have to define a class just to hold the three (queue, killswitch, future for completed value) materialized values).使用Source.queueSource.actorRefprematerialize它们通常很有用:在像您的示例这样您还需要接收器的物化值的情况下,替代方法是大量使用Mat运算符来自定义物化值(在 Scala 中,可以使用元组至少简化多个物化值的组合,但是在 Java 中,一旦超出了一对(就像使用queue一样),我很确定你必须定义一个 ZA2DZC21 到 222F8EBC2保存三个(队列,killswitch,完成值的未来)物化值)。

It's also worth noting that, since Akka Streams run on actors in the background (and thus get scheduled as needed onto the ActorSystem 's threads), there's almost never a reason to create a thread on which to run a stream.还值得注意的是,由于 Akka 流在后台运行在 Actor 上(因此根据需要安排到ActorSystem的线程上),几乎没有理由创建一个线程来运行 stream。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM