- 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() } }
flink学习笔记-广播变量、累加器、分布式缓存
最新推荐文章于 2023-03-13 08:15:00 发布