• 基于Broadcast 状态的Flink Etl Demo


    接上文: 【翻译】The Broadcast State Pattern(广播状态) 

    最近尝试了一下Flink 的 Broadcase 功能,在Etl,流表关联场景非常适用:一个流数据量大,一个流数据量小(配置表)需要更新

    业务逻辑如下:

      

    注: 正常情况广播流只有一个输出源,更新也在这个源里,这里做了个优化:将广播流的输入源改为两部分配置文件和更新topic(原因:flink 读取文件,读完就结束了无法做更新,而每次从kafka获取全量配置数据,涉及到kafka topic数据的删除时间,除非涉及非常长的删除时间,不然每次读取全量也不太方便),这里不使用flink的CacheFile,因为不能更新

    具体业务如下:转码三位城市编码为对应城市中文

      1. 自定义输入流,输入三位的城市编码和五位的随机字符串

      2. 广播流读取配置文件和配置文件更新topic

      3. connect两个流,读取配置文件对应的数据解析数据流输入的数据

    自定义输入流如下:

    class RadomFunction extends SourceFunction[String]{
      var flag = true
      override def cancel(): Unit = {
        flag = false
      }
    
      override def run(ctx: SourceFunction.SourceContext[String]): Unit = {
        while (flag){
          for (i <- 0 to 300) {
            var nu = i.toString
            while (nu.length < 3) {
              nu = "0" + nu
            }
            ctx.collect(nu + "," + StringUtil.getRandomString(5))
            Thread.sleep(2000)
          }
        }
      }
    }

    Etl 代码如下:

    import java.io.File
    import com.venn.flink.util.{StringUtil}
    import com.venn.index.conf.Common
    import org.apache.flink.api.common.serialization.SimpleStringSchema
    import org.apache.flink.api.common.state.MapStateDescriptor
    import org.apache.flink.api.common.typeinfo.BasicTypeInfo
    import org.apache.flink.api.scala._
    import org.apache.flink.runtime.state.filesystem.FsStateBackend
    import org.apache.flink.streaming.api.functions.co.BroadcastProcessFunction
    import org.apache.flink.streaming.api.functions.source.SourceFunction
    import org.apache.flink.streaming.api.scala.StreamExecutionEnvironment
    import org.apache.flink.streaming.api.{CheckpointingMode, TimeCharacteristic}
    import org.apache.flink.streaming.connectors.kafka.FlinkKafkaConsumer
    import org.apache.flink.util.Collector
    
    /**
      * broadcast
      */
    object BroadCastDemo {
    
      def main(args: Array[String]): Unit = {
        val env = StreamExecutionEnvironment.getExecutionEnvironment
        env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
        if ("/".equals(File.separator)) {
          val backend = new FsStateBackend(Common.CHECK_POINT_DATA_DIR, true)
          env.setStateBackend(backend)
          env.enableCheckpointing(10 * 1000, CheckpointingMode.EXACTLY_ONCE)
        } else {
          env.setMaxParallelism(1)
          env.setParallelism(1)
        }
        // 配置更新流
        val configSource = new FlinkKafkaConsumer[String]("broad_cast_demo", new SimpleStringSchema, Common.getProp)
        // 配置流的初始化,可以通过读取配置文件实现
        var initFilePath = ""
        if ("/".equals(File.separator)){
          initFilePath = "hdfs:///venn/init_file.txt"
        }else{
          initFilePath = "D:\idea_out\broad_cast.txt"
        }
        val init = env.readTextFile(initFilePath)
        val descriptor = new MapStateDescriptor[String,  String]("dynamicConfig", BasicTypeInfo.STRING_TYPE_INFO, BasicTypeInfo.STRING_TYPE_INFO)
        val configStream = env.addSource(configSource).union(init).broadcast(descriptor)
    
    
        val input = env.addSource(new RadomFunction)
          .connect(configStream)
          .process(new BroadcastProcessFunction[String, String, String] {
            override def processBroadcastElement(value: String, ctx: BroadcastProcessFunction[String, String, String]#Context, out: Collector[String]): Unit = {
    
              println("new config : " + value)
              val configMap = ctx.getBroadcastState(descriptor)
              // process update configMap,读取配置数据,写入广播状态中
              val line = value.split(",")
              configMap.put(line(0), line(1))
            }
            override def processElement(value: String, ctx: BroadcastProcessFunction[String, String, String]#ReadOnlyContext, out: Collector[String]): Unit = {
              // use give key, return value
              val configMap = ctx.getBroadcastState(descriptor)
              // 解析三位城市编码,根据广播状态对应的map,转码为城市对应中文
    //          println(value)
              val line = value.split(",")
              val code = line(0)
              var va = configMap.get(code)
              // 不能转码的数据默认输出 中国(code=xxx)
              if ( va == null){
                va = "中国(code="+code+")";
              }else{
                va = va + "(code="+code+")"
              }
              out.collect(va + "," + line(1))
            }
          })
        input.print()
    
        env.execute("BroadCastDemo")
      }
    }

    配置数据如下:

    001,邯郸市
    002,石家庄
    003,保定市
    004,张家口
    005,承德市
    006,唐山市
    007,廊坊市
    008,沧州市
    009,衡水市
    010,邢台市

    数据源数据如下:

    001,bGTqQM
    002,sCfdSK
    003,RWtLNC
    004,qkGita
    005,fOemDF
    006,KRaUmj
    007,MNwKdS
    008,RgZDlI
    009,QbUyeh

    转码后输出如下:

    邯郸市(code=001),bGTqQM
    石家庄(code=002),sCfdSK
    保定市(code=003),RWtLNC
    张家口(code=004),qkGita
    承德市(code=005),fOemDF
    唐山市(code=006),KRaUmj
    廊坊市(code=007),MNwKdS
    沧州市(code=008),RgZDlI
    衡水市(code=009),QbUyeh

    执行结果如下:

    ...
    new config : 047,十堰市
    new config : 048,随枣市
    new config : 049,荆门市
    new config : 050,江汉(仙桃)
    邯郸市(code=001),ovLKQN
    石家庄(code=002),QTgxXn
    保定市(code=003),bIPefX
    张家口(code=004),XcdHUd
    ...
    宜昌市(code=045),sQRonA
    恩施市(code=046),gfipAY
    十堰市(code=047),ASPulh
    随枣市(code=048),mqurwg
    荆门市(code=049),hfTlue
    江汉(仙桃)(code=050),EfiXec
    中国(code=051),xGuihq  # 不能转码数据
    中国(code=052),niMlrb
    中国(code=053),fHvIpU
    中国(code=054),MdqqCb
    中国(code=055),CFgNmM
    ...

    广播流数据更新如下:

    new config : 150,xxx   # 获取当新配置数据
    中国(code=148),fLtwye
    中国(code=149),bEJfMP
    new config : 151,fff
    xxx(code=150),TTIPii   # 新配置数据转码数据
    fff(code=151),iJSAjJ
    中国(code=152),yBvlUZ
    new config : 152,ggg

    搞定

  • 相关阅读:
    Spring Boot
    Spring Boot Tomcat配置详解
    Spring Boot读取配置的 5 种方式
    Spring Boot日志集成实战
    Spring Boot国际化开发实战
    Spring Boot整合 Thymeleaf 模板引擎
    Spring Boot Debug调试
    Spring Boot实现热部署
    Exchange Cards(dfs)
    Dungeon Master
  • 原文地址:https://www.cnblogs.com/Springmoon-venn/p/11357608.html
Copyright © 2020-2023  润新知