文章目录
- FlinkCEP——Flink的复杂事件处理
- FlinkCEP 简单流程
- Pattern API
- 个体模式(Individual Patterns)
- 量词
- 条件
- 模式组(Groups of Patterns)
- 近邻
- 引入
FlinkCEP——Flink的复杂事件处理
FlinkCEP是在Flink之上实现的复杂事件处理 (CEP)库。它使您可以检查无穷无尽的事件流的事件模型,从而使您有机会掌握数据中的重要信息
FlinkCEP 简单流程
DataStream<Event> input = ...
Pattern<Event, ?> pattern = Pattern.<Event>begin("start").where(
new SimpleCondition<Event>() {
@Override
public boolean filter(Event event) {
return event.getId() == 42;
}
}
).next("middle").subtype(SubEvent.class).where(
new SimpleCondition<SubEvent>() {
@Override
public boolean filter(SubEvent subEvent) {
return subEvent.getVolume() >= 10.0;
}
}
).followedBy("end").where(
new SimpleCondition<Event>() {
@Override
public boolean filter(Event event) {
return event.getName().equals("end");
}
}
);
PatternStream<Event> patternStream = CEP.pattern(input, pattern);
DataStream<Alert> result = patternStream.process(
new PatternProcessFunction<Event, Alert>() {
@Override
public void processMatch(
Map<String, List<Event>> pattern,
Context ctx,
Collector<Alert> out) throws Exception {
out.collect(createAlertFrom(pattern));
}
});
检测和发现无界事件流中多个记录的关联规则,也就是从无界事件流中得到满足规则的复杂事件。
CEP(Complex Event Processing)就是在无界事件流中检测事件模式,让我们掌握数据中重要的部分。flink CEP是在flink中实现的复杂事件处理库。
目标:从简单事件流中发现一些高阶特征
输入:一个或者多个简单事件构成的事件流
处理:检测简单事件之间的联系,多个事件组合一起符合匹配规则,将该多个事件构成复杂事件
输出:符合规则的复杂事件
Pattern API
我们先来认识一下CEP中Pattern模式,也就是规则的制定
val start: Pattern[X, X] = Pattern.begin[X]("start")
*Pattern根据模式的组合种类,分为了三种*
个体模式(Individual Patterns)
组成复杂规则的每一个单独的模式定义,就是“个体模式”
start.where(condition: F => Boolean)
量词
个体模式根据接收同一种事件的数量又可以分为“单例模式”和“循环模式“,我们通过一个“量词”来指定接受同一种事件的数量。
start.times(2) // 必须2次
start.times(2, 5) // 2,3,4或者5次都可以
start.times(2, 5).greedy // 2,3,4或者5次,并且尽可能的重复匹配
start.times(2).optional // 0或者2次
start.oneOrMore // 1次或者多次
start.timesOrMore(2).optional.greedy // 0, 2或者多次,并且尽可能的重复匹配
start.oneOrMore().greedy(); // 期望出现1次或更多次,并重复尽可能多的次数
start.oneOrMore().optional(); // 期望出现0次或更多次
start.timesOrMore(2); // 期望出现2次或更多次
start.timesOrMore(2).greedy(); // 期望出现2次或更多的事件,并重复尽可能多的事件
start.timesOrMore(2).optional() // 期望出现0次、2次或更多次
start.timesOrMore(2).optional().greedy(); //期望出现0次、2次或更多次,并重复尽可能多的次
条件
个体模式的条件,可以在一个个体模式上判断使用多个条件,只有当条件都满足的情况下才算匹配成功
// 组合条件 (参考sql中的 where or)
.where() // 条件相连为and
.or() // 条件相连为or
// 终止条件
.until() // 当使用了oneOrMore或者oneOrMore.optional时需要进行终止,以便清楚状态
// 迭代条件
.where(condition: (F, Context[F]) => Boolean) // 调用上下文对前边接收的事件进行处理
// ctx.getEventsForPattern("start")
组合模式(Combining Patterns)
多个个体模式组合起来就形成了一个组合模式
Pattern.begin[X]("start").where(condition: F => Boolean)
.next("middle").where(condition: F => Boolean)
注意:组合模式必须由一个“开始个体模式”开始
val start: Pattern[X, X] = Pattern.begin[X]("start")
模式组(Groups of Patterns)
将一个组合模式作为条件嵌套在个体模式里,成为一组模式
近邻
当我们在对事件流进行复杂事件处理,有时我们需要两个事件在流中必须相连,有时只要两个事件出现在流中不必须相连中间可以间隔一个其他事件。也就是我们对多个事件组成规则严格性的宽容度,那么如何来表达这个容忍度呢?使用近邻。
严格近邻:所有事件严格按照顺序进行,中间没有任何不匹配的事件。使用next()指定
对于模式"a next b",事件流[a, c, b, d],没有匹配
**宽松近邻:**允许中间出现不匹配事件。使用followedBy()指定
对于模式"a followedBy b",事件流[a, c, b, d],匹配为 [a, b]
对于模式"a followedByAny b",事件流[a, c, b1, b2],匹配为 [a, b1],[a, b2]
此外,还可以为模式指定时间约束,用来要求在多长时间内匹配有效
.within( Time.seconds( 5 ) ); // 五秒钟内匹配有效
引入
1 CEP并不包含在flink中,使用前需要自己导入cep jar包
<!-- 使用cep需要引入该jar包,
scala语言编写要导入 artifactId前缀为 flink-cep-scala -->
<!-- https://mvnrepository.com/artifact/org.apache.flink/flink-cep -->
<dependencies>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-cep_${scala.binary.version}</artifactId>
<version>${flink.version}</version>
</dependency>
</dependencies>
2 一组数据
34729,create,,1558430842
34730,create,,1558430843
34729,pay,sd76f87d6,1558430844
34730,pay,3hu3k2432,1558430845
34731,create,,1558430846
34731,pay,35jue34we,1558430849
34732,create,,1558430852
34733,create,,1558430855
34734,create,,1558430859
34732,pay,32h3h4b4t,1558430861
34735,create,,1558430862
34734,pay,435kjb45d,1558430863
34733,pay,766lk5nk4,1558430864
34736,create,,1558430866
34737,create,,1558430868
34735,pay,5k432k4n,1558430869
34738,create,,1558430871
34739,create,,1558430874
34736,pay,435kjb45s,1558430875
34740,create,,1558430877
3 定义Pattern模式且必须要以Pattern.begin(“xxx”)开始,"xxx"为别名,在后边PatternStream.select()中获取符合模式的数据时会使用到。
例如:
val pattern = Pattern.begin[T]("start").where(...).next("middle").where(...)
总体引入
package huan;
import com.huan.beans.OrderEvent;
import com.huan.beans.OrderResult;
import org.apache.flink.cep.CEP;
import org.apache.flink.cep.PatternSelectFunction;
import org.apache.flink.cep.PatternStream;
import org.apache.flink.cep.PatternTimeoutFunction;
import org.apache.flink.cep.pattern.Pattern;
import org.apache.flink.cep.pattern.conditions.SimpleCondition;
import org.apache.flink.streaming.api.TimeCharacteristic;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.datastream.KeyedStream;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.timestamps.AscendingTimestampExtractor;
import org.apache.flink.streaming.api.windowing.time.Time;
import org.apache.flink.util.OutputTag;
import java.util.List;
import java.util.Map;
public class FlinkCEP {
public static void main(String[] args) throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism( 1 );
env.setStreamTimeCharacteristic( TimeCharacteristic.EventTime );
// 读取数据并转换成POJO类型
String filePath = "E:\\Project\\UserBehaviorAnalysis_Java\\OrderTimeoutDetect\\src\\test\\Login.csv";
DataStream<OrderEvent> orderEventStream = env.readTextFile( filePath )
.map( line -> {
String[] fields = line.split( "," );
return new OrderEvent( new Long( fields[0] ), fields[1], fields[2], new Long( fields[3] ) );
} )
// 升序时间戳
.assignTimestampsAndWatermarks( new AscendingTimestampExtractor<OrderEvent>() {
@Override
public long extractAscendingTimestamp(OrderEvent element) {
return element.getTimestamp() * 1000L;
}
} );
//1.定义一个带时间限制的模式
Pattern<OrderEvent, OrderEvent> start = Pattern.<OrderEvent>begin( "start" ).where( new SimpleCondition<OrderEvent>() {
@Override
public boolean filter(OrderEvent value) throws Exception {
return "create".equals( value.getEventType() );
}
} ).followedBy( "end" ).where( new SimpleCondition<OrderEvent>() {
@Override
public boolean filter(OrderEvent value) throws Exception {
return "pay".equals( value.getEventType() );
}
} ).within( Time.seconds( 5 ) );
//2.定义侧输出流标签,用来标示超时事件
OutputTag<OrderResult> orderTimeoutTag = new OutputTag<OrderResult>( "order-timeout" ) {
};
KeyedStream<OrderEvent, Long> keyedStream = orderEventStream.keyBy( OrderEvent::getOrderId );
//3.将pattern 应用到输入数据流 得到pattern stream
PatternStream<OrderEvent> pattern = CEP.pattern( keyedStream, start );
//4.调用select方法,实现对匹配复杂事件和超时复杂事件的提取和处理
SingleOutputStreamOperator<OrderResult> resultStream = pattern.select( orderTimeoutTag, new OrderTimeoutSelect1(), new OrderPaySelect1() );
resultStream.print("payed normally ");
resultStream.getSideOutput( orderTimeoutTag ).print("timeOUT");
env.execute( "FlinkCEP" );
}
//实现自定义的超时事件处理函数
public static class OrderTimeoutSelect1 implements PatternTimeoutFunction<OrderEvent,OrderResult> {
@Override
public OrderResult timeout(Map<String, List<OrderEvent>> pattern, long timeoutTimestamp) throws Exception {
Long timeoutOrderId = pattern.get( "start" ).iterator().next().getOrderId();
return new OrderResult( timeoutOrderId,"timeout"+ timeoutTimestamp );
}
}
//实现自定义的正常匹配事件处理函数
public static class OrderPaySelect1 implements PatternSelectFunction<OrderEvent,OrderResult> {
@Override
public OrderResult select(Map<String, List<OrderEvent>> pattern) throws Exception {
Long payedOrderId = pattern.get( "end" ).iterator().next().getOrderId();
return new OrderResult( payedOrderId,"payed" );
}
}
}