学习目标
1.入门案例(掌握)
2.输入数据集DataSource(掌握)
3.DataSet的Transformation(掌握)
4.数据的输出DataSink(掌握)
5.Flink的广播变量(掌握)
6.Flink的分布式缓存(掌握)
7.Flink的累加器(了解)
1.入门案例
/**
* 单词统计,入门案例
*/
object BatchWordCount {
def main(args: Array[String]): Unit = {
//1.创建批处理执行环境
val env: ExecutionEnvironment = ExecutionEnvironment.getExecutionEnvironment
//2.构建数据源
//导入隐式转换包
import org.apache.flink.api.scala._
val sourceDataSet: DataSet[String] = env.fromCollection(List("hadoop hadoop spark","flink flink hive"))
//3.数据处理
val wordAndOne: DataSet[(String, Int)] = sourceDataSet.flatMap(x=>x.split(" ")).map((_,1))
val resultDataSet: DataSet[(String, Int)] = wordAndOne.groupBy(0).reduce((v1, v2)=>(v1._1,v1._2+v2._2))
//4.打印测试
resultDataSet.print()
}
}
2.输入数据集DataSource
1) 基于本地集合的 source
1.1 使用 env.fromElements(), 这种方式也支持 Tuple, 自定义对象等复合形式。
//使用 env.fromElements(), 这种方式也支持 Tuple, 自定义对象等复合形式
object BatchFromElementsDemo {
def main(args: Array[String]): Unit = {
//1.创建执行环境
val env: ExecutionEnvironment = ExecutionEnvironment.getExecutionEnvironment
//2.使用 env.fromElements构建数据集
import org.apache.flink.api.scala._
val sourceElement: DataSet[String] = env.fromElements("hadoop hadoop hive flink")
//3.输出测试
sourceElement.print()
}
}
1.2 使用 env.fromCollection(),这种方式支持多种 Collection 的具体类型
object BatchFromCollectionDemo {
def main(args: Array[String]): Unit = {
//1.创建执行环境
val env: ExecutionEnvironment = ExecutionEnvironment.getExecutionEnvironment
//2.使用env.fromCollection()构建数据集
import org.apache.flink.api.scala._
val sourceCollection: DataSet[String] = env.fromCollection(List("hadoop hadoop","flink flink"))
//3.输出打印
sourceCollection.print()
}
}
1.3 使用 env.generateSequence()方法创建基于 Sequence 的 DataSet
object BatchGenerateSequenceDemo {
def main(args: Array[String]): Unit = {
//1.创建执行环境
val env: ExecutionEnvironment = ExecutionEnvironment.getExecutionEnvironment
//2.使用generateSequence构建数据集
val numberDataSet: DataSet[Long] = env.generateSequence(1,10)
//3.打印输出
numberDataSet.print()
}
}
2) 基于文件的 source
2.1 读取本地文件
object BatchFromLocalFileSource {
def main(args: Array[String]): Unit = {
//1.创建执行环境
val env: ExecutionEnvironment = ExecutionEnvironment.getExecutionEnvironment
//2.从本地文件构建数据集
val localFileSource: DataSet[String] = env.readTextFile("day02/data/input/wordcount.txt")
//3.打印输出
localFileSource.print()
}
}
2.2 读取 HDFS 数据
object BatchFromLocalFileSource {
def main(args: Array[String]): Unit = {
//1.创建执行环境
val env: ExecutionEnvironment = ExecutionEnvironment.getExecutionEnvironment
//2.从本地文件构建数据集
val localFileSource: DataSet[String] = env.readTextFile("day02/data/input/wordcount.txt")
//3.打印输出
localFileSource.print()
}
}
2.3 读取 CSV 数据
object BatchFromCSVFileSource {
case class Subject(id:Int,name:String)
def main(args: Array[String]): Unit = {
//1.创建执行环境
val env: ExecutionEnvironment = ExecutionEnvironment.getExecutionEnvironment
//2.从csv文件构建数据集
import org.apache.flink.api.scala._
val csvDataSet: DataSet[Subject] = env.readCsvFile[Subject]("day02/data/input/subject.csv")
//3.输出打印
csvDataSet.print()
}
}
2.4 读取压缩文件
在这里插入代码片
object BatchFromCompressFileSource {
def main(args: Array[String]): Unit = {
//1.创建执行环境
val env: ExecutionEnvironment = ExecutionEnvironment.getExecutionEnvironment
//2.从压缩文件中构建数据集
val compressFileSource: DataSet[String] = env.readTextFile("day02/data/input/wordcount.txt.gz")
//3.输出打印
compressFileSource.print()
}
}
2.5 基于文件的 source(遍历目录)
在这里插入代码片object BatchFromFolderSource {
def main(args: Array[String]): Unit = {
//1.创建执行环境
val env: ExecutionEnvironment = ExecutionEnvironment.getExecutionEnvironment
//2.开启recursive.file.enumeration
val configuration: Configuration = new Configuration()
configuration.setBoolean("recursive.file.enumeration", true)
//3.根据遍历多级目录来构建数据集
val result: DataSet[String] = env.readTextFile("day02/data/input/a").withParameters(configuration)
result.print()
}
}
3、DateSet 的 Transformation
3.1 map 函数
-
获取 ExecutionEnvironment 运行环境
-
使用 fromCollection 构建数据源
-
创建一个 User 样例类
-
使用 map 操作执行转换
-
打印测试
package com.czxy.flink.batch.transfromation
import org.apache.flink.api.scala.ExecutionEnvironment
/**
* 需求:
* 使用 map 操作, 将以下数据
* "1,张三", "2,李四", "3,王五", "4,赵六"
* 转换为一个 scala 的样例类。
*/
object BatchMapDemo {
//3.创建样例类
case class user(id:Int,name:String)
def main(args: Array[String]): Unit = {
//1.创建执行环境
val env = ExecutionEnvironment.getExecutionEnvironment
//2.构建数据集
import org.apache.flink.api.scala._
val sourceDataSet: DataSet[String] = env.fromElements("1,张三", "2,李四", "3,王五", "4,赵六")
//4.数据转换处理
val userDataSet: DataSet[user] = sourceDataSet.map(item => {
val itemsArr: Array[String] = item.split(",")
user(itemsArr(0).toInt, itemsArr(1))
})
//5.打印输出
userDataSet.print()
}
}
3.2 flatMap 函数
package com.czxy.flink.batch.transfromation
import org.apache.flink.api.scala.ExecutionEnvironment
import scala.collection.mutable
/**
* 需求:
* 分别将以下数据, 转换成 国家 、 省份 、 城市 三个维度的数据。
* 将以下数据
* 张三,中国,江西省,南昌市
* 李四,中国,河北省,石家庄市
* Tom,America,NewYork,Manhattan
* 转换为
* 张三,中国
* 张三,中国江西省
* 张三,中国江西省南昌市
*/
object BatchFlatMapDemo {
def main(args: Array[String]): Unit = {
/**
* 1) 构建批处理运行环境
* 2) 构建本地集合数据源
* 3) 使用 flatMap 将一条数据转换为三条数据
* a. 使用逗号分隔字段
* b. 分别构建国家、 国家省份、 国家省份城市三个元组
* 4) 打印输出
*/
val env: ExecutionEnvironment = ExecutionEnvironment.getExecutionEnvironment
import org.apache.flink.api.scala._
val sourceDatSet: DataSet[String] = env.fromCollection(List(
"张三,中国,江西省,南昌市",
"李四,中国,河北省,石家庄市",
"Tom,America,NewYork,Manhattan"
))
val resultDataSet: DataSet[(String, String)] = sourceDatSet.flatMap(item => {
val itemsArr: mutable.ArrayOps[String] = item.split(",")
List(
(itemsArr(0), itemsArr(1)),
(itemsArr(0), itemsArr(1) + itemsArr(2)),
(itemsArr(0), itemsArr(1) + itemsArr(2) + itemsArr(3))
)
})
resultDataSet.print()
}
}
3.3 mapPartition 函数
package com.czxy.flink.batch.transfromation
import org.apache.flink.api.scala.ExecutionEnvironment
/**
* 需求:
* 使用 mapPartition 操作, 将以下数据
* "1,张三", "2,李四", "3,王五", "4,赵六"
* 转换为一个 scala 的样例类。
*/
object BatchMapPartitionDemo {
case class user(id:Int,name:String)
def main(args: Array[String]): Unit = {
//1.创建执行环境
val env: ExecutionEnvironment = ExecutionEnvironment.getExecutionEnvironment
//2.构建数据集
import org.apache.flink.api.scala._
val sourceDataSet: DataSet[String] = env.fromElements("1,张三", "2,李四", "3,王五", "4,赵六")
//3数据处理
val userDataSet: DataSet[user] = sourceDataSet.mapPartition(itemPartition => {
itemPartition.map(item => {
val itemsArr: Array[String] = item.split(",")
user(itemsArr(0).toInt, itemsArr(1))
})
})
//4.打印数据
userDataSet.print()
}
}
3.4 Rebalance
在这里插入代码片package com.czxy.flink.batch.transfromation
import org.apache.flink.api.common.functions.RichMapFunction
import org.apache.flink.api.scala.{DataSet, ExecutionEnvironment}
/**
* 实现步骤:
* 1) 构建批处理运行环境
* 2) 使用 env.generateSequence 创建 0-100 的并行数据
* 3) 使用 fiter 过滤出来 大于 8 的数字
* 4) 使用 map 操作传入 RichMapFunction , 将当前子任务的 ID 和数字构建成一个元组
* 5) 在 RichMapFunction 中可以使用 getRuntimeContext.getIndexOfThisSubtask 获取子
* 任务序号
* 6) 打印测试
*/
object BatchRebalanceDemo {
def main(args: Array[String]): Unit = {
//1) 构建批处理运行环境
val env: ExecutionEnvironment = ExecutionEnvironment.getExecutionEnvironment
//2) 使用 env.generateSequence 创建 0-100 的并行数据
val source: DataSet[Long] = env.generateSequence(0,100)
//3) 使用 fiter 过滤出来 大于 8 的数字
val filter: DataSet[Long] = source.filter(_>8)
//使用rebalance进行处理数据
val rebalance: DataSet[Long] = filter.rebalance()
//4) 使用 map 操作传入 RichMapFunction , 将当前子任务的 ID 和数字构建成一个元组
import org.apache.flink.api.scala._
val result: DataSet[(Int, Long)] = rebalance.map(new RichMapFunction[Long, (Int, Long)] {
override def map(value: Long): (Int, Long) = {
(getRuntimeContext.getIndexOfThisSubtask, value)
}
})
result.print()
}
}
4 数据输出 Data Sinks
1) 基于本地集合的 sink
package com.czxy.flink.batch.sink
import org.apache.flink.api.scala.ExecutionEnvironment
//基于本地集合的sink
object BatchSinkCollection {
def main(args: Array[String]): Unit = {
//1.创建执行环境
val env: ExecutionEnvironment = ExecutionEnvironment.getExecutionEnvironment
//2.构建数据集
import org.apache.flink.api.scala._
val source: DataSet[(Int, String, Double)] = env.fromElements(
(19, "zhangsan", 178.8),
(17, "lisi", 168.8),
(18, "wangwu", 184.8),
(21, "zhaoliu", 164.8)
)
//3.数据打印
source.print()
println(source.collect())
source.printToErr()
}
}
2) 基于文件的 sink
package com.czxy.flink.batch.sink
import org.apache.flink.api.scala.ExecutionEnvironment
import org.apache.flink.core.fs.FileSystem.WriteMode
//基于文件的 sink
object BatchSinkFile {
def main(args: Array[String]): Unit = {
//1.创建执行环境
val env: ExecutionEnvironment = ExecutionEnvironment.getExecutionEnvironment
//2.构建数据集
import org.apache.flink.api.scala._
val source: DataSet[(Int, String, Double)] = env.fromElements(
(19, "zhangsan", 178.8),
(17, "lisi", 168.8),
(18, "wangwu", 184.8),
(21, "zhaoliu", 164.8)
)
//保存到本地文件
// source.writeAsText("day02/data/output/sinkLocalFile").setParallelism(1)
//保存到HDFS文件中
source.writeAsText("hdfs://node01:8020/test/output/sinkHDFSFile0708",WriteMode.OVERWRITE).setParallelism(1)
env.execute(this.getClass.getSimpleName)
}
}
package com.czxy.flink.batch.sink
import org.apache.flink.api.scala.ExecutionEnvironment
import org.apache.flink.core.fs.FileSystem.WriteMode
//基于文件的 sink
object BatchSinkFile {
def main(args: Array[String]): Unit = {
//1.创建执行环境
val env: ExecutionEnvironment = ExecutionEnvironment.getExecutionEnvironment
//2.构建数据集
import org.apache.flink.api.scala._
val source: DataSet[(Int, String, Double)] = env.fromElements(
(19, "zhangsan", 178.8),
(17, "lisi", 168.8),
(18, "wangwu", 184.8),
(21, "zhaoliu", 164.8)
)
//保存到本地文件
// source.writeAsText("day02/data/output/sinkLocalFile").setParallelism(1)
//保存到HDFS文件中
source.writeAsText("hdfs://node01:8020/test/output/sinkHDFSFile0708",WriteMode.OVERWRITE).setParallelism(1)
env.execute(this.getClass.getSimpleName)
}
}
5 、Flink 的广播变量
广播就是一个公共的共享变量 ,将一个数据集广播后, 不同的 Task 都可以在节点上获取到 ,每个节点 只存一份 ,广播变量的数据大小要小于内存的大小,在这里插入代码片
如果不使用广播, 每一个 Task 都会拷贝一份数据集, 造成内存资源浪费
package com.czxy.flink.batch
import java.util
import org.apache.flink.api.common.functions.RichMapFunction
import org.apache.flink.api.scala.ExecutionEnvironment
import org.apache.flink.configuration.Configuration
/**
* 需求:
* 创建一个 学生 数据集, 包含以下数据
* |学生 ID | 姓名 |
* |------|------|
* List((1, "张三"), (2, "李四"), (3, "王五"))
* 将该数据, 发布到广播。
* 再创建一个 成绩 数据集,
* |学生 ID | 学科 | 成绩 |
* |------|------|-----|
* List( (1, "语文", 50),(2, "数学", 70), (3, "英文", 86))
* 请通过广播获取到学生姓名, 将数据转换为
* List( ("张三", "语文", 50),("李四", "数学", 70), ("王五", "英文", 86))
*/
object BatchBroadcastDemo {
def main(args: Array[String]): Unit = {
/**
* 实现步骤:
* 1) 获取批处理运行环境
* 2) 分别创建两个数据集
* 3) 使用 RichMapFunction 对 成绩 数据集进行 map 转换
* 4) 在数据集调用 map 方法后, 调用 withBroadcastSet 将 学生 数据集创建广播
* 5) 实现 RichMapFunction
* a. 将成绩数据(学生 ID, 学科, 成绩) -> (学生姓名, 学科, 成绩)
* b. 重写 open 方法中, 获取广播数据
* c. 导入 scala.collection.JavaConverters._ 隐式转换d. 将广播数据使用 asScala 转换为 Scala 集合, 再使用 toList 转换为 scala List
* 集合
* e. 在 map 方法中使用广播进行转换
* 6) 打印测试
*/
val env: ExecutionEnvironment = ExecutionEnvironment.getExecutionEnvironment
import org.apache.flink.api.scala._
val studentDataSet: DataSet[(Int, String)] = env.fromCollection(List((1, "张三"), (2, "李四"), (3, "王五")))
val scoreDataSet: DataSet[(Int, String, Int)] = env.fromCollection(List((1, "语文", 50), (2, "数学", 70), (3, "英文", 86)))
val resultDataSet: DataSet[(String, String, Int)] = scoreDataSet.map(new RichMapFunction[(Int, String, Int), (String, String, Int)] {
var stuMap: Map[Int, String] = null
override def open(parameters: Configuration): Unit = {
import scala.collection.JavaConversions._
val student: util.List[(Int, String)] = getRuntimeContext.getBroadcastVariable[(Int, String)]("student")
stuMap = student.toMap
}
//每一条数据都会执行一次
override def map(value: (Int, String, Int)): (String, String, Int) = {
val name: String = stuMap.getOrElse(value._1, "")
(name, value._2, value._3)
}
}).withBroadcastSet(studentDataSet, "student")
resultDataSet.print()
}
}
6 、Flink 的分布式缓存
【 注意】 广播是将变量分发到各个 worker 节点的内存上, 分布式缓存是将文件缓存到各个worker 节点上;
package com.czxy.flink.batch
import java.io.File
import org.apache.flink.api.common.functions.RichMapFunction
import org.apache.flink.api.scala.ExecutionEnvironment
import org.apache.flink.configuration.Configuration
import scala.io.Source
/**
* 需求:
* 创建一个 成绩 数据集
* List( (1, "语文", 50),(2, "数学", 70), (3, "英文", 86))
* 请通过分布式缓存获取到学生姓名, 将数据转换为
* List( ("张三", "语文", 50),("李四", "数学", 70), ("王五", "英文", 86))
* 注: distribute_cache_student 测试文件保存了学生 ID 以及学生姓名
*/
object BatchDisCachedFile {
def main(args: Array[String]): Unit = {
/**
* 实现步骤:
* 1) 将 distribute_cache_student 文件上传到 HDFS / 目录下
* 2) 获取批处理运行环境
* 3) 创建成绩数据集
* 4) 对 成绩 数据集进行 map 转换, 将( 学生 ID, 学科, 分数) 转换为( 学生姓名, 学科,
* 分数)
* a. RichMapFunction 的 open 方法中, 获取分布式缓存数据
* b. 在 map 方法中进行转换
* 5) 实现 open 方法
* a. 使用 getRuntimeContext.getDistributedCache.getFile 获取分布式缓存文件
* b. 使用 Scala.fromFile 读取文件, 并获取行
* c. 将文本转换为元组( 学生 ID, 学生姓名) , 再转换为 List
* 6) 实现 map 方法
* a. 从分布式缓存中根据学生 ID 过滤出来学生b. 获取学生姓名
* c. 构建最终结果元组
* 7) 打印测试
*/
// 获取批处理运行环境
val env: ExecutionEnvironment = ExecutionEnvironment.getExecutionEnvironment
//注册分布式缓存文件
env.registerCachedFile("hdfs://node01:8020/test/input/distribute_cache_student", "student")
//创建成绩数据集
import org.apache.flink.api.scala._
val scoreDataSet: DataSet[(Int, String, Int)] = env.fromCollection(List((1, "语文", 50), (2, "数学", 70), (3, "英文", 86)))
val resultDataSet: DataSet[(String, String, Int)] = scoreDataSet.map(new RichMapFunction[(Int, String, Int), (String, String, Int)] {
var stuMap: Map[Int, String] = null
//初始化的时候只被执行一次
override def open(parameters: Configuration): Unit = {
val studentFile: File = getRuntimeContext.getDistributedCache.getFile("student")
val linesIter: Iterator[String] = Source.fromFile(studentFile).getLines()
stuMap = linesIter.map(item => {
val itemArr: Array[String] = item.split(",")
(itemArr(0).toInt, itemArr(1))
}).toMap
}
//每条数据都会执行一次
override def map(value: (Int, String, Int)): (String, String, Int) = {
val name: String = stuMap.getOrElse(value._1, "")
(name, value._2, value._3)
}
})
resultDataSet.print()
}
}
咳咳!你懂的,下回再讲。