Apache flink Apache Flink-从另一个流访问WindowedStream的内部缓冲区';s映射函数

Apache flink Apache Flink-从另一个流访问WindowedStream的内部缓冲区';s映射函数,apache-flink,flink-streaming,Apache Flink,Flink Streaming,我有一个基于Apache Flink的流媒体应用程序,具有以下设置: 数据源:每分钟生成一次数据 使用CountWindow(大小=100,滑动=1)的窗口化流(滑动计数窗口) ProcessWindowFunction对窗口中的数据应用一些计算(例如F(x)) 使用输出流的数据接收器 这个很好用。现在,我想让用户能够提供函数G(x),并将其应用于窗口中的当前数据,并将输出实时发送给用户 我不是在问如何应用任意函数G(x)-我是在使用动态脚本来实现这一点。我询问如何从另一个流的map函数访问

我有一个基于Apache Flink的流媒体应用程序,具有以下设置:

  • 数据源:每分钟生成一次数据
  • 使用CountWindow(大小=100,滑动=1)的窗口化流(滑动计数窗口)
  • ProcessWindowFunction对窗口中的数据应用一些计算(例如F(x))
  • 使用输出流的数据接收器
这个很好用。现在,我想让用户能够提供函数G(x),并将其应用于窗口中的当前数据,并将输出实时发送给用户

我不是在问如何应用任意函数G(x)-我是在使用动态脚本来实现这一点。我询问如何从另一个流的map函数访问窗口中的缓冲数据

一些代码需要澄清

DataStream<Foo> in  = .... // source data produced every minute
    in
       .keyBy(new MyKeySelector())
       .countWindow(100, 1)
       .process(new MyProcessFunction())
       .addSink(new MySinkFunction())

// The part above is working fine. Note that windowed stream created by countWindow() function above has to maintain internal buffer. Now the new requirement

DataStream<Function> userRequest  = .... // request function from user

