一、生成自定义数据源
案例1: 自定义手机数据源
格式:(序号 品牌 时间戳 状态(0,1,2) 价格)
示例:(1 华为 123454 1 9999)
//【生成手机自定义数据源】
//格式:(序号 品牌 时间戳 状态(0,1,2) 价格)
//示例:(1 华为 123454 1 9999)
class telePhone extends SourceFunction[String]{ //指定其输出类型为String
//生成数据并将其发送到Flink流中。它接受一个SourceContext对象作为参数,用于将数据发送到流中。
override def run(sourceContext: SourceFunction.SourceContext[String]): Unit = {
//第一种循环方式
var i=1
//开始一个无限循环,源源不断生成数据源
while (true){
//定义一个包含手机品牌名称的列表。
val names: List[String] = List("华为", "三星", "小米", "苹果")
//打乱上面的列表:Random.shuffle(),并随机取出一个品牌名称:.head。
val name: String = Random.shuffle(names).head
//获取当前的时间戳。
val time: Long = System.currentTimeMillis()
//生成一个0或1的随机数,用于表示商品状态
val state: Int = Random.nextInt(2)
//生成一个1000到10000之间的随机整数,表示手机的价格。
//+1000的原因是保证手机的最低价格不低于1000,最高价格不高于10000
val price: Int = Random.nextInt(9001) + 1000
//输出到数据流
sourceContext.collect(i+","+name+","+time+","+state+","+price)
i=i+1
Thread.sleep(1000) //让当前线程休眠1秒,以控制数据生成的速率。
/*
//第二种循环方式
// for (i <- 1 until 10000) { //不包括10000
// sourceContext.collect(i.toString)
// }
//第三种循环方式
// var i=0
// (1 to 10000).foreach(m=>{
// sourceContext.collect(i.toString)
// })
*/
}
}
//cancel方法:用于在流处理任务被取消或停止时执行一些清理操作。
// 当Flink任务因为某种原因(如用户取消任务或任务失败)而停止时,这个方法会被调用。
override def cancel(): Unit = {
//Unit表示这个方法没有返回值
}
}
案例2: 自定义传感器数据源
格式:(名字 时间 温度)
示例:(sensor1 1234567 98.77)
//【自定义一个传感器数据源】
//格式:(名字 时间 温度)
//示例:(sensor1 12345678 98.77)
class sensor extends SourceFunction[String]{
override def run(sourceContext: SourceFunction.SourceContext[String]): Unit = {
while (true) {
//生成一个传感器名称,格式为sensor后跟一个0到9之间的随机整数。
val name: String = "sensor" + Random.nextInt(10) //[0,1)
//获取当前时间的时间戳
val time: Long = System.currentTimeMillis()
//创建一个SimpleDateFormat对象format,用于格式化时间戳为字符串
val format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS")
//使用format对象将时间戳time时间戳格式化为字符串
val time1: String = format.format(time)
//产生的小数(浮点数)是0到1的范围,乘100是为了模拟一个0到100之间的温度值
val temp: Double = Random.nextDouble() * 100
//数据源中不可以直接写按条件输出!!!故以下内容被注释!
//Q1:如果温度超出50度,发出预警信息
// if(temp >50){
// //输出
// sourceContext.collect(name + " " + time1 + " 温度为" + temp.formatted("%.2f")+" 发出预警")
// }
sourceContext.collect(name + " " + time + " " + temp.formatted("%.2f"))
Thread.sleep(1000)
}
}
override def cancel(): Unit = {
}
}
二、读取自定义数据
object readMySource {
def main(args: Array[String]): Unit = {
//引入流式环境
val env: StreamExecutionEnvironment = StreamExecutionEnvironment
.getExecutionEnvironment
//addSource将数据源添加到flink的流环境
env.addSource(new sensor)
.setParallelism(1) //流处理的并行度为1。
.print()
env.execute()
}
}
三、传参方式添加自定义数据源
①设置传参的工具
//传参,设置传参的工具
val tool: ParameterTool = ParameterTool.fromArgs(args)
val source: String = tool.get("sourcename")
②对传入参数进行判断
方法1:用if条件对传入参数进行判断
//用if条件对传入参数进行判断
val sourceData:SourceFunction[String]={
if(source == "phone"){
new telePhone()
}else if(source == "sensor"){
new sensor()
}else{
println("传参有问题!")
//sys.exit是退出程序的系统调用,1表示程序由于错误而非正常退出。
sys.exit(1)
}
}
方法2:用模式匹配的方式对传入参数进行判断
//用模式匹配的方式对传入参数进行判断
val sourceData:SourceFunction[String]= {
source match {
case "phone" => new telePhone()
case "sensor" => new sensor()
case "_" => {
println("传参有问题")
sys.exit(1)
}
}
}
env.addSource(sourceData)
③将数据源添加到flink的流环境
//将数据源添加到flink的流环境
env.addSource(sourceData)
.print()
.setParallelism(1)
四、案例相关例题
案例1——对手机数据进行分流,如果价格大于7000,输出高端机,否则输出低端机
【数据分流】
所谓“分流”,就是将一条数据流拆分成完全独立的两条、甚至多条流。
分流操作不对数据做任何筛选操作,只为数据打上标签并输出。也就是基于一个DataStream,定义一些筛选条件,将符合条件的数据拣选出来放到对应的流里。
基本步骤为:
- 使用process算子(Flink分层API中的最底层的处理函数)
- 定义OutputTag对象,即输出标签对象,用于后面标记和提取侧流
- 调用上下文ctx的.output()方法
- 打印数据
【案例】
①切割数据,封装样例类
//【Q2:对手机数据进行分流,如果价格大于7000,输出高端机,否则输出低端机】
val value: DataStream[Phone] = env.addSource(new telePhone)
.map(x => {
val strings: Array[String] = x.split(",")
//封装数据,需要先定义一个样例类
Phone(strings(1), strings(2).toLong, strings(3).toInt, strings(4).toInt)
})
.process()
②编写数据分流条件的自定义类
//【Q2:对手机数据进行分流,如果价格大于7000,输出高端机,否则输出低端机】
// ProcessFunction[输入的数据类型, 输出到主流上的数据类型]
class splitPhone extends ProcessFunction[Phone,Phone]{
override def processElement(i: Phone,
context: ProcessFunction[Phone, Phone]#Context,
collector: Collector[Phone]): Unit = {
if (i.price>7000){
//走测输出流
context.output(new OutputTag[Phone]("高端机"),i)
}else{
//走主流
collector.collect(i)
}
}
}
③打印数据
.process(new splitPhone)
//打印主流
value.print("低端机:")
//打印测输出流
value.getSideOutput(new OutputTag[Phone]("高端机")).print("高端机:")
案例2——如果温度超过50度,发出预警信息
env.addSource(new sensor)
.map(x=>{
val strings: Array[String] = x.split(" ")
val format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
val time: String = format.format(strings(1).toLong)
//取出(传感器,时间,温度)
(strings(0),time,strings(2).toDouble)
})
.keyBy(_._1)
.process(new tempWarn)
.print()
.setParallelism(1)
//【Q1:如果温度超过50度,发出预警信息】
class tempWarn extends KeyedProcessFunction[String,(String,String,Double),String]{
override def processElement(i: (String, String, Double),
context: KeyedProcessFunction[String, (String, String, Double), String]#Context,
collector: Collector[String]): Unit = {
if (i._3>50){
collector.collect(s"${i._1}在${i._2}时间温度超过50度,当前温度为${i._3}")
}
}
}