*此文用到的数据为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。
先运行消费者,再运行生产者。