Apache flink ApacheFlink0.10如何从无界输入数据流中获取复合密钥的第一次出现?

Apache flink ApacheFlink0.10如何从无界输入数据流中获取复合密钥的第一次出现?,apache-flink,flink-streaming,Apache Flink,Flink Streaming,我是阿帕奇·弗林克的新手。我的输入中有一个未绑定的数据流(通过kakfa输入flink 0.10) 我想获得每个主键的第一次出现(主键是contract_num和event_dt) 这些“重复”几乎是紧接着发生的。 源系统无法为我过滤,所以flink必须这样做 这是我的输入数据: contract_num, event_dt, attr A1, 2016-02-24 10:25:08, X A1, 2016-02-24 10:25:08, Y A1, 2016-02-24 10:25:09,

我是阿帕奇·弗林克的新手。我的输入中有一个未绑定的数据流(通过kakfa输入flink 0.10)

我想获得每个主键的第一次出现(主键是contract_num和event_dt)
这些“重复”几乎是紧接着发生的。 源系统无法为我过滤,所以flink必须这样做

这是我的输入数据:

contract_num, event_dt, attr 
A1, 2016-02-24 10:25:08, X
A1, 2016-02-24 10:25:08, Y
A1, 2016-02-24 10:25:09, Z
A2, 2016-02-24 10:25:10, C
以下是我想要的输出数据:

A1, 2016-02-24 10:25:08, X
A1, 2016-02-24 10:25:09, Z
A2, 2016-02-24 10:25:10, C
注:第2行已删除,因为第1行中已出现A001和“2016-02-24 10:25:08”的组合键

我怎样才能用flink 0.10做到这一点

我正在考虑使用
keyBy(0,1)
,但之后我不知道该怎么办

(我使用了joda time和org.flinkspect来设置这些测试)

@测试
公开无效测试(){
DateTime 3secondsago=(新的DateTime()).minusSeconds(3);
DateTime twoSecondsAgo=(新的DateTime()).minusSeconds(2);
DateTime oneSecondsAgo=(new DateTime()).minusSeconds(2);
数据流测试流=
createTimedTestStreamWith(
Tuple3.of(“A1”,3secondsSago.toDate(),“X”))
.emit(Tuple3.of(“A1”,3secondsSago.toDate(),“Y”),在(0,时间单位纳秒)之后)
.emit(Tuple3.of(“A1”,twoSecondsAgo.toDate(),“Z”),在(0,时间单位纳秒)之后)
.emit(Tuple3.of(“A2”,oneSecondsAgo.toDate(),“C”),在(0,时间单位纳秒)之后)
.close();
testStream.keyBy(0,1);
}

如果密钥空间大于可用存储空间,则在无限流上过滤重复项最终将失败。原因是您必须将已看到的密钥存储在某个位置以过滤掉重复的密钥。因此,最好定义一个时间窗口,在该时间窗口之后,您可以清除当前看到的关键点集

如果您知道此问题,但仍想尝试,可以在调用
keyBy
后应用有状态的
flatMap
操作。有状态映射器使用Flink的状态抽象来存储它是否已经看到具有此键的元素。这样,您也将受益于Flink的容错机制,因为您的状态将被自动检查

一个完成你工作的Flink程序可能看起来像

publicstaticvoidmain(字符串[]args)引发异常{
StreamExecutionEnvironment env=StreamExecutionEnvironment.getExecutionEnvironment();
数据流输入=环境fromElements(Tuple3.of(“foo”,新日期(1000),“bar”),Tuple3.of(“foo”,新日期(1000),“foobar”);
input.keyBy(0,1).flatMap(新的DuplicateFilter()).print();
环境执行(“测试”);
}
其中,
DuplicateFilter
的实现取决于Flink的版本

版本>=1.0实现
公共静态类DuplicateFilter扩展了RichFlatMapFunction{
静态最终ValueStateDescriptor描述符=新的ValueStateDescriptor(“seen”,Boolean.class,false);
私人价值国家经营者国家;
@凌驾
公共无效打开(配置){
operatorState=this.getRuntimeContext().getState(描述符);
}
@凌驾
公共void flatMap(Tuple3值,收集器输出)引发异常{
如果(!operatorState.value()){
//我们还没有看到元素
取出。收集(价值);
//将运算符状态设置为true,这样我们就不会再次使用此键发射元素
运算符状态更新(true);
}
}
}
版本0.10的实现
公共静态类DuplicateFilter扩展了RichFlatMapFunction{
私人经营者国家经营者国家;
@凌驾
公共无效打开(配置){
operatorState=this.getRuntimeContext().getKeyValueState(“seen”,Boolean.class,false);
}
@凌驾
公共void flatMap(Tuple3值,收集器输出)引发异常{
如果(!operatorState.value()){
//我们还没有看到元素
取出。收集(价值);
运算符状态更新(true);
}
}
}
更新:使用翻滚时间窗口
input.keyBy(0,1).timeWindow(Time.seconds(1)).apply(newwindowfunction()){
@凌驾
public void apply(元组、时间窗口、Iterable输入、收集器输出)引发异常{
collect(input.iterator().next());
}
})

