繁体   English   中英

减少键控流中的函数行为

[英]Reduce function behaviour in keyed streams

对于我们的一个用例,我们需要根据文件中的更改重做一些计算,然后广播该文件的结果,以便我们可以在另一个流中使用它。

程序的生命周期,几乎是这样的:

数据流 1:监控文件 -> 检测一些变化 -> 重新处理文件中的所有元素 -> 计算一个结果 -> 广播

数据流 2:一些转换 -> 在使用所有现有广播元素的同时对 DS2 中的每个元素做一些事情(在广播元素中可以容忍一些数据丢失一段时间)

我将给出一些代码示例来更好地解释问题所在:

所以这是 DS1:映射每个元素,将它们发送到减速器,然后计算总数

env.readFile(format, clientPath, FileProcessingMode.PROCESS_CONTINUOUSLY, interval)
    .map(new Adder())
    .keyBy(Map::size)
    .reduce(new Reducer());

这是映射阶段,它只是从一行创建一个哈希图

public static class Adder extends RichMapFunction<String, Map<String, String>> {
  private static final long serialVersionUID = 1L;

  @Override
  public Map<String, String> map(String string) throws Exception {
    String[] strings = string.split("=");
    HashMap<String, String> hashMap = new HashMap<>();
    hashMap.put(strings[0], strings[1]);
    return hashMap;
  }
}

这是最后一步,减速器。 获取来自映射器的所有减少元素,然后返回总数,单个哈希图

public static class Reducer extends RichReduceFunction<Map<String, String>> {
  private static final long serialVersionUID = 1L;

  @Override
  public Map<String, String> reduce(Map<String, String> stringStringMap, Map<String, String> t1) throws Exception {
    stringStringMap.putAll(t1);
    return stringStringMap;
  }
}

然后像下面的代码片段一样广播 DS1。

MapStateDescriptor<String, String> descriptor = new MapStateDescriptor<>("Brodcasted map state", Types.STRING, Types.STRING);
BroadcastStream<Map<String, String>> broadcastedProperties =  clientProperties.broadcast(descriptor); 
ds2.connect(broadcastedProperties).process(new EventListener(properties));

在给定时间内使用以下元素

Time    Document
T1      K1=V1, K2=V2
T2      K2=V2
T3      K3=V3, K1=V4

当我运行我们的程序时,我期望的是:

Time    Broadcasted Elements
T1      K1=V1, K2=V2
T2      K2=V2
T3      K3=V3, K1=V4

我看到的是这样的:

Time    Broadcasted Elements
T1      K1=V1, K2=V2
T2      K1=V1, K2=V2
T3      K1=V4, K2=V2, K3=V3

我为克服这个问题所做的只是简单地在数据流上打开一个窗口,并使用带有累加器而不是减速器的聚合函数,但我更愿意使用非窗口方法。

我做了一些调试,我已经意识到,即使在映射阶段它只映射当时可用的元素,在减少阶段它根据以前的状态减少(我的意思是结果time – 1 ) + 此时的所有元素。 我发现在 reduce 阶段有一个不可见的状态很奇怪。 从我的角度来看,它应该仅基于直接来自映射器的元素。 也许我对 Flink 中的 reduce 的理解是错误的,但我很想得到一些澄清。

是的,当 Flink 的任何内置聚合器(例如 sum、max、reduce 等)应用于流时,它都会以增量的、有状态的方式聚合整个流。 或者更准确地说,这是在 KeyedStreams 上完成的,聚合是在逐个键的基础上完成的,但是以一种持续的、无限制的方式完成的。 例如,如果您在整数流 1, 2, 3, 4, 5, ... 上使用 sum() 那么 sum() 将产生流 1, 3, 6, 10, 15, ... 。 在您的情况下,reduce() 将产生一个不断更新的流,其中将包含越来越多的键/值对。

如果您要按时间键控流,那么您应该得到您正在寻找的结果,但键控状态仍将永远保持,这可能会出现问题。 我建议您使用窗口 API,或者使用 RichFlatMap 或 ProcessFunction 之类的东西,您可以在其中直接管理状态。

没有窗口的reduce函数将是一个滚动reduce。 如果要在滚动减少之间保持一致的状态,请使用状态对象来保存状态,稍后检索并更新它。 我认为这也是@David Anderson 对 RichReduceFunction 提出的建议。

public static class Reducer extends RichReduceFunction<Map<String, String>> {     
private static final long serialVersionUID = 1L;
private final MapStateDescriptor<String, String> mapStateDesc = new MapStateDescriptor<>("myMapState", BasicTypeInfo.STRING_TYPE_INFO, BasicTypeInfo.STRING_TYPE_INFO); 

@Override
public void open(Configuration parameters) {
        getRuntimeContext().getMapState(this.mapStateDesc);
}

@Override     
public Map<String, String> reduce(Map<String, String> stringStringMap, Map<String, String> t1) throws Exception {       
     MapState<String, String> myMapState = getRuntimeContext().getMapState(this.mapStateDesc);
     HashMap<String, String> newMap = new HashMap<>();
     //updating your map from previous state 
     for(Map.Entry<String,String> entry : myMapState.entries()) {
        newMap.put(entry.getKey(),entry.getValue());
     }       
     newMap.putAll(stringStringMap);       
     newMap.putAll(t1);
     //update the state with latest data set
     myMapState.putAll(newMap);
     return newMap;     
} 

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM