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
2:广播数据
.withBroadcastSet(toBroadcast, "broadcastSetName");
3:获取数据
Collection
注意:
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()
}
}