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