day6:【生成自定义数据源,读取数据】【以传参的方式添加自定义数据源】【数据分流】

一、生成自定义数据源

案例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}")
    }

  }
}

  • 15
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值