flink复杂事物处理机制CEP
CEP概念
CEP是Complex Event Processing三个单词的缩写,表示复杂事件处理,是一种基于流处理的技术,CEP是Flink专门为我们提供的一个基于复杂事件监测处理的库,CEP通过一个或多个由简单事件构成的事件流通过一定的规则匹配,然后输出用户想得到的数据,满足规则的复杂事件。
CEP复杂事件处理主要应用于防范网络欺诈、设备故障检测、风险规避和智能营销等领域。Flink 基于 DataStrem API 提供了 FlinkCEP 组件栈,专门用于对复杂事件的处理,帮助用户从流式数据中发掘有价值的信息
简单来说:
CEP就是用户自己制定了一些规则,然后把这些规则应用到实时不断产生的数据流中,最后利用规则对数据做匹配筛选处理
CEP的主要特点
- 目标
- 从有序的简单事件流中发现一些高阶特征
- 输入
- 一个或多个由简单时间构成的事件流
- 处理
- 识别简单时间之间的内在联系,多个符合一定规则的简单时间构成复杂时间
- 输出
- 满足规则的复杂时间
Pattern API
Flink CEP中提供了pattern API用于对输入流数据的复杂时间规则定义,并从时间流中抽取事件结果。
包括四个步骤
- 1)输入事件流的创建
- 2)Pattern的定义
- 3)Pattern应用在事件流上检查
- 4)选取结果
输入流的创建
//获取数据输入流
val input: DataStream[Event] = ...
Pattern的定义
Pattern有两种循环模式:
- 单次执行模式
- 单次执行模式一次只接受一个事件
- 循环执行模式
- 循环执行模式可以接受一个或者多个事件
单次执行模式可以通过指定循环次数变为循环执行模式
每种模式能给将多个条件组合停用到同一事件之上,条件组合可以通过where方法进行叠加
- 每个Pattern通过begin方法定义
//[]表示要匹配的类型,可以是event、object、string等
//start_pattern为对其起的别名
val start = Pattern.begin[Event]("start_pattern")
- 下一步,通过Pattern.where()方法在Pattern上指定Conditition。只有当Condition满足之后,当前的Pattern才会接受事件
start.where(_.getID == "9527")
设置循环次数
对于已经创建好的Pattern,可以指定循环次数,形成循环执行的Pattern
- times
- 通过times指定固定的循环次数
//指定循环触发4次
start.times(4);
//可以执行触发次数范围,让循环执行次数在该范围之内
start.times(2, 4);
- optiional
- 通过optional指定要么不触发,要么触发指定次数
tart.times(4).optional()
start.times(2, 4).optional()
- greedy
- 通过greedy将Pattern标记为贪婪模式,在Pattern匹配成功的前提下,会尽可能多次触发
//触发2、3、4次,尽可能重复执行
start.times(2, 4).greedy()
//触发0、2、3、4次,尽可能重复执行
start.times(2, 4).optional().greedy()
- oneOrMore
- 通过onOrMore方法指定触发一次或者多次
// 触发一次或者多次
start.oneOrMore()
//触发一次或者多次,尽可能重复执行
start.oneOrMore().greedy()
// 触发0次或者多次
start.oneOrMore().optional()
// 触发0次或者多次,尽可能重复执行
start.oneOrMore().optional().greedy()
- timesOrMor
- 通过timesOrMore方法可以指定触发固定此时以上。如:执行两次以上
/ 触发两次或者多次
start.timesOrMore(2);
// 触发两次或者多次,尽可能重复执行
start.timesOrMore(2).greedy()
// 不触发或者触发两次以上,尽可能重执行
start.timesOrMore(2).optional().greedy()
定义触发条件
每个模式都需要指定触发条件,作为事件进入到该模式是否接受的判断依据,当时间中的数值满足了条件是,便进行下一步操作。
在Flink CEP中通过以下方法来指定条件
- pattern.where()
- pattern.or()
- pattern.until()
Pattern 条件有以下三种类型
- Simple Conditions(简单条件)
- Combining Conditions(组合条件)
- Stop condition (终止条件)
Simple Conditions(简单条件)
- 只要根据事件中的字段信息进行判断,是否接受该时间
// 把ID为9527的事件挑选出来
start.where(_.getID == "9527")
Combining Conditions(组合条件)
- 通过where、or将简单条件进行合并(where相当于and)
// 把ID为9527或者年龄大于30的事件挑选出来
val start = Pattern.begin[Event]("start_pattern")
.where(_.callType=="success").or(_.age >30)//.where(_.resType=="success") 多个where相当于and
Stop condition (终止条件)
如果程序中使用了
- oneOrMore
- oneOrMore().optional()方法
还可以指定停止条件,否则模式中的规则会一直循环下去,如下终止条件通过 until()方法指定
start.oneOrMore.until(_.getID == "123")
模式序列
将相互独立的模式进行组合然后形成模式序列。模式序列基本的编写方式和独立模式一 致,各个模式之间通过邻近条件进行连接即可。
- 严格相邻:next
- 严格邻近条件中,需要所有的事件都按照顺序满足模式条件,不允许忽略任意不满足的模式
//示例
begin("first").where(_.name='a').next("second").where(.name='b')
//当且仅当数据为a,b时,模式才会被命中。如果数据为a,c,b,由于a的后面跟了c,所以a会被直接丢弃,模式不会命中。
- 宽松相邻:followedBy
- 在宽松邻近条件下,会忽略没有成功匹配模式条件,并不会像严格邻近要求得那么高
/示例
begin("first").where(_.name='a').followedBy("second").where(.name='b')
//当且仅当数据为a,b或者为a,c,b,,模式均被命中,中间的c会被忽略掉。
- 非确定宽松邻近:followedByAny
- 和宽松邻近条件相比,进一步放宽条件,之前已匹配过的事件也可以再次使用
//示例
begin("first").where(_.name='a').followedByAny("second").where(.name='b')
//当且仅当数据为a,c,b,b时,对于followedBy模式而言命中的为{a,b},
//对于followedByAny而言会有两次命中{a,b},{a,b}
除以上模式序列外,还可以定义“不希望出现某种近邻关系
- notNext()
- 不想让某个事件严格紧邻前一个事件发生
- notFollowedBy()
- 不想让某个事件在两个事件之间发生
注意
-
1、所有模式序列必须以 .begin() 开始
-
2、模式序列不能以 .notFollowedBy() 结束
-
3、“not” 类型的模式不能和optional关键字同时使用
-
4、此外,还可以为模式指定时间约束,用来要求在多长时间内匹配有效
//指定模式在10秒内有效
pattern.within(Time.seconds(10))
Pattern检测
调用CEP.pattern()方法,给定输入流和模式,就能得到一个PatternStream
//Pattern检测
val patternStream = CEP.pattern[Event](dataStream,pattern)
选取结果
得到PatternStream 类型的数据集后,接下来数据获取都基于PatternStream 进行。该数据集中只包含所有的匹配事件。
Flink CEP提供两种方法从PatternStream 提取事件结果
- select
- flatSelect
通过select抽取正常事件
在PatternStream 的Select方法中传入自定义的Select Function
函数解析如下:
- 参数: Map[String, Iterable[IN]]
- Map中的key:模式序列中的Pattern名称
- Map中的value:Pattern 所接受的事件集合,格式为输入事件的数据类型
def selectFunction(pattern : Map[String, Iterable[IN]]): OUT = {
//获取pattern中的start
Event val startEvent = pattern.get("start_pattern").get.next
//获取Pattern中middle
Event val middleEvent = pattern.get("middle").get.next
//返回结果
OUT(startEvent, middleEvent)
}
通过flatSelect抽取正常事件
- Flat Select Funciton与Select Function 相比,可以返回任意数量的结果。
- Flat Select Funciton使用Collector 作为返回结果的容器,可以将需要出的事件都放置在Collector中返回
def flatSelectFunction(pattern : Map[String, Iterable[IN]]),collector:Collector[OUT] = {
//获取pattern中的start
Event val startEvent = pattern.get("start_pattern").get.next
//获取Pattern中middle
Event val middleEvent = pattern.get("middle").get.next
//并根据startEvent的Value数量进行返回
for (i <- 0 to startEvent.getValue) {
collector.collect(OUT(startEvent