• Flink广播流使用举例过滤字符串流


    1. 参考资料

    https://nightlies.apache.org/flink/flink-docs-release-1.14/zh/docs/dev/datastream/fault-tolerance/broadcast_state/

    2. 过滤字符串流

    需求:

    • 打开一个SocketStream实时接收用户的输入,过滤掉数据流中不想要的字符串,把剩余的结果打印出来。
    • 用另外一个SocketStream接收我们不想看到的字符串,为了简化实现,在同一时刻最多只能设置一个不想看到的字符串。

    3. 实现步骤

    • 定义两个输入流,接受输入的字符串
     val wordStream: DataStream[String] = env.socketTextStream("localhost", 12345, '\n')
     val ruleStream: DataStream[String] = env.socketTextStream("localhost", 12346, '\n')
    • 定义一个MapStateDescriptor来描述我们要广播的数据的格式,此处是String类型,我们想要的广播字符串
    val ruleStateDescriptor: MapStateDescriptor[String, String] = new MapStateDescriptor[String, String](
        "RuleBroadcastState",
        BasicTypeInfo.STRING_TYPE_INFO,
        BasicTypeInfo.STRING_TYPE_INFO
    )
    • 把ruleStream注册成广播流
    val broadcastStream: BroadcastStream[String] = ruleStream.broadcast(ruleStateDescriptor)
    • 非广播流使用connect方法连接广播流,在process方法中提供BroadcastProcessFunction来处理广播流和非广播流中的数据
    val output: DataStream[String] = wordStream.connect(broadcastStream).process(
      new BroadcastProcessFunction[String, String, String] {
        override def processElement(in1: String, readOnlyContext: BroadcastProcessFunction[String, String, String]#ReadOnlyContext, collector: Collector[String]): Unit = {
          val filter: String = readOnlyContext.getBroadcastState(ruleStateDescriptor).get("filter")
          if (filter == null || !in1.equals(filter)) {
            collector.collect(in1)
          }
        }
    
        override def processBroadcastElement(in2: String, context: BroadcastProcessFunction[String, String, String]#Context, collector: Collector[String]): Unit = {
          context.getBroadcastState(ruleStateDescriptor).put("filter", in2)
        }
      }
    )
    • 实现 BroadcastProcessFunction 中的 processElement和processBroadcastElement方法
      • processElement用来处理非广播流中的数据,传入的参数中有一个readOnlyContext可以获取broadState,不过是只读的。此处从broadcastState中查找key等于"filter"的值,如果没有查找到,或者当前元素不等于查找到的元素,则输出,否则不做任何操作。
      • processBroadcastElement用来处理广播流中的数据,此处仅仅是把收到的元素加入到broadState中,key固定为"filter"。

    4. 完整代码

    import org.apache.flink.api.common.state.MapStateDescriptor
    import org.apache.flink.api.common.typeinfo.BasicTypeInfo
    import org.apache.flink.streaming.api.datastream.BroadcastStream
    import org.apache.flink.streaming.api.functions.co.BroadcastProcessFunction
    import org.apache.flink.streaming.api.scala._
    import org.apache.flink.util.Collector
    
    object WordCountWithPatternJob {
      def main(args: Array[String]): Unit = {
        val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
        val wordStream: DataStream[String] = env.socketTextStream("localhost", 12345, '\n')
        val ruleStream: DataStream[String] = env.socketTextStream("localhost", 12346, '\n')
    
        val ruleStateDescriptor: MapStateDescriptor[String, String] = new MapStateDescriptor[String, String](
          "RulesBroadcastState",
          BasicTypeInfo.STRING_TYPE_INFO,
          BasicTypeInfo.STRING_TYPE_INFO
        )
    
        val broadcastStream: BroadcastStream[String] = ruleStream.broadcast(ruleStateDescriptor)
    
        val output: DataStream[String] = wordStream.connect(broadcastStream).process(
          new BroadcastProcessFunction[String, String, String] {
            override def processElement(in1: String, readOnlyContext: BroadcastProcessFunction[String, String, String]#ReadOnlyContext, collector: Collector[String]): Unit = {
              val filter: String = readOnlyContext.getBroadcastState(ruleStateDescriptor).get("filter")
              if (filter == null || !in1.equals(filter)) {
                collector.collect(in1)
              }
            }
        
            override def processBroadcastElement(in2: String, context: BroadcastProcessFunction[String, String, String]#Context, collector: Collector[String]): Unit = {
              context.getBroadcastState(ruleStateDescriptor).put("filter", in2)
            }
          }
        )
    
        output.print()
        env.execute("filter word streaming")
      }
    }

    5. 总结

    称做广播流应该不太严谨,从实现来看实际上的把一个MapState给广播出去,其他流通过connect一个广播流可以获取到这个广播的状态,并且是只读的。

    这里有一些 broadcast state 的重要注意事项,在使用它时需要时刻清楚:

    • 没有跨 task 通讯:如上所述,这就是为什么只有在 (Keyed)-BroadcastProcessFunction 中处理广播流元素的方法里可以更改 broadcast state 的内容。 同时,用户需要保证所有 task 对于 broadcast state 的处理方式是一致的,否则会造成不同 task 读取 broadcast state 时内容不一致的情况,最终导致结果不一致。

    • **broadcast state 在不同的 task 的事件顺序可能是不同的:**虽然广播流中元素的过程能够保证所有的下游 task 全部能够收到,但在不同 task 中元素的到达顺序可能不同。 所以 broadcast state 的更新不能依赖于流中元素到达的顺序

    • 所有的 task 均会对 broadcast state 进行 checkpoint:虽然所有 task 中的 broadcast state 是一致的,但当 checkpoint 来临时所有 task 均会对 broadcast state 做 checkpoint。 这个设计是为了防止在作业恢复后读文件造成的文件热点。当然这种方式会造成 checkpoint 一定程度的写放大,放大倍数为 p(=并行度)。Flink 会保证在恢复状态/改变并发的时候数据没有重复且没有缺失。 在作业恢复时,如果与之前具有相同或更小的并发度,所有的 task 读取之前已经 checkpoint 过的 state。在增大并发的情况下,task 会读取本身的 state,多出来的并发(p_new - p_old)会使用轮询调度算法读取之前 task 的 state。

    • 不使用 RocksDB state backend: broadcast state 在运行时保存在内存中,需要保证内存充足。这一特性同样适用于所有其他 Operator State。

  • 相关阅读:
    set使用
    css盒子模型详解一
    列表页调用当前栏目的子类织梦标签
    织梦如何在导航栏下拉菜单中调用当前栏目子类
    HDU1812
    BZOJ1485: [HNOI2009]有趣的数列
    组合数学学习笔记
    Full_of_Boys训练2总结
    Full_of_Boys训练1总结
    FFT算法学习笔记
  • 原文地址:https://www.cnblogs.com/liujinhong/p/15984349.html
Copyright © 2020-2023  润新知