什麼你還不會Flink的CEP,那你需要好好看看這篇文章

Flink之CEP詳解

一、是什麼

維基百科對CEP的定義爲:“CEP是一種事件處理模式,它從若干源中獲取事件,並偵測複雜環境的事件或模式,CEP的目的是確認一些有意義的事件(比如某種威脅或某種機會),並儘快對其作出響應”。總結一下也就是CEP是一個事件處理模式,當某項檢測需要在多源且複雜的事件流中進行處理,並需要低延遲、秒級或毫秒級的響應時,我們就可以考慮用到它。市場上有多種CEP的解決方案,例如Spark、Samza、Beam等,但他們都沒有提供專門的library支持。但是Flink提供了專門的CEP library。

二、 Flink CEP詳解

Flink中實現一個CEP可以總結爲四步:

  • 構建需要的數據流
  • 構造正確的模式
  • 將數據流和模式進行結合
  • 在模式流中獲取匹配到的數據

其中第一步和第三步一般會是標準操作,核心在於第二部構建模式,需要利用Flink CEP支持的特性,構造出正確反映業務需求的匹配模式。

2.1 詳解Flink CEP library

Flink爲CEP所提供的Flink CEP library包含如下組件:

  • Event Stream
  • pattern定義
  • pattern檢測
  • 生成Alert

首先,開發人員要在DataStream流上定義出模式條件,之後Flink CEP引擎進行模式檢測,必要時生成告警。
爲了使用Flink CEP,我們需要導入依賴:

<dependency>
  <groupId>org.apache.flink</groupId>
  <artifactId>flink-cep_${scala.binary.version}</artifactId>
  <version>${flink.version}</version>
</dependency>

2.1.1 Event Streams

以登陸事件流爲例:

case class LoginEvent(userId: String, ip: String, eventType: String, eventTime: String)

val env = StreamExecutionEnvironment.getExecutionEnvironment
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
env.setParallelism(1)

val loginEventStream = env.fromCollection(List(
  LoginEvent("001", "192.168.0.101", "fail", "1558430842"),
  LoginEvent("001", "192.168.0.102", "fail", "1558430843"),
  LoginEvent("001", "192.168.0.103", "fail", "1558430844"),
  LoginEvent("002", "192.168.10.104", "success", "1558430845")
)).assignAscendingTimestamps(_.eventTime.toLong)

2.1.2 Pattern API

每個Pattern都應該包含幾個步驟,或者叫做state。從一個state到另一個state,通常我們需要定義一些條件,例如下列的代碼:

val loginFailPattern = Pattern.begin[LoginEvent]("begin")
  .where(_.eventType.equals("fail"))
  .next("next")
  .where(_.eventType.equals("fail"))
  .within(Time.seconds(10)

每個state都應該有一個標示:例如.begin[LoginEvent]("begin")中的"begin"
每個state都需要有一個唯一的名字,而且需要一個filter來過濾條件,這個過濾條件定義事件需要符合的條件,例如:
.where(_.eventType.equals("fail"))
我們也可以通過subtype來限制event的子類型:
start.subtype(SubEvent.class).where(...);
事實上,你可以多次調用subtype和where方法;而且如果where條件是不相關的,你可以通過or來指定一個單獨的filter函數:
pattern.where(...).or(...);
之後,我們可以在此條件基礎上,通過next或者followedBy方法切換到下一個state,next的意思是說上一步符合條件的元素之後緊挨着的元素;而followedBy並不要求一定是挨着的元素。這兩者分別稱爲嚴格近鄰和非嚴格近鄰。

val strictNext = start.next("middle")
val nonStrictNext = start.followedBy("middle")

最後,我們可以將所有的Pattern的條件限定在一定的時間範圍內:
next.within(Time.seconds(10))
這個時間可以是Processing Time,也可以是Event Time。

注:有人可能會說API介紹的不夠詳細,所以這裏推薦一篇關於Pattern API詳解的博客

2.1.3 Pattern 檢測

通過一個input DataStream以及剛剛我們定義的Pattern,我們可以創建一個

PatternStream:
val input = ...
val pattern = ...

val patternStream = CEP.pattern(input, pattern)
val patternStream = CEP.pattern(loginEventStream.keyBy(_.userId), loginFailPattern)

一旦獲得PatternStream,我們就可以通過select或flatSelect,從一個Map序列找到我們需要的警告信息。

2.1.4 生成Alert

2.1.4.1 select

select方法需要實現一個PatternSelectFunction,通過select方法來輸出需要的警告。它接受一個Map對,包含string/event,其中key爲state的名字,event則爲真實的Event

val loginFailDataStream = patternStream
  .select((pattern: Map[String, Iterable[LoginEvent]]) => {
    val first = pattern.getOrElse("begin", null).iterator.next()
    val second = pattern.getOrElse("next", null).iterator.next()

    Warning(first.userId, first.eventTime, second.eventTime, "warning")
  })

其返回值僅爲1條記錄。

2.1.4.2 flatSelect

通過實現PatternFlatSelectFunction,實現與select相似的功能。唯一的區別就是flatSelect方法可以返回多條記錄,它通過一個Collector[OUT]類型的參數來將要輸出的數據傳遞到下游。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章