我正在使用Akka(版本2.4.17
)来构建Java中的观察流程(让我们说类型<T>
的元素保持通用)。
我的要求是,此Flow应该是可自定义的,以便在单个时间到达时提供最大数量的观察。例如,它应该能够每分钟传送最多2个观察结果(第一个到达,其余部分可以被丢弃)。
我非常仔细地看了Akka文档,特别是this page详细介绍了内置阶段及其语义。
到目前为止,我尝试了以下方法。
使用throttle
和shaping()
模式(在超出限制时不关闭流):
Flow.of(T.class)
.throttle(2,
new FiniteDuration(1, TimeUnit.MINUTES),
0,
ThrottleMode.shaping())
使用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
以前的方法可以为每单位时间提供正确的观察次数,但这些观察结果会被保留,并且仅在时间窗结束时传递(因此会有额外的延迟)。
举一个更具体的例子,如果以下观察到达我的流程:
[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帖子,感谢任何帮助;)
答案 0 :(得分:3)
我无法想到一个开箱即用的解决方案。由于铲斗模型的实施方式,节流阀将以稳定的流量排放,而不是在每个时间段的开始都有允许的租赁。
要获得您之后必须创建自己的自定义速率限制阶段(可能不那么难)的确切行为。您可以在此处找到有关如何创建自定义阶段的文档:http://doc.akka.io/docs/akka/2.5.0/java/stream/stream-customize.html#custom-linear-processing-stages-using-graphstage
一个可行的设计是有一个容差计数器,说明每个间隔重置多少个可以发出的元素,每个传入的元素从计数器中减去一个并发出一个,当容差用完时你继续拉上游但是丢弃元素而不是发射它们。使用TimerGraphStageLogic
GraphStageLogic
可以设置可以重置限额的定时回调。
答案 1 :(得分:2)
答案 2 :(得分:0)
感谢@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;
}
}
};
}
}