Akka stream 急切地阻止阿卡河源头

Akka stream 急切地阻止阿卡河源头,akka-stream,Akka Stream,我有以下(简化的)消费者,他们应该在第一项之后取消Akka流(今天的2.11_2.5快照)Source,但是onNext仍被调用了4次: static Subscriber<Object> println() { return new Subscriber<Object>() { Subscription s; int n; @Override public void onSubscribe(S

我有以下(简化的)消费者,他们应该在第一项之后取消Akka流(今天的2.11_2.5快照)
Source
,但是
onNext
仍被调用了4次:

static Subscriber<Object> println() {
    return new Subscriber<Object>() {

        Subscription s;

        int n;

        @Override
        public void onSubscribe(Subscription s) {
            this.s = s;
            s.request(5);
        }

        @Override
        public void onNext(Object t) {
            System.out.println(Thread.currentThread().getName() 
                  + ": " + t + " - " + (++n));
            if (s != null) {
                s.cancel();
                s = null;
            }
        }

        @Override
        public void onError(Throwable t) {
            t.printStackTrace();
        }

        @Override
        public void onComplete() {
            System.out.println(Thread.currentThread().getName() + ": DONE");
        }
    };
}

public static void main(String[] args) throws Exception {
    Config cfg = ConfigFactory.parseResources(
        AkkaRange.class, "/akka-streams.conf").resolve();
    ActorSystem actorSystem = ActorSystem.create("sys", cfg);

    ActorMaterializer materializer = ActorMaterializer.create(actorSystem);

    Source<Integer, NotUsed> source = Source.repeat(1);

    Publisher<Integer> p = source.runWith(Sink.asPublisher(
         AsPublisher.WITH_FANOUT), materializer);

    p.subscribe(println());

    Thread.sleep(1000);

    actorSystem.terminate();
}
static Subscriber println(){
返回新订户(){
认购书;
int n;
@凌驾
认购的公共无效(认购){
这个.s=s;
s、 请求(5);
}
@凌驾
公共void onNext(对象t){
System.out.println(Thread.currentThread().getName())
+“:“+t+”-“+(+++n));
如果(s!=null){
s、 取消();
s=零;
}
}
@凌驾
公共作废登记员(可丢弃的t){
t、 printStackTrace();
}
@凌驾
未完成的公共空间(){
System.out.println(Thread.currentThread().getName()+“:DONE”);
}
};
}
公共静态void main(字符串[]args)引发异常{
Config cfg=ConfigFactory.parseResources(
AkkaRange.class,“/akka streams.conf”).resolve();
ActorSystem ActorSystem=ActorSystem.create(“sys”,cfg);
ActorMaterializer materializer=ActorMaterializer.create(actorSystem);
震源=震源。重复(1);
Publisher p=source.runWith(Sink.asppublisher(
AsPublisher.WITH_FANOUT),materializer);
p、 订阅(println());
睡眠(1000);
actorSystem.terminate();
}
假设请求为5,但只进行了4次调用,我假设底层消息传递体系结构在检查消息队列中的取消(或进一步请求)消息之前,会以4个批次响应请求

是否有一个设置,使取消发生更迫切


用例类似于互操作计算,其中有一个计算密集型阶段(map),在1-2个源元素之后可能会产生期望的结果,在这种情况下,下游取消流。问题在于,由于这4个批次,剩余的2-3个元素也会执行计算。

订户接口是反应流规范,包括akka流在内的许多库都实现了该规范。本规范规定了以下内容:

在调用Subscription.cancel()后,订户必须准备好接收一个或多个onNext信号,如果仍有请求的元素挂起[请参见3.12]。Subscription.cancel()不保证立即执行基础清理操作


因此,您必须在订阅服务器中手动处理这种情况,否则它将违反规范,因此不适合用于实现规范的库。

我完全了解规范,如果它只是我的订阅服务器,我将忽略更多的onNexts。问题在于,繁重的计算是在Akka流端执行的一个plain map()操作,该操作执行4次而不是一次。我无法控制map()函数的实现。同样,像Rx一样渴望取消也不会违反规范。@akarnokd您是否在订户中发出
请求(5)
,然后发出
取消
?这是一个公平问题。在
map
中执行长时间运行的计算可能会阻止某些信号被急切地注册。有两种可能的解决方案:1)改用
mapsync
并越位运行长时间运行的计算,或2)降低
actor.stream.materialier.sync处理限制
,以确保更快地处理外部信号(但会降低吞吐量)。谢谢,它们似乎是合理的选择。现在将使用选项2,因为没有其他需要吞吐量的Akka流。你能把你的评论作为回答吗?