flink CEP示例

愛吃甜食_發表於2020-10-01

三分鐘時間內,出現三次及以上的溫度高於40度就算作是異常溫度,進行報警輸出

  • 場景介紹

    • 現在工廠當中有大量的感測裝置,用於檢測機器當中的各種指標資料,例如溫度,溼度,氣壓等,並實時上報資料到資料中心,現在需要檢測,某一個感測器上報的溫度資料是否發生異常。
  • 異常的定義

    • 三分鐘時間內,出現三次及以上的溫度高於40度就算作是異常溫度,進行報警輸出
  • 測試資料
感測器裝置mac地址,檢測機器mac地址,溫度,溼度,氣壓,資料產生時間

00-34-5E-5F-89-A4,00-01-6C-06-A6-29,38,0.52,1.1,2020-03-02 12:20:32
00-34-5E-5F-89-A4,00-01-6C-06-A6-29,47,0.48,1.1,2020-03-02 12:20:35
00-34-5E-5F-89-A4,00-01-6C-06-A6-29,50,0.48,1.1,2020-03-02 12:20:38
00-34-5E-5F-89-A4,00-01-6C-06-A6-29,31,0.48,1.1,2020-03-02 12:20:39
00-34-5E-5F-89-A4,00-01-6C-06-A6-29,52,0.48,1.1,2020-03-02 12:20:41
00-34-5E-5F-89-A4,00-01-6C-06-A6-29,53,0.48,1.1,2020-03-02 12:20:43
00-34-5E-5F-89-A4,00-01-6C-06-A6-29,55,0.48,1.1,2020-03-02 12:20:45

程式碼

import java.util

import org.apache.commons.lang3.time.FastDateFormat
import org.apache.flink.cep.PatternSelectFunction
import org.apache.flink.cep.scala.pattern.Pattern
import org.apache.flink.cep.scala.{CEP, PatternStream}
import org.apache.flink.streaming.api.TimeCharacteristic
import org.apache.flink.streaming.api.scala.{DataStream, KeyedStream, StreamExecutionEnvironment}
import org.apache.flink.streaming.api.windowing.time.Time

//定義溫度資訊pojo
case class DeviceDetail(sensorMac:String,deviceMac:String,temperature:String,dampness:String,pressure:String,date:String)

//報警的裝置資訊樣例類
//感測器裝置mac地址,檢測機器mac地址,溫度
case class AlarmDevice(sensorMac:String,deviceMac:String,temperature:String)

/**
  * 基於FlinkCEP的裝置溫度檢測
  */
object checkTemperature {

  private val format: FastDateFormat = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss")
  def main(args: Array[String]): Unit = {
    val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
    //指定時間型別
    env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
    env.setParallelism(1)
    import org.apache.flink.api.scala._

    //接受資料
    val socDS: DataStream[String] = env.socketTextStream("node01",9999)
    val deviceDS: KeyedStream[DeviceDetail, String] = socDS.map(x => {
      val stringArr: Array[String] = x.split(",")
      DeviceDetail(stringArr(0), stringArr(1), stringArr(2), stringArr(3), stringArr(4), stringArr(5))
    })
    .assignAscendingTimestamps(x => {
       format.parse(x.date).getTime
     })
      .keyBy(x => x.sensorMac)
    //todo:定義Pattern,指定相關條件和模型序列
   val pattern: Pattern[DeviceDetail, DeviceDetail] = Pattern.begin[DeviceDetail]("start")
     .where(x => x.temperature.toInt >= 40)
     .followedByAny("second")
     .where(x => x.temperature.toInt >= 40)
     .followedByAny("third")
     .where(x => x.temperature.toInt >= 40)
     .within(Time.minutes(3))

    //todo:模式檢測,將模式應用到流中
    val patternResult: PatternStream[DeviceDetail] = CEP.pattern(deviceDS,pattern)


    //todo:選取結果
    patternResult.select(new MyPatternResultFunction).print()

    //todo: 啟動
    env.execute("startTempeature")

  }
}

//自定義PatternSelectFunction
class MyPatternResultFunction extends PatternSelectFunction[DeviceDetail,AlarmDevice]{
  override def select(pattern: util.Map[String, util.List[DeviceDetail]]): AlarmDevice = {
    val startDetails: util.List[DeviceDetail] = pattern.get("start")
    val startResult: DeviceDetail = startDetails.iterator().next()
    println("第一條資料: "+ startResult)
    val secondDetails: util.List[DeviceDetail] = pattern.get("start")
    val secondResult: DeviceDetail = secondDetails.iterator().next()
    println("第二條資料: "+ secondResult)
    val thirdDetails: util.List[DeviceDetail] = pattern.get("start")
    val thirdResult: DeviceDetail = thirdDetails.iterator().next()
    println("第三條資料: "+ thirdResult)
    AlarmDevice(thirdResult.sensorMac,thirdResult.deviceMac,thirdResult.temperature)
  }
}

  • 輸入和輸出測試-1
    左閉右開,收到第四條資料時,顯示滿足條件的三條資料在這裡插入圖片描述
  • 輸入和輸出測試-2
    在這裡插入圖片描述
    輸出如圖所示:共4條。
    原因是由followedByAny引起,詳情如下:
    當第一個滿足條件的溫度52(此時有超過3條溫度>=40)出現後,在接受一條資料(左閉右開)顯示第一個紅框內容
    當再接受一個溫度53時,由於非嚴格緊鄰定義,53在前面的3條大於40的溫度中任意選擇兩條,組合成滿足條件的:有3個大於40度的條件。此時按照組合公式:共C 3 2共3種組合
    在這裡插入圖片描述

