【问题标题】:Sliding window based on Akka actor source not behaving as expected基于 Akka 演员源的滑动窗口未按预期运行
【发布时间】:2021-06-13 17:55:05
【问题描述】:

使用下面的代码,我尝试使用演员作为源并发送 Double 类型的消息以通过滑动窗口进行处理。

滑动窗口被定义为sliding(2, 2)来计算每个发送的twp值序列。

发送消息:

        actorRef.tell(10, ActorRef.noSender());
        actorRef.tell(20, ActorRef.noSender());
        actorRef.tell(30, ActorRef.noSender());
        actorRef.tell(40, ActorRef.noSender());
        actorRef.tell(20, ActorRef.noSender());

应按如下方式计算平均值:

10 + 20 / 2 = 15
30 + 40 / 2 = 35

但在下面的代码中似乎没有调用计算。

这里我输出值:

    movingAverage.runForeach(n -> {
        if( n > 0){
            System.out.println(n);
        }
    }, system);

源代码:

import akka.Done;
import akka.actor.ActorRef;
import akka.stream.CompletionStrategy;
import akka.stream.OverflowStrategy;
import akka.stream.javadsl.Sink;
import akka.stream.javadsl.Source;

import java.util.Optional;

public class FilterThreshold {

    public static void main(String[] args) {

        final akka.actor.ActorSystem system = akka.actor.ActorSystem.create("Source");

        final int bufferSize = 1;
        final Source<Double, ActorRef> source =
                Source.actorRef(
                        elem -> {
                            // complete stream immediately if we send it Done
                            if (elem == Done.done()) {
                                return Optional.of(CompletionStrategy.immediately());
                            } else {
                                return Optional.empty();
                            }
                        },
                        // never fail the stream because of a message
                        elem -> Optional.empty(),
                        bufferSize,
                        OverflowStrategy.dropHead());
        ActorRef actorRef = source.to(Sink.foreach(System.out::println)).run(system);

        actorRef.tell(10, ActorRef.noSender());
        actorRef.tell(20, ActorRef.noSender());
        actorRef.tell(30, ActorRef.noSender());
        actorRef.tell(40, ActorRef.noSender());
        actorRef.tell(20, ActorRef.noSender());

        Source<Double, ActorRef> movingAverage = source
                .sliding(2, 2)
                .map(window -> (window.stream().mapToDouble(i -> i).sum()) / window.size());


        movingAverage.runForeach(n -> {
            if( n > 0){
                System.out.println(n);
            }
        }, system);

    }
}

我已经编辑了来自https://doc.akka.io/docs/akka/current/stream/operators/Source-or-Flow/sliding.html的代码

如何应用定义为movingAverage 的滑动窗口函数来计算通过Akka actor actorRef 发送的值?

更新:

permaterialize 方法将参与者系统作为参数。

从以下位置更新代码:

final Pair<ActorRef, Source<Double, ActorRef>> prematPair = source.preMaterialize();

到:

final Pair<ActorRef, Source<Double, ActorRef>> prematPair = source.preMaterialize(system);

导致编译时错误:

Required type:
Pair
<ActorRef,
Source<Double, ActorRef>>

Provided:
Pair
<ActorRef,
Source<Double, NotUsed>>

我应该使用其他方法吗?

已发布更新代码:

import akka.Done;
import akka.NotUsed;
import akka.actor.ActorRef;
import akka.japi.Pair;
import akka.stream.CompletionStrategy;
import akka.stream.OverflowStrategy;
import akka.stream.javadsl.Flow;
import akka.stream.javadsl.Source;

import java.util.Optional;

public class FilterThreshold {

    public static void main(String[] args) {

        final akka.actor.ActorSystem system = akka.actor.ActorSystem.create("Source");

        final int bufferSize = 1;
        final Source<Double, ActorRef> source =
                Source.actorRef(
                        elem -> {
                            System.out.println("elem is "+elem);
                            // complete stream immediately if we send it Done
                            if (elem == Done.done()) {
                                return Optional.of(CompletionStrategy.immediately());
                            } else {
                                return Optional.empty();
                            }
                        },
                        // never fail the stream because of a message
                        elem -> Optional.empty(),
                        bufferSize,
                        OverflowStrategy.dropHead());

        // source is as before
        final Pair<ActorRef, Source<Double, ActorRef>> prematPair = source.preMaterialize(system);

        Flow<Double, Double, NotUsed> movingAverageFlow =
                Flow.of(Double.class)
                        .sliding(2, 2)
                        .map(window -> (window.stream().mapToDouble(i -> i).sum()) / window.size());

        final Source<Double, ActorRef> prematSource = prematPair.second();

        prematSource.via(movingAverageFlow).runForeach(n -> {
            System.out.println("n is "+n);
            if (n > 0) {
                System.out.println(n);
            }
        }, system);

        final ActorRef actorRef = prematPair.first();

        actorRef.tell(10, ActorRef.noSender());
        actorRef.tell(20, ActorRef.noSender());
        actorRef.tell(20, ActorRef.noSender());
        actorRef.tell(20, ActorRef.noSender());
        actorRef.tell(20, ActorRef.noSender());

    }
}

