Apache flink 在RichCoFlatMapFunction中更新外部数据库

Apache flink 在RichCoFlatMapFunction中更新外部数据库,apache-flink,flink-streaming,Apache Flink,Flink Streaming,我有一个RichCoFlatMapFunction DataStream<Metadata> metadataKeyedStream = env.addSource(metadataStream) .keyBy(Metadata::getId); SingleOutputStreamOperator<Output> outputStream = env.addSource(recordStream) .assignTime

我有一个RichCoFlatMapFunction

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(metadataTable.listAllAsMap()));

public class CustomCoFlatMap extends RichCoFlatMapFunction<Record, Metadata, Output> {

    private transient Map<String, Metadata> datasource;
    private transient ValueState<String, Metadata> metadataState;

    @Inject
    public void setDataSource(Map<String, Metadata> datasource) {
        this.datasource = datasource;
    }

    @Override
    public void open(Configuration parameters) throws Exception {
        // read ValueState
        metadataState = getRuntimeContext().getState(
            new ValueStateDescriptor<String, Metadata>("metadataState", Metadata.class));
    }

    @Override
    public void flatMap2(Metadata metadata, Collector<Output> collector) throws Exception {
        // if metadata record is removed from table, removing the same from local state
        if(metadata.getEventName().equals("REMOVE")) {
            metadataState.clear();
            return;
        }
        // update metadata in ValueState
        this.metadataState.update(metadata);
    }

    @Override
    public void flatMap1(Record record, Collector<Output> collector) throws Exception {
        
        Metadata metadata = this.metadataState.value();
        // if metadata is not present in ValueState
        if(metadata == null) {
            // get metadata from datasource
            metadata = datasource.get(record.getId());
            // if metadata found in datasource, add it to ValueState
            if(metadata != null) {
                metadataState.update(metadata);
                Output output = new Output(record.getId(), metadataState.getName(), 
                    metadataState.getVersion(), metadata.getType());
                if(metadata.getId() == 123) {
                    // here I want to update metadata into another Database
                    // can I do it here directly ?
                }
                collector.collect(output);
            }
        }
    }
}
数据流metadataKeyedStream= 环境addSource(元数据流) .keyBy(元数据::getId); SingleOutputStream运算符outputStream= 环境添加源(记录流) .assignTimestampsAndWatermarks(新的RecordTimeExtractor()) .keyBy(记录::getId) .connect(metadataKeyedStream) .flatMap(新的CustomCoFlatMap(metadataTable.listAllAsMap()); 公共类CustomCoFlatMap扩展了RichCoFlatMapFunction{ 私有瞬态地图数据源; 私有瞬态值状态元数据状态; @注入 公共void setDataSource(映射数据源){ this.datasource=数据源; } @凌驾 公共void open(配置参数)引发异常{ //读取值状态 metadataState=getRuntimeContext().getState( 新的ValueStateDescriptor(“metadataState”,Metadata.class)); } @凌驾 公共void flatMap2(元数据、收集器)引发异常{ //如果从表中删除元数据记录,则从本地状态中删除元数据记录 if(metadata.getEventName().equals(“删除”)){ metadataState.clear(); 返回; } //更新ValueState中的元数据 this.metadataState.update(元数据); } @凌驾 公共void flatMap1(记录记录、收集器)引发异常{ 元数据元数据=this.metadataState.value(); //如果ValueState中不存在元数据 if(元数据==null){ //从数据源获取元数据 元数据=datasource.get(record.getId()); //如果在数据源中找到元数据,请将其添加到ValueState if(元数据!=null){ metadataState.update(元数据); 输出=新输出(record.getId(),metadataState.getName(), metadataState.getVersion(),metadata.getType(); if(metadata.getId()==123){ //这里我想将元数据更新到另一个数据库中 //我可以直接在这里做吗? } collect.collect(输出); } } } }
在这里,在flatmap1方法中,我想更新一个数据库。我可以在flatmap1中执行该操作吗?我这样问是因为它需要一些等待时间来查询数据库,然后更新数据库。

虽然原则上可以这样做,但这不是一个好主意。在Flink用户函数中执行同步i/o会导致两个问题:

  • 您占用了大量资源,这些资源大部分时间都处于闲置状态,等待响应
  • 在等待过程中,该操作员正在产生反压力,阻止检查点障碍取得进展。这很容易导致偶尔的检查点超时和作业失败
  • 最好使用
    键控协处理函数
    ,并将预期的数据库更新作为。然后,可以通过数据库接收器或使用