建立訂單之後15分鐘之內一定要付款,否則就取消訂單

  • 場景介紹

    • 在我們的電商系統當中,經常會發現有些訂單下單之後沒有支付,就會有一個倒數計時的時間值,提示你在15分鐘之內完成支付,如果沒有完成支付,那麼該訂單就會被取消,主要是因為拍下訂單就會減庫存,但是如果一直沒有支付,那麼就會造成庫存沒有了,別人購買的時候買不到,然後別人一直不支付,就會產生有些人買不到,有些人買到了不付款,最後導致商家一件產品都賣不出去
  • 需求

    • 建立訂單之後15分鐘之內一定要付款,否則就取消訂單
  • 訂單資料格式如下型別欄位說明

    • 訂單編號

    • 訂單狀態

      • 1.建立訂單,等待支付
      • 2.支付訂單完成
      • 3.取消訂單,申請退款
      • 4.已發貨
      • 5.確認收貨,已經完成
    • 訂單建立時間

    • 訂單金額

20160728001511050311389390,1,2016-07-28 00:15:11,295
20160801000227050311955990,1,2016-07-28 00:16:12,165
20160728001511050311389390,2,2016-07-28 00:18:11,295
20160801000227050311955990,2,2016-07-28 00:18:12,165
20160728001511050311389390,3,2016-07-29 08:06:11,295
20160801000227050311955990,4,2016-07-29 12:21:12,165
20160804114043050311618457,1,2016-07-30 00:16:15,132
20160801000227050311955990,5,2016-07-30 18:13:24,165

程式碼

import java.util

import org.apache.commons.lang3.time.FastDateFormat
import org.apache.flink.cep.{PatternSelectFunction, PatternTimeoutFunction}
import org.apache.flink.cep.scala.{CEP, PatternStream, pattern}
import org.apache.flink.cep.scala.pattern.Pattern
import org.apache.flink.streaming.api.TimeCharacteristic
import org.apache.flink.streaming.api.functions.timestamps.BoundedOutOfOrdernessTimestampExtractor
import org.apache.flink.streaming.api.scala.{DataStream, KeyedStream, OutputTag, StreamExecutionEnvironment}
import org.apache.flink.streaming.api.windowing.time.Time


/**
  *  訂單下單未支付檢測
  */

case class OrderDetail(orderId:String,status:String,orderCreateTime:String,price :Double)

object orderCheck {

  private val format: FastDateFormat = FastDateFormat.getInstance("yyy-MM-dd HH:mm:ss")

  def main(args: Array[String]): Unit = {

    val environment: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
    environment.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
    environment.setParallelism(1)
    import org.apache.flink.api.scala._
    val sourceStream: DataStream[String] = environment.socketTextStream("node01",9999)

    val keyedStream: KeyedStream[OrderDetail, String] = sourceStream.map(x => {
      val strings: Array[String] = x.split(",")
      OrderDetail(strings(0), strings(1), strings(2), strings(3).toDouble)
    }).assignTimestampsAndWatermarks(new BoundedOutOfOrdernessTimestampExtractor[OrderDetail](Time.seconds(5)) {
      override def extractTimestamp(element: OrderDetail): Long = {
        format.parse(element.orderCreateTime).getTime
      }
    }).keyBy(x=>x.orderId)

    //定義pattern模式,指定條件
    val pattern: Pattern[OrderDetail, OrderDetail] = Pattern.begin[OrderDetail]("start")
      .where(order => order.status.equals("1"))
      .followedBy("second")
      .where(order => order.status.equals("2"))
      .within(Time.minutes(15))
    //呼叫select方法,提取事件序列,超時的事件要做報警提示
    val orderTimeOutputTag = new OutputTag[OrderDetail]("orderTimeOut")
    //將模式應用到流中
    val patternStream: PatternStream[OrderDetail] = CEP.pattern(keyedStream,pattern)

    //選擇結果
    val selectedStream : DataStream[OrderDetail] = patternStream.select(orderTimeOutputTag, new OrderTimeOutPatternFunction,new OrderPatternFunction)
    //列印測輸出流資料,過了15分支還沒支付的資料
    selectedStream.getSideOutput(orderTimeOutputTag).print()

    environment.execute()
  }
}
//訂單超時檢查
class OrderTimeOutPatternFunction extends PatternTimeoutFunction[OrderDetail,OrderDetail]{
  override def timeout(pattern: util.Map[String, util.List[OrderDetail]], timeoutTimestamp: Long): OrderDetail = {
    val detail: OrderDetail = pattern.get("start").iterator().next()
    println("超時訂單號為: "+ detail)
    detail
  }
}
class OrderPatternFunction extends PatternSelectFunction[OrderDetail,OrderDetail]{
  override def select(pattern: util.Map[String, util.List[OrderDetail]]): OrderDetail = {
    val detail: OrderDetail = pattern.get("second").iterator().next()
    println("支付成功的訂單為: "+ detail)
    detail
  }
}

輸出
在這裡插入圖片描述

相關文章