这是我刚刚写的另一种方法。它的缺点是,它是一个更多的自定义代码,因为它不使用内置的Flink窗口功能,但它没有延迟惩罚,直到提到。完整的例子

package com.dataartisans.filters;
导入com.google.common.cache.CacheBuilder;
导入com.google.common.cache.CacheLoader;
导入com.google.common.cache.LoadingCache;
导入org.apache.flink.api.common.functions.RichFilterFunction;
导入org.apache.flink.api.java.functions.KeySelector;
导入org.apache.flink.configuration.configuration;
同步导入org.apache.flink.streaming.api.checkpoint.checkpoint;
导入java.io.Serializable;
导入java.util.HashSet;
导入java.util.concurrent.TimeUnit;
/**
*此类过滤数据流中在可配置时间内发生的重复项。
*/
公共类dedudefilterFunction扩展了RichFilterFunction,并同步实现了Checkpointeda{
私有加载缓存重复数据缓存;
专用最终密钥选择器密钥选择器;
私有最终长缓存过期时间ms;
/**
*@param cacheExpirationTimeMs缓存中元素的过期时间
*/
公共重复数据消除过滤器功能(KeySelector KeySelector,long cacheExpirationTimeMs){
this.keySelector=keySelector;
this.cacheExpirationTimeMs=cacheExpirationTimeMs;
}
@凌驾
公共void open(配置参数)引发异常{
creatededuplecache();
}
@Test
public void test() {
    DateTime threeSecondsAgo = (new DateTime()).minusSeconds(3);
    DateTime twoSecondsAgo = (new DateTime()).minusSeconds(2);
    DateTime oneSecondsAgo = (new DateTime()).minusSeconds(2);

    DataStream<Tuple3<String, Date, String>> testStream =
            createTimedTestStreamWith(
                    Tuple3.of("A1", threeSecondsAgo.toDate(), "X"))
            .emit(Tuple3.of("A1", threeSecondsAgo.toDate(), "Y"), after(0, TimeUnit.NANOSECONDS))
            .emit(Tuple3.of("A1", twoSecondsAgo.toDate(), "Z"), after(0, TimeUnit.NANOSECONDS))
            .emit(Tuple3.of("A2", oneSecondsAgo.toDate(), "C"), after(0, TimeUnit.NANOSECONDS))
            .close();

    testStream.keyBy(0,1);
}
package com.dataartisans.filters;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import org.apache.flink.api.common.functions.RichFilterFunction;
import org.apache.flink.api.java.functions.KeySelector;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.streaming.api.checkpoint.CheckpointedAsynchronously;

import java.io.Serializable;
import java.util.HashSet;
import java.util.concurrent.TimeUnit;


/**
 * This class filters duplicates that occur within a configurable time of each other in a data stream.
 */
public class DedupeFilterFunction<T, K extends Serializable> extends RichFilterFunction<T> implements CheckpointedAsynchronously<HashSet<K>> {

  private LoadingCache<K, Boolean> dedupeCache;
  private final KeySelector<T, K> keySelector;
  private final long cacheExpirationTimeMs;

  /**
   * @param cacheExpirationTimeMs The expiration time for elements in the cache
   */
  public DedupeFilterFunction(KeySelector<T, K> keySelector, long cacheExpirationTimeMs){
    this.keySelector = keySelector;
    this.cacheExpirationTimeMs = cacheExpirationTimeMs;
  }

  @Override
  public void open(Configuration parameters) throws Exception {
    createDedupeCache();
  }


  @Override
  public boolean filter(T value) throws Exception {
    K key = keySelector.getKey(value);
    boolean seen = dedupeCache.get(key);
    if (!seen) {
      dedupeCache.put(key, true);
      return true;
    } else {
      return false;
    }
  }

  @Override
  public HashSet<K> snapshotState(long checkpointId, long checkpointTimestamp) throws Exception {
    return new HashSet<>(dedupeCache.asMap().keySet());
  }

  @Override
  public void restoreState(HashSet<K> state) throws Exception {
    createDedupeCache();
    for (K key : state) {
      dedupeCache.put(key, true);
    }
  }

  private void createDedupeCache() {
    dedupeCache = CacheBuilder.newBuilder()
      .expireAfterWrite(cacheExpirationTimeMs, TimeUnit.MILLISECONDS)
      .build(new CacheLoader<K, Boolean>() {
        @Override
        public Boolean load(K k) throws Exception {
          return false;
        }
      });
  }
}