Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/379.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java Akka流-在不引入延迟的情况下限制流速_Java_Stream_Akka - Fatal编程技术网

Java Akka流-在不引入延迟的情况下限制流速

Java Akka流-在不引入延迟的情况下限制流速,java,stream,akka,Java,Stream,Akka,我正在与Akka(版本2.4.17)合作,用Java构建一个观察流(比如说类型的元素以保持通用性) 我的要求是,这个流程应该是可定制的,以便在每个时间单位到达时提供最大数量的观察值。例如,它每分钟最多可以提供2个观察结果(第一个到达,其余的可以丢弃) 我仔细查看了Akka文档,特别是其中详细介绍了内置阶段及其语义 到目前为止,我尝试了以下方法 使用节流和整形()模式(在超过限制时不关闭流): 使用groupedWith和中间自定义方法: final int nbObsMax = 2; Fl

我正在与Akka(版本
2.4.17
)合作,用Java构建一个观察流(比如说
类型的元素以保持通用性)

我的要求是,这个流程应该是可定制的,以便在每个时间单位到达时提供最大数量的观察值。例如,它每分钟最多可以提供2个观察结果(第一个到达,其余的可以丢弃)

我仔细查看了Akka文档,特别是其中详细介绍了内置阶段及其语义

到目前为止,我尝试了以下方法

  • 使用
    节流
    整形()
    模式(在超过限制时不关闭流):

  • 使用
    groupedWith
    和中间自定义方法:

    final int nbObsMax = 2;
    
    Flow.of(T.class)
        .groupedWithin(Integer.MAX_VALUE, new FiniteDuration(1, TimeUnit.MINUTES))
        .map(list -> {
             List<T> listToTransfer = new ArrayList<>();
             for (int i = list.size()-nbObsMax ; i>0 && i<list.size() ; i++) {
                 listToTransfer.add(new T(list.get(i)));
             }
             return listToTransfer;
        })
        .mapConcat(elem -> elem)  // Splitting List<T> in a Flow of T objects
    
    final int nbObsMax=2;
    流量(T级)
    .groupedWithin(整数.MAX_值,新的限定时间(1,时间单位.min))
    .map(列表->{
    List listToTransfer=new ArrayList();
    for(int i=list.size()-nbObsMax;i>0&&i elem)//在T对象流中拆分列表
    
以前的方法为我提供了每单位时间的正确观察次数,但这些观察结果会被保留,并且仅在时间窗口结束时交付(因此会有额外的延迟)

举一个更具体的例子,如果以下观察结果进入我的流程:

[Obs1 t=0s][Obs2 t=45s][Obs3 t=47s][Obs4 t=121s][Obs5 t=122s]

应在以下各项到达后立即输出(此处可忽略处理时间):

窗口1:[Obs1 t~0s][Obs2 t~45s] 窗口2:[Obs4 t~121s][Obs5 t~122s]


感谢您阅读我的第一篇StackOverflow帖子,我们将不胜感激。

我想不出一个现成的解决方案来满足您的需求。节流阀将以稳定的方式排放,因为它是如何在bucket模型中实现的,而不是在每个时间段开始时都有一个允许的租约

要获得您所追求的确切行为,您必须创建自己的自定义利率限制阶段(这可能并不难)。您可以在此处找到有关如何创建自定义阶段的文档:


一种可行的设计是,设置一个余量计数器,说明每个间隔可以释放多少个元素,对于每个传入元素,您从计数器中减去一个元素并发射,当余量用完时,您继续向上游拉动,但丢弃元素而不是发射它们。使用
TimerGraphStageLogic
对于
GraphStageLogic
允许您设置一个定时回调,以重置余量。

我认为这正是您需要的:

感谢@johanandren的回答,我成功实现了一个满足我需求的自定义基于时间的GraphStage

如果有人感兴趣,我将在下面发布代码:

import akka.stream.Attributes;
import akka.stream.FlowShape;
import akka.stream.Inlet;
import akka.stream.Outlet;
import akka.stream.stage.*;
import scala.concurrent.duration.FiniteDuration;

public class CustomThrottleGraphStage<A> extends GraphStage<FlowShape<A, A>> {

    private final FiniteDuration silencePeriod;
    private int nbElemsMax;

    public CustomThrottleGraphStage(int nbElemsMax, FiniteDuration silencePeriod) {
        this.silencePeriod = silencePeriod;
        this.nbElemsMax = nbElemsMax;
    }

    public final Inlet<A> in = Inlet.create("TimedGate.in");
    public final Outlet<A> out = Outlet.create("TimedGate.out");

    private final FlowShape<A, A> shape = FlowShape.of(in, out);
    @Override
    public FlowShape<A, A> shape() {
        return shape;
    }

    @Override
    public GraphStageLogic createLogic(Attributes inheritedAttributes) {
        return new TimerGraphStageLogic(shape) {

            private boolean open = false;
            private int countElements = 0;

            {
                setHandler(in, new AbstractInHandler() {
                    @Override
                    public void onPush() throws Exception {
                        A elem = grab(in);
                        if (open || countElements >= nbElemsMax) {
                            pull(in);  // we drop all incoming observations since the rate limit has been reached
                        }
                        else {
                            if (countElements == 0) { // we schedule the next instant to reset the observation counter
                                scheduleOnce("resetCounter", silencePeriod);
                            }
                            push(out, elem); // we forward the incoming observation
                            countElements += 1; // we increment the counter
                        }
                    }
                });
                setHandler(out, new AbstractOutHandler() {
                    @Override
                    public void onPull() throws Exception {
                        pull(in);
                    }
                });
            }

            @Override
            public void onTimer(Object key) {
                if (key.equals("resetCounter")) {
                    open = false;
                    countElements = 0;
                }
            }
        };
    }
}
导入akka.stream.Attributes;
导入akka.stream.FlowShape;
进口akka.stream.import;
进口akka.stream.Outlet;
导入akka.stream.stage.*;
导入scala.concurrent.duration.FiniteDuration;
公共类CustomThrottleGraphStage扩展GraphStage{
私人最终期限沉默期;
私人恩贝莱姆斯马斯;
公共定制节流阀阶段(内部nbElemsMax,有限持续时间沉默期){
this.沉默期=沉默期;
this.nbElemsMax=nbElemsMax;
}
公共最终入口in=入口.create(“TimedGate.in”);
public final Outlet out=Outlet.create(“TimedGate.out”);
私有最终FlowShape形状=FlowShape.of(in,out);
@凌驾
公共流形状(){
返回形状;
}
@凌驾
公共图形标记逻辑createLogic(属性继承属性){
返回新的TimerGraphStageLogic(形状){
私有布尔开放=假;
私有int countElements=0;
{
setHandler(在,新的AbstractInHandler()中){
@凌驾
public void onPush()引发异常{
A元素=抓取(in);
如果(打开| | countElements>=nbElemsMax){
拉(入);//由于达到速率限制,我们放弃所有传入的观察值
}
否则{
如果(countElements==0){//我们安排下一个时刻重置观测计数器
scheduleOnce(“重置计数器”,静音周期);
}
推(出去,elem);//我们把即将到来的观察向前推进
countElements+=1;//我们递增计数器
}
}
});
setHandler(out,新的AbstractOutHandler(){
@凌驾
public void onPull()引发异常{
拉(入);
}
});
}
@凌驾
公用void onTimer(对象键){
if(键等于(“重置计数器”)){
开=假;
countElements=0;
}
}
};
}
}

感谢您的回答。我已经看过这个解决方案,但它并不令人满意,因为它使用了一个专用的参与者,而不是一个GraphStage(我需要在我的应用程序的其他部分重用这个模块)。我完全错过了
TimerGraphStageLogic
!我将开始实现我自己的模块。感谢您的回答:)添加了实现您建议的解决方案的代码。再次感谢。
import akka.stream.Attributes;
import akka.stream.FlowShape;
import akka.stream.Inlet;
import akka.stream.Outlet;
import akka.stream.stage.*;
import scala.concurrent.duration.FiniteDuration;