userRequest.map(new MapFunction<Function, FunctionResult>(){
   public FunctionResult map(Function Gx) throws Exception {
         Iterable<Foo> windowedDataFromAbove = // HOW TO GET THIS???
         FunctionResult result = Gx.apply(windowedDataFromAbove);
         return result;

   }
DataStream in=..//每分钟生成的源数据
在里面
.keyBy(新的MyKeySelector())
.countWindow(100,1)
.process(新的MyProcessFunction())
.addSink(新的MySinkFunction())
//上面的部分工作正常。请注意,上面的countWindow()函数创建的窗口流必须维护内部缓冲区。现在新的要求
DataStream userRequest=..//用户请求函数
userRequest.map(新的MapFunction(){
公共函数结果映射(函数Gx)引发异常{
Iterable windowedDataFromAbove=//如何获取此信息???
FunctionResult=Gx.apply(上面的WindowedData);
返回结果;
}

})

连接两条流,然后使用。获取函数流的方法调用可以将它们应用到另一个方法调用的窗口中


如果您想要广播函数,那么您需要使用Flink 1.5(它支持连接键控流和广播流),或者使用一些直升机特技创建一个可以包含Foo和函数类型的流,并适当复制函数(和密钥生成)模拟广播。

假设Fx动态聚合传入的FOO,Gx处理窗口的FOO,您应该能够实现以下目标:

DataStream<Function> userRequest  = .... // request function from user
Iterator<Function> iter = DataStreamUtils.collect(userRequest);
Function Gx = iter.next();

DataStream<Foo> in  = .... // source data
 .keyBy(new MyKeySelector())
 .countWindow(100, 1)
 .fold(new ArrayList<>(), new MyFoldFunc(), new MyProcessorFunc(Gx))
 .addSink(new MySinkFunction())
DataStream userRequest=..//用户请求函数
迭代器iter=DataStreamUtils.collect(userRequest);
函数Gx=iter.next();
数据流输入=..//源数据
.keyBy(新的MyKeySelector())
.countWindow(100,1)
.fold(新的ArrayList(),新的MyFoldFunc(),新的MyProcessorFunc(Gx))
.addSink(新的MySinkFunction())
折叠功能(在传入数据到达时对其进行操作)可定义如下:

private static class MyFoldFunc implements FoldFunction<foo, Tuple2<Integer, List<foo>>> {
    @Override
    public Tuple2<Integer, List<foo>> fold(Tuple2<Integer, List<foo>> acc, foo f) {
        acc.f0 = acc.f0 + 1; // if Fx is a simple aggregation (count)
        acc.f1.add(foo);
        return acc;
    }
}
public class MyProcessorFunc
    extends ProcessWindowFunction<Tuple2<Integer, List<foo>>, Tuple2<Integer, FunctionResult>, String, TimeWindow> {

    public MyProcessorFunc(Function Gx) {
        super();
        this.Gx = Gx;
    }

    @Override
    public void process(String key, Context context,
                        Iterable<Tuple2<Integer, List<foo>> accIt,
                        Collector<Tuple2<Integer, FunctionResult>> out) {
        Tuple2<Integer, List<foo> acc = accIt.iterator().next();
        out.collect(new Tuple2<Integer, FunctionResult>(
            acc.f0, // your Fx aggregation
            Gx.apply(acc.f1), // your Gx results
        ));
    }
}
私有静态类MyFoldFunc实现FoldFunction{
@凌驾
公共Tuple2折叠(Tuple2 acc,foo f){
acc.f0=acc.f0+1;//如果Fx是一个简单的聚合(计数)
根据f1添加(foo);
返回acc;
}
}
处理器功能可以是这样的:

private static class MyFoldFunc implements FoldFunction<foo, Tuple2<Integer, List<foo>>> {
    @Override
    public Tuple2<Integer, List<foo>> fold(Tuple2<Integer, List<foo>> acc, foo f) {
        acc.f0 = acc.f0 + 1; // if Fx is a simple aggregation (count)
        acc.f1.add(foo);
        return acc;
    }
}
public class MyProcessorFunc
    extends ProcessWindowFunction<Tuple2<Integer, List<foo>>, Tuple2<Integer, FunctionResult>, String, TimeWindow> {

    public MyProcessorFunc(Function Gx) {
        super();
        this.Gx = Gx;
    }

    @Override
    public void process(String key, Context context,
                        Iterable<Tuple2<Integer, List<foo>> accIt,
                        Collector<Tuple2<Integer, FunctionResult>> out) {
        Tuple2<Integer, List<foo> acc = accIt.iterator().next();
        out.collect(new Tuple2<Integer, FunctionResult>(
            acc.f0, // your Fx aggregation
            Gx.apply(acc.f1), // your Gx results
        ));
    }
}
公共类MyProcessorFunc
扩展ProcessWindowFunction{
公共MyProcessorFunc(函数Gx){
超级();
这个.Gx=Gx;
}
@凌驾
公共无效进程(字符串键、上下文、,

这是我的第一个想法。但API不支持在(Co)中连接WindowedStreamProcessFunction您可以通过控制计时器的触发时间来进行自己的窗口设置。无论如何,实现自己的窗口设置将更加高效——使用这些滑动计数窗口,每个事件都被复制到100个窗口对象中。这是真的吗?为什么Flink要复制100个?滑动计数窗口可以使用固定大小的缓冲区实现.Gx来自其自身数据流中的最终用户。在上面的代码示例中,Gx被声明为MyProcessorFunc构造函数的输入参数,但调用方代码没有显示Gx是如何传递给构造函数的。@Sebastian假设在程序开始时用户的函数始终可用,您可以将其转换为迭代器并如更新的答案所示,将其作为MyProcessorFunc的输入。您也可以在内存中收集第一个流输出(列表),并等待用户请求到来,但这与Flink的工作方式不符,因为您将阻止第一个流计算(并将其输出保留在内存中),直到用户请求到来!来自用户(Gx)的函数在程序开始时不可用。它由最终用户通过REST API发送-转换为流并发送到Flink进行处理。但是,有一些内置函数(Fx)正在对窗口化数据进行处理。这里的问题是如何利用内存中已有的窗口化数据(因为它被用来计算外汇),计算Gx