day4:【ais船舶案例,使用process算子】

*此文用到的数据为ais船舶数据ship_trajectory.csv,可自取
链接:https://pan.baidu.com/s/1C2CkWzwoM4fhcp3NrRnFJQ?pwd=fx11 
提取码:fx11
一条数据表示一条报文数,数据“mmsi,receive,lng,lat,speed,course,trajectory_id1”分别表示“船舶号,收到报文时间,经度,维度,速度,航向,
轨迹ID”。

案例1:输出5个船舶字段,分别是mmsi,总报文数,平均速度,最快速度,最慢速度

**创建样例类caseClass.scala

//船舶数据样例类
case class caseClass(mmsi:String,count:Int,avg:Double,fast:Double,slow:Double)
import org.apache.flink.api.common.eventtime.WatermarkStrategy
import org.apache.flink.api.common.serialization.SimpleStringSchema
import org.apache.flink.api.common.state.{ValueState, ValueStateDescriptor}
import org.apache.flink.connector.kafka.source.KafkaSource
import org.apache.flink.connector.kafka.source.enumerator.initializer.OffsetsInitializer
import org.apache.flink.streaming.api.scala.{DataStream, StreamExecutionEnvironment}
import org.apache.flink.api.scala._
import org.apache.flink.configuration.Configuration //!解决报错:value toDouble is not a member of String
import org.apache.flink.streaming.api.functions.KeyedProcessFunction
import org.apache.flink.util.Collector

object fast_slow_speed {
  def main(args: Array[String]): Unit = {
    //【Q1:输出传播的5个字段,分别是mmsi,总报文数,平均速度,最快速度,最慢速度】
    //1-> 引入环境
    val env: StreamExecutionEnvironment = StreamExecutionEnvironment
      .getExecutionEnvironment

    //2-> 生成一个kafka的消费者,读kafka数据
    val kafkaConsumer = KafkaSource.builder[String]()
      //设置kafka的地址
      .setBootstrapServers("192.168.126.51:9092,192.168.126.52:9092,192.168.126.53:9092")
      //设置组别,用来区分不同的人消费同一个主题的数据
      .setGroupId("paipai")
      //设置主题
      .setTopics("flink")
      //设置是从头消费还是从最新位置开始消费
      .setStartingOffsets(OffsetsInitializer.latest())
      //对value进行 反序列化
      .setValueOnlyDeserializer(new SimpleStringSchema())
      //这个不能丢
      .build()

    //3-> 开始读数据
    val value: DataStream[String] = env
      .fromSource(kafkaConsumer, WatermarkStrategy.noWatermarks(), "kafka")

    value
      //过滤首行
      .filter(!_.contains("mmsi"))
      .map(x=>{
        val strings: Array[String] = x.split(",")
        (strings(0),strings(4).toDouble)  //(mmsi,速度)
      })
      .keyBy(_._1)
      .process(new fastSlowSpeed )
      .print()

    env.execute()

  }
}

class fastSlowSpeed extends KeyedProcessFunction[String,(String,Double),(caseClass)]{
  //定义4个状态变量
  //状态变量1:总报文数
  lazy val sum: ValueState[Int] = getRuntimeContext
    .getState(new ValueStateDescriptor[Int]("sum", classOf[Int]))
  //状态变量2:计算(每条船)总的速度
  lazy val sum_speed: ValueState[Double] = getRuntimeContext
    .getState(new ValueStateDescriptor[Double]("sum_speed", classOf[Double]))
  //状态变量3:计算(每条船)最快速度
  lazy val fast_speed: ValueState[Double] = getRuntimeContext
    .getState(new ValueStateDescriptor[Double]("fast_speed", classOf[Double]))
  //状态变量4:计算(每条船)最慢速度
  lazy val slow_speed: ValueState[Double] = getRuntimeContext
    .getState(new ValueStateDescriptor[Double]("slow_speed", classOf[Double]))

  //processElement的方法,每进来一个事件,也就是每进来一条数据就会使用这个方法。
  override def processElement(i: (String, Double),
                              context: KeyedProcessFunction[String, (String, Double), caseClass]#Context,
                              collector: Collector[caseClass]): Unit = {

    //更新总报文
    sum.update(sum.value()+1)
    //更新总速
    sum_speed.update(sum_speed.value()+i._2)
    //求出平均速度
    val avg: Double = sum_speed.value() / sum.value()
    //更新最大速度
    if(i._2>fast_speed.value()){
      fast_speed.update(i._2)
    }
    //更新最慢速度
    if (slow_speed.value()>i._2){
      slow_speed.update(i._2)
    }
    //step4:输出,要封装样例类
    collector.collect(caseClass(i._1,sum.value(),avg,fast_speed.value(),slow_speed.value()))
  }
}

案例2:如果船舶最快速度超过15,那么就输出这个船舶的信息,发出报警

//【Q2:发出预警,如果船舶最快速度超过15,那么就输出这个船舶的信息,发出报警】
object fast_slow_speed {
  def main(args: Array[String]): Unit = {
    //【Q1:输出传播的5个字段,分别是mmsi,总报文数,平均速度,最快速度,最慢速度】
    //1-> 引入环境
    val env: StreamExecutionEnvironment = StreamExecutionEnvironment
      .getExecutionEnvironment

    //2-> 生成一个kafka的消费者,读kafka数据
    val kafkaConsumer = KafkaSource.builder[String]()
      //设置kafka的地址
      .setBootstrapServers("192.168.126.51:9092,192.168.126.52:9092,192.168.126.53:9092")
      //设置组别,用来区分不同的人消费同一个主题的数据
      .setGroupId("paipai")
      //设置主题
      .setTopics("flink")
      //设置是从头消费还是从最新位置开始消费
      .setStartingOffsets(OffsetsInitializer.latest())
      //对value进行 反序列化
      .setValueOnlyDeserializer(new SimpleStringSchema())
      //这个不能丢
      .build()

    //3-> 开始读数据
    val value: DataStream[String] = env
      .fromSource(kafkaConsumer, WatermarkStrategy.noWatermarks(), "kafka")

    value
      //过滤首行
      .filter(!_.contains("mmsi"))
      .map(x=>{
        val strings: Array[String] = x.split(",")
        (strings(0),strings(4).toDouble)  //(mmsi,速度)
      })
      .keyBy(_._1)
      .process(new fastWarn)
      .print()

    env.execute()

  }
}
//【Q2:发出预警,如果船舶最快速度超过15,那么就输出这个船舶的信息,发出报警】
class fastWarn extends KeyedProcessFunction[String,(String,Double),String]{
  //定义2个状态变量
  //状态变量1:存放最快速度
  lazy val fast_speed: ValueState[Double] = getRuntimeContext
    .getState(new ValueStateDescriptor[Double]("fast_speed", classOf[Double]))
  //状态变量2:存放超速次数
  lazy val warn_count: ValueState[Int] = getRuntimeContext
    .getState(new ValueStateDescriptor[Int]("warn_count", classOf[Int]))

  override def processElement(i: (String, Double),
                              context: KeyedProcessFunction[String, (String, Double), String]#Context,
                              collector: Collector[String]): Unit = {
    if (i._2>fast_speed.value()){
      //更新最快速度
      fast_speed.update(i._2)
    }
    if (fast_speed.value()>15){
      //更新报警次数
      warn_count.update(warn_count.value()+1)
      //输出报警信息
      collector
        .collect(s"${i._1}号船超速${warn_count.value()}次,此刻速度为${fast_speed.value()}")
    }
  }
}

**注意:运行前打开Kafka。
             先运行消费者,再运行生产者。
            

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值