更新2:

使用代码:

import akka.Done;
import akka.NotUsed;
import akka.actor.ActorRef;
import akka.japi.Pair;
import akka.stream.CompletionStrategy;
import akka.stream.OverflowStrategy;
import akka.stream.javadsl.Flow;
import akka.stream.javadsl.Source;

import java.util.Optional;

public class FilterThreshold {

    public static void main(String[] args) {

        final akka.actor.ActorSystem system = akka.actor.ActorSystem.create("Source");

        final int bufferSize = 1;
        final Source<Double, ActorRef> source =
                Source.actorRef(
                        elem -> {
                            System.out.println("elem is "+elem);
                            // complete stream immediately if we send it Done
                            if (elem == Done.done()) {
                                return Optional.of(CompletionStrategy.immediately());
                            } else {
                                return Optional.empty();
                            }
                        },
                        // never fail the stream because of a message
                        elem -> Optional.empty(),
                        bufferSize,
                        OverflowStrategy.dropHead());

        // source is as before
        final Pair<ActorRef, Source<Double, NotUsed>> prematPair = source.preMaterialize(system);
        final ActorRef actorRef = prematPair.first();
        final Source<Double, NotUsed> prematSource = prematPair.second();

        Flow<Double, Double, NotUsed> movingAverageFlow =
                Flow.of(Double.class)
                        .sliding(2, 2)
                        .map(window -> (window.stream().mapToDouble(i -> i).sum()) / window.size());

        prematSource.via(movingAverageFlow).runForeach(n -> {
            System.out.println("n is "+n);
            if (n > 0) {
                System.out.println(n);
            }
        }, system);

        actorRef.tell(10, ActorRef.noSender());
        actorRef.tell(20, ActorRef.noSender());
        actorRef.tell(20, ActorRef.noSender());
        actorRef.tell(20, ActorRef.noSender());
        actorRef.tell(20, ActorRef.noSender());

        prematSource.run(system);

    }
}

打印:

elem is 10
elem is 20
elem is 20
elem is 20
elem is 20

看来消息发送正确,但移动平均线并未实现。

使用prematSource.run(system); 不是实现价值的正确方法吗?

【问题讨论】:

    标签: java akka akka-stream


    【解决方案1】:

    简短的回答是,您的source 是实现Source&lt;Double, ActorRef&gt; 的各种方法,并且每个实现最终都是不同的来源。

    在您的代码中,source.to(Sink.foreach(System.out::println)).run(system) 是一个流,具体化的actorRef 仅连接到该流,并且

    movingAverage.runForeach(n -> {
        if( n > 0){
            System.out.println(n);
        }
    }, system);
    

    是一个完全独立的流,具有不同的物化ActorRef(由于runForeach 物化为CompletionStage&lt;Done&gt;,最终被丢弃。

    在处理Source.actorRef 时,最好在运行流之前预先实现源:

    import akka.NotUsed
    import akka.japi.Pair
    import akka.stream.javadsl.Flow
    
    // source is as before
    final Pair<ActorRef, Source<Double, NotUsed>> prematPair = source.preMaterialize(system);
    final ActorRef actorRef = prematPair.first();
    final Source<Double, NotUsed> prematSource = prematPair.second();
    
    Flow<Double, Double, NotUsed> movingAverageFlow =
        Flow.of(Double.class)
            .sliding(2, 2)
            .map(window -> (window.stream().mapToDouble(i -> i).sum()) / window.size());
    
    prematSource.via(movingAverageFlow).runForeach(n -> {
        if (n > 0) {
          System.out.println(n);
        }
    }, system);
    

    (抱歉,我的 Java 生锈了)

    【讨论】:

    • 抱歉,我在脑海中编译它...prematPairPair&lt;ActorRef, Source&lt;Double, NotUsed&gt;&gt;prematSourceSource&lt;Double, NotUsed&gt;。我现在基本上只写 Scala,我通常不会在其中包含显式类型归属。
    • preMaterialize 类似:在Scala 中通过system 实现流是隐含的魔法:)
    • 再次感谢,请查看我的最新更新。我认为我对如何实现价值有疑问?
    • 你可以尝试明确发送双打,例如actorRef.tell(10.0, ActorRef.noSender())?
    • 成功了!另外,事实证明我不需要'prematSource.run(system);'
    猜你喜欢
    • 2017-09-09
    • 1970-01-01
    • 2015-07-05
    • 1970-01-01
    • 2010-10-23
    • 2010-11-10
    • 1970-01-01
    • 2019-02-09
    • 2019-02-18
    相关资源
    最近更新 更多