flink学习笔记-广播变量、累加器、分布式缓存

  • 1:广播变量
    • 广播变量主要分为两种方式:dataStream当中的广播变量以及dataSet当中的广播变量,这两个地方的广播变量还有一定的不一样的各自的特性,一句话解释,可以理解为是一个公共的共享变量,我们可以把一个dataset 数据集广播出去,然后不同的task在节点上都能够获取到,这个数据在每个节点上只会存在一份,节约内存
    • 1.1:dataStream当中的广播分区
      • 将数据广播给所有的分区,数据可能会被重复处理,一般用于某些公共的配置信息读取,不会涉及到更改的数据
      import org.apache.flink.streaming.api.scala.{DataStream, StreamExecutionEnvironment}
      
      object FlinkBroadCast {
        def main(args: Array[String]): Unit = {
          val environment: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
          environment.setParallelism(4)
          import org.apache.flink.api.scala._
          val result: DataStream[String] = environment.fromElements("hello").setParallelism(1)
          val resultValue: DataStream[String] = result.broadcast.map(x => {
            println(x)
            x
          })
          resultValue.print()
          environment.execute()
        }
      }
      
  • 1.2:dataSet当中的广播变量
    • 广播变量允许编程人员在每台机器上保持1个只读的缓存变量,而不是传送变量的副本给tasks
    • 广播变量创建后,它可以运行在集群中的任何function上,而不需要多次传递给集群节点。另外需要记住,不应该修改广播变量,这样才能确保每个节点获取到的值都是一致的
    • 一解释,可以理解为是一个公共的共享变量,我们可以把一个dataset 数据集广播出去,然后不同的task在节点上都能够获取到,这个数据在每个节点上只会存在一份。如果不使用broadcast,则在每个节点中的每个task中都需要拷贝一份dataset数据集,比较浪费内存(也就是一个节点中可能会存在多份dataset数据)。
    // 初始化数据
    DataSet<Integer> toBroadcast = env.fromElements(1, 2, 3)
    // 广播数据
    .withBroadcastSet(toBroadcast, "broadcastSetName");
    // 获取数据
    Collection<Integer> broadcastSet = getRuntimeContext().getBroadcastVariable("broadcastSetName");
    
    • 注意
      • 1:广播出去的变量存在于每个节点的内存中,所以这个数据集不能太大。因为广播出去的数据,会常驻内存,除非程序执行结束
      • 2:广播变量在初始化广播出去以后不支持修改,这样才能保证每个节点的数据都是一致的。
  • 1.3: 小案例
    • 将订单和商品数据合并成为一条数据(使用广播变量,将商品数据广播到每一个节点,然后通过订单数据来进行join即可)
    import java.util
    import org.apache.flink.api.common.functions.RichMapFunction
    import org.apache.flink.api.scala.ExecutionEnvironment
    import org.apache.flink.configuration.Configuration
    import scala.collection.mutable
    
    object FlinkDataSetBroadCast {
      def main(args: Array[String]): Unit = {
        val environment: ExecutionEnvironment = ExecutionEnvironment.getExecutionEnvironment
        import org.apache.flink.api.scala._
        val productData: DataSet[String] = environment.readTextFile("file:///D:\\product.txt")
        val productMap = new mutable.HashMap[String,String]()
    
        val prouctMapSet: DataSet[mutable.HashMap[String, String]] = productData.map(x => {
          val strings: Array[String] = x.split(",")
          productMap.put(strings(0), x)
          productMap
        })
    
        //获取商品数据
        val ordersDataset: DataSet[String] = environment.readTextFile("file:///D:\\orders.txt")
    
        //将商品数据转换成为map结构,key为商品id,value为一行数据
        val resultLine: DataSet[String] = ordersDataset.map(new RichMapFunction[String, String] {
          var listData: util.List[Map[String, String]] = null
          var allMap = Map[String, String]()
    
          override def open(parameters: Configuration): Unit = {
            this.listData = getRuntimeContext.getBroadcastVariable[Map[String, String]]("productBroadCast")
            val listResult: util.Iterator[Map[String, String]] = listData.iterator()
            while (listResult.hasNext) {
              allMap =  allMap.++(listResult.next())
            }
          }
    
          //获取到了订单数据,将订单数据与商品数据进行拼接成为一整
          override def map(eachOrder: String): String = {
            val str: String = allMap.getOrElse(eachOrder.split(",")(2),"暂时没有值")
            eachOrder + ","+str
          }
        }).withBroadcastSet(prouctMapSet, "productBroadCast")
        resultLine.print()
        environment.execute("broadCastJoin")
      }
    }
    
  • 2:累加器
    • Accumulators(累加器)是非常简单的,通过一个add操作累加最终的结果,在job执行后可以获取最终结果
    • 最简单的累加器是counter(计数器):你可以通过Accumulator.add(V value)这个方法进行递增。在任务的最后,flink会吧所有的结果进行合并,然后把最终结果发送到client端。
    • 小需求:统计日志当中exception关键字出现了多少次
    import org.apache.flink.api.common.accumulators.LongCounter
    import org.apache.flink.api.common.functions.RichMapFunction
    import org.apache.flink.api.scala.ExecutionEnvironment
    import org.apache.flink.configuration.Configuration
    
    object FlinkCounterAndAccumulator {
    
      def main(args: Array[String]): Unit = {
        val env=ExecutionEnvironment.getExecutionEnvironment
        import org.apache.flink.api.scala._
        //统计日志当中exception关键字出现了多少次
        val sourceDataSet: DataSet[String] = env.readTextFile("file:///D:\\log.out")
    
        sourceDataSet.map(new RichMapFunction[String,String] {
    
          var counter=new LongCounter()
    
          override def open(parameters: Configuration): Unit = {
            getRuntimeContext.addAccumulator("my-accumulator",counter)
          }
          override def map(value: String): String = {
            if(value.toLowerCase().contains("exception")){
              counter.add(1)
    
            }
            value
          }
        }).setParallelism(4).writeAsText("c:\\t4")
    
        val job=env.execute()
        //获取累加器,并打印累加器的值
        val a=job.getAccumulatorResult[Long]("my-accumulator")
        println(a)
      }
    }
    
  • 3:分布式缓存DistributedCache
    • Flink提供了一个分布式缓存,类似于hadoop,可以使用户在并行函数中很方便的读取本地文件
    • 缓存的工作机制
      • 程序注册一个文件或者目录(本地或者远程文件系统,例如hdfs或者s3),通过ExecutionEnvironment注册缓存文件并为它起一个名称。当程序执行,Flink自动将文件或者目录复制到所有taskmanager节点的本地文件系统,用户可以通过这个指定的名称查找文件或者目录,然后从taskmanager节点的本地文件系统访问它
    • 用法:
    // 1:注册一个文件
    env.registerCachedFile("hdfs:///path/to/your/file", "hdfsFile") 
    // 2:访问数据
    File myFile = getRuntimeContext().getDistributedCache().getFile("hdfsFile");
    
    import org.apache.commons.io.FileUtils
    import org.apache.flink.api.common.functions.RichMapFunction
    import org.apache.flink.api.scala.ExecutionEnvironment
    import org.apache.flink.configuration.Configuration
    
    object FlinkDistributedCache {
      def main(args: Array[String]): Unit = {
        //将缓存文件,拿到每台服务器的本地磁盘进行存储,然后需要获取的时候,直接从本地磁盘文件进行获取
        val env = ExecutionEnvironment.getExecutionEnvironment
        import org.apache.flink.api.scala._
        //1:注册分布式缓存文件
        env.registerCachedFile("hdfs:///path/to/your/file","advert")
        val data = env.fromElements("hello","flink","spark","dataset")
        val result = data.map(new RichMapFunction[String,String] {
    
          override def open(parameters: Configuration): Unit = {
            super.open(parameters)
            //2:获取缓存文件
            val myFile = getRuntimeContext.getDistributedCache.getFile("advert")
            val lines = FileUtils.readLines(myFile)
            val it = lines.iterator()
            while (it.hasNext){
              val line = it.next();
              println("line:"+line)
            }
          }
          override def map(value: String) = {
            value
          }
        }).setParallelism(2)
        result.print()
        env.execute()
      }
    }
    
发布了40 篇原创文章 · 获赞 59 · 访问量 1395

猜你喜欢

转载自blog.csdn.net/qq_26719997/article/details/105086927