Apache flink RichCoFlatMapFunction中的MapState始终为空
我在读两条溪流。一个有记录,一个有元数据 这是我第一次希望我的应用程序通过扫描整个表并将其保存到Flink的MapState来构建元数据。表上的更新将通过元数据流捕获,MapState将相应地更新 从第二次开始,我想使用MapState,而不是读取整个表 下面是我对这个功能的实现,但是我的MapState总是空的,我在这里做错了什么吗Apache flink RichCoFlatMapFunction中的MapState始终为空,apache-flink,flink-streaming,flink-statefun,Apache Flink,Flink Streaming,Flink Statefun,我在读两条溪流。一个有记录,一个有元数据 这是我第一次希望我的应用程序通过扫描整个表并将其保存到Flink的MapState来构建元数据。表上的更新将通过元数据流捕获,MapState将相应地更新 从第二次开始,我想使用MapState,而不是读取整个表 下面是我对这个功能的实现,但是我的MapState总是空的,我在这里做错了什么吗 public class CustomCoFlatMap extends RichCoFlatMapFunction<Record, Metadata, O
public class CustomCoFlatMap extends RichCoFlatMapFunction<Record, Metadata, Output> {
private transient DataSource datasource;
private transient MapState<String, Metadata> metadataState;
@Inject
public void setDataSource(DataSource datasource) {
this.datasource = datasource;
}
@Override
public void open(Configuration parameters) throws Exception {
final RichFunctionComponent component = DaggerRichFunctionComponent.builder()
.richFunctionModule(RichFunctionModule.builder()
.runtimeContext(getRuntimeContext())
.build())
.build();
component.inject(this);
// read MapState from snapshot
metadataState = getRuntimeContext().getMapState(new MapStateDescriptor<String, Cluster>("metadataState",
TypeInformation.of(new TypeHint<String>(){}), TypeInformation.of(new TypeHint<Metadata>() {})));
}
@Override
public void flatMap2(Metadata metadata, Collector<Output> collector) throws Exception {
// this should happen only when application starts for first time
// from next time, application will read from snapshot
readMetadataForFirstTime();
// update metadata in MapState
this.metadataState.put(metadata.getId(), metadata);
}
@Override
public void flatMap1(Record record, Collector<Output> collector) throws Exception {
readMetadataForFirstTime();
Metadata metadata = this.metadataState.get(record.getId());
Output output = new Output(record.getId(), metadataState.getName(), metadataState.getVersion(), metadata.getType());
collector.collect(output);
}
private void readMetadataForFirstTime() throws Exception {
if(this.metadataState.iterator().hasNext()) {
// metadataState from snapshot has data
// not reading from table
return;
}
// do this only once
// read metadata from table and add it to MapState
List<Metadata> metadataList = datasource.listAllMetadata();
for(Metadata metadata: metadataList) {
this.metadataState.put(metadata.getid(), metadata);
}
}
}
公共类CustomCoFlatMap扩展了RichCoFlatMapFunction{
私有瞬态数据源;
私有瞬态映射状态元数据状态;
@注入
public void setDataSource(数据源数据源){
this.datasource=数据源;
}
@凌驾
公共void open(配置参数)引发异常{
final RichFunctionComponent组件=DaggerRichFunctionComponent.builder()
.richFunctionModule(richFunctionModule.builder())
.runtimeContext(getRuntimeContext())
.build())
.build();
组件。注入(此);
//从快照读取映射状态
metadataState=getRuntimeContext().getMapState(新的MapStateDescriptor(“metadataState”),
of(new TypeHint(){}),TypeInformation.of(new TypeHint(){}));
}
@凌驾
公共void flatMap2(元数据、收集器)引发异常{
//只有当应用程序第一次启动时,才会发生这种情况
//从下一次开始,应用程序将从快照读取数据
readMetadataForFirstTime();
//在MapState中更新元数据
this.metadataState.put(metadata.getId(),metadata);
}
@凌驾
公共void flatMap1(记录记录、收集器)引发异常{
readMetadataForFirstTime();
Metadata Metadata=this.metadataState.get(record.getId());
输出输出=新输出(record.getId()、metadataState.getName()、metadataState.getVersion()、metadata.getType());
collect.collect(输出);
}
私有void readMetadataForFirstTime()引发异常{
if(this.metadataState.iterator().hasNext()){
//快照中的metadataState包含数据
//不在桌子上看书
返回;
}
//只做一次
//从表中读取元数据并将其添加到MapState
List metadataList=datasource.listAllMetadata();
for(元数据:metadataList){
this.metadataState.put(metadata.getid(),metadata);
}
}
}
编辑:应用程序的其余部分
DataStream<Metadata> metadataKeyedStream =
env.addSource(metadataStream)
.keyBy(Metadata::getId);
SingleOutputStreamOperator<Output> outputStream =
env.addSource(recordStream)
.assignTimestampsAndWatermarks(new RecordTimeExtractor())
.keyBy(Record::getId)
.connect(metadataKeyedStream)
.flatMap(new CustomCoFlatMap());
数据流metadataKeyedStream=
环境addSource(元数据流)
.keyBy(元数据::getId);
SingleOutputStream运算符outputStream=
环境添加源(记录流)
.assignTimestampsAndWatermarks(新的RecordTimeExtractor())
.keyBy(记录::getId)
.connect(metadataKeyedStream)
.flatMap(新的CustomCoFlatMap());
MapState
是一种密钥分区状态——这意味着Flink为输入流中的每个不同密钥维护一个单独的Map
readMetadataForFirstTime
必须读取并插入由RichCoFlatMapFunction
处理的流中每个键的数据,因为每个键都有一个单独的映射
您可能希望以不同的方式处理此问题,具体取决于您正试图做什么。
例如,如果您只想为源流中的每个键存储一个值,那么应该使用ValueState
,而不是MapState
。您可以将ValueState
视为切分键/值存储,其中有状态运算符的每个并行实例(例如RichCoFlatMapFunction
)将具有键空间片段的值MapState
适用于需要为每个键存储整个hashmap而不是单个对象的情况
(如果我错误判断了问题所在,请分享更多上下文,说明应用程序的其余部分是如何使用此
RichCoFlatMapFunction
)使用ValueState
而不是MapState
是有意义的,因为我每个键只有一个值。但是如何将元数据写入每个Id的值状态?我是否应该使用ValueStateDescriptor
名称作为Id
和值作为Metadata
保存?如果是,那么我应该执行getRuntimeContext().getState(新的ValueStateDescriptor(metadata.getId(),TypeInformation.of(new-TypeHint(){}))
对于在flatmap 1
和flatmap 2
中收到的每个记录,我建议您查看和中的Flink培训资料,其中详细解释了如何工作,并包括一些示例。状态描述符名称应为常量字符串;像“元数据”这样的东西就可以了。如果元数据是一个POJO,那么您可以简单地执行类似于新的ValueStateDescriptor(“Metadata”,Metadata.class)
的操作。明白了,我想知道如何将所有元数据
放入ValueState
中,也就是说,当我第一次调用readMetadataForFirstTime()
,我想将元数据的列表添加到ValueState
中。但我只看到ValueState
上的更新,如何将所有Metadata
列表添加到`ValueState`中?换句话说,我希望读取表Metadata
中的所有条目,并仅在应用程序第一次启动时将它们保存到state。当应用程序重新启动时,我希望从状态中检索它们,而不是再次读取表。