一、什么是 CEP
- CEP 是 Complex Event Processing 的缩写,意为"复杂事件处理";
- Flink CEP 是 Flink 中实现的复杂事件处理库;
- CEP 允许在无休止的事件流中检测事件模式,让我们有机会掌握数据中的重要部分;
- 一个或多个由简单事件构成的事件流通过一定的匹配规则,然后输出用户想要得到的数据;
二、量词 Quantifier
- start.times(6):匹配出现 6 次;
- start.times(6).optional:匹配出现 0 次或 6 次;
- start.times(3,6):匹配出现 3 ~ 6 次;
- start.times(3,6).greedy:匹配出现 3 ~ 6 次,并且尽可能多的匹配;
- start.oneOrMore():匹配 1 次或多次;
- start.timesOrMore(2).optional.greedy:匹配 0 次或 2 次或多次,且尽可能多的重复匹配
三、条件:where、or、until
- 简单条件:start.where(event => event.getName.startsWith("Liu"));
- 组合条件:pattern.where(event => ...).or(event => ...); pattern.where(e => ...).where(e => ...);
- 终止条件:如果使用了:oneOrMore 或者 oneOrMore.optional ,建议使用 .until 作为终止条件,清理状态;
- 迭代条件:能够对模式之前所有接收的事件进行处理,调用 .where((value,ctx) => {...}),可以调用 ctx.getEventsForPattern("name")
四、模式序列
- 严格近邻:紧挨着下一个发生的事件,由 .next() 指定,例如:对于事件 "a next b",序列 [a,c,b1,b2] 没有匹配;
- 宽松近邻:两个事件不必紧挨着,中间可以有不匹配事件,由 .followedBy() 指定,例如:对于事件 "a followedBy b",序列 [a,c,b1,b2] 匹配为 {a,b1};
- 非确定性宽松近邻:进一步放宽条件,之前已经匹配过的事件也可以再次使用,由 .followedByAny() 指定,例如:对于事件 "a followedByAny b" ,序列 [a,c,b1,b2] 匹配为 {a,b1},{a,b2}
- notNext():不想让某个事件紧邻前一个事件发生
- notFollowedBy():不想让某个事件在两个事件之间发生
五、注意事项
- 所有模式序列必须以 .begin() 开始;
- 模式序列不能以 notFollowedBy() 结束;
- "not" 类型的模式不能用 optional 修饰;
- 此外,还可以为模式指定时间约束:next().within(Time.seconds(6)) ,6 秒之内的事件
六、事件的提取
1.匹配事件的提取
...
...
//定义匹配模式
val pattern = Pattern.begin[LoginLog]("begin").where(_.loginType == "fail")
.next("next").where(_.loginType == "fail")
.within(Time.seconds(2))
//CEP将匹配模式代入数据流,得到匹配好的数据
val patternStream = CEP.pattern(sourceData, pattern)
val loginFailData: DataStream[LoginFailView] = patternStream.select(new LogFailSelectFunciton())
...
...
class LogFailSelectFunciton() extends PatternSelectFunction[LoginLog, LoginFailView] {
override def select(map: util.Map[String, util.List[LoginLog]]): LoginFailView = {
val begin = map.get("begin").iterator().next()
val next = map.get("next").iterator().next()
LoginFailView(begin.userId, "搞事情!!!")
}
}
select 的参数:Map[String, util.List[LoginLog]] ,其中 key (String) 就是每个模式的名称,value 就是接收到事件的 Iterable 类型
2.超时事件的获取
当一个模式通过 within() 方法指定了窗口检测时间时,部分事件序列可能因为超过窗口长度而无法被丢弃,为了能够处理这些超时的部分匹配,select 和 flatSelect API 调用允许指定超时处理程序;
超时处理程序会接收到目前为止由模式匹配到的所有事件,由 OutputTag 定义接收到的超时事件序列。
例:实时监控订单超时,15分钟之内未支付则视为超时
...
...
val pattern = Pattern.begin[OrderLog]("begin")
.where(_.orderType == "create")
.followedBy("follow")
.where(_.orderType == "pay")
.within(Time.minutes(15))
val patternDataStream = CEP.pattern(resourceData, pattern)
val outputTag = new OutputTag[OrderResult]("order-timeout")
val orderResultDataStream = patternDataStream.select(outputTag,
new OrderTimeoutFunction(),
new OrderPatternFunction())
orderResultDataStream.getSideOutput(outputTag).print("timeout")
orderResultDataStream.print("pay")
...
...
class OrderTimeoutFunction() extends PatternTimeoutFunction[OrderLog, OrderResult] {
override def timeout(map: util.Map[String, util.List[OrderLog]], l: Long): OrderResult = {
val begin = map.get("begin").iterator().next()
OrderResult(begin.orderId, "已超时!!!")
}
}
class OrderPatternFunction() extends PatternSelectFunction[OrderLog, OrderResult] {
override def select(map: util.Map[String, util.List[OrderLog]]): OrderResult = {
val begin = map.get("begin").iterator().next()
val end = map.get("follow").iterator().next()
OrderResult(begin.orderId, end.orderType)
}
}