public class CustomThrottleGraphStage<A> extends GraphStage<FlowShape<A, A>> {

    private final FiniteDuration silencePeriod;
    private int nbElemsMax;

    public CustomThrottleGraphStage(int nbElemsMax, FiniteDuration silencePeriod) {
        this.silencePeriod = silencePeriod;
        this.nbElemsMax = nbElemsMax;
    }

    public final Inlet<A> in = Inlet.create("TimedGate.in");
    public final Outlet<A> out = Outlet.create("TimedGate.out");

    private final FlowShape<A, A> shape = FlowShape.of(in, out);
    @Override
    public FlowShape<A, A> shape() {
        return shape;
    }

    @Override
    public GraphStageLogic createLogic(Attributes inheritedAttributes) {
        return new TimerGraphStageLogic(shape) {

            private boolean open = false;
            private int countElements = 0;

            {
                setHandler(in, new AbstractInHandler() {
                    @Override
                    public void onPush() throws Exception {
                        A elem = grab(in);
                        if (open || countElements >= nbElemsMax) {
                            pull(in);  // we drop all incoming observations since the rate limit has been reached
                        }
                        else {
                            if (countElements == 0) { // we schedule the next instant to reset the observation counter
                                scheduleOnce("resetCounter", silencePeriod);
                            }
                            push(out, elem); // we forward the incoming observation
                            countElements += 1; // we increment the counter
                        }
                    }
                });
                setHandler(out, new AbstractOutHandler() {
                    @Override
                    public void onPull() throws Exception {
                        pull(in);
                    }
                });
            }

            @Override
            public void onTimer(Object key) {
                if (key.equals("resetCounter")) {
                    open = false;
                    countElements = 0;
                }
            }
        };
    }
}