flink的广播变量,累加器,计数器以及分布式缓存

1、广播变量
广播变量主要分为两种方式:dataStream当中的广播变量以及dataSet当中的广播变量,这两个地方的广播变量还有一定的不一样的各自的特性,一句话解释,可以理解为是一个公共的共享变量,我们可以把一个dataset 数据集广播出去,然后不同的task在节点上都能够获取到,这个数据在每个节点上只会存在一份,节约内存
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()
}
}

2、dataSet当中的广播变量
广播变量允许编程人员在每台机器上保持1个只读的缓存变量,而不是传送变量的副本给tasks
广播变量创建后,它可以运行在集群中的任何function上,而不需要多次传递给集群节点。另外需要记住,不应该修改广播变量,这样才能确保每个节点获取到的值都是一致的
一句话解释,可以理解为是一个公共的共享变量,我们可以把一个dataset 数据集广播出去,然后不同的task在节点上都能够获取到,这个数据在每个节点上只会存在一份。如果不使用broadcast,则在每个节点中的每个task中都需要拷贝一份dataset数据集,比较浪费内存(也就是一个节点中可能会存在多份dataset数据)。
用法
1:初始化数据
DataSet toBroadcast = env.fromElements(1, 2, 3)
2:广播数据
.withBroadcastSet(toBroadcast, "broadcastSetName");
3:获取数据
Collection broadcastSet = getRuntimeContext().getBroadcastVariable("broadcastSetName");
注意:
1:广播出去的变量存在于每个节点的内存中,所以这个数据集不能太大。因为广播出去的数据,会常驻内存,除非程序执行结束
2:广播变量在初始化广播出去以后不支持修改,这样才能保证每个节点的数据都是一致的。
需求:求取订单对应的商品
将订单和商品数据进行合并成为一条数据
注意:数据格式参见附件中的orders.txt以及product.txt,商品表当中的第1个字段表示商品id,订单表当中的第3个字段表示商品id,字段之间都是使用,进行切割
使用广播变量,将商品数据广播到每一个节点,然后通过订单数据来进行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:\开课吧课程资料\Flink实时数仓\订单与商品表\product.txt")
val productMap = new mutable.HashMapString,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:\\开课吧课程资料\\Flink实时数仓\\订单与商品表\\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端。累加器在调试或者你想更快了解你的数据的时候是非常有用的。
Flink现在有一下内置累加器。每个累加器都实现了Accumulator接口。
需求:统计tomcat日志当中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._
//统计tomcat日志当中exception关键字出现了多少次
val sourceDataSet: DataSet[String] = env.readTextFile("file:///D:\开课吧课程资料\Flink实时数仓\catalina.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、Flink的分布式缓存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("D:\开课吧课程资料\Flink实时数仓\advert.csv","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)
    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()

}
}

你可能感兴趣的:(flink的广播变量,累加器,计数器以及分布式缓存)