FlinkCEP
1.CEP
CEP全称 Complex event processing 复杂事件处理
FlinkCEP 是在 Flink 之上实现的复杂事件处理(CEP)库
擅长高吞吐、低延迟的处理,市场上有多种CEP的解决方案,例如Spark,但是Flink专门类库更方便使用
官网链接:https://ci.apache.org/projects/flink/flink-docs-release-1.13/docs/libs/cep/
2.应用场景
检测和发现无边界事件流中多个记录的关联规则,得到满足规则的复杂事件
允许业务定义要从输入流中提取的复杂模式序列
3.使用流程
- 定义pattern
- pattern应用到数据流,得到模式流
- 从模式流 获取结果
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并不包含在flink中,使用前需要自己导入
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-cep_${scala.version}</artifactId>
<version>${flink.version}</version>
</dependency>
4.模式(Pattern):定义处理事件的规则
三种模式PatternAPI
- 个体模式(Individual Patterns)
组成复杂规则的每一个单独的模式定义 - 组合模式(Combining Patterns)
很多个体模式组合起来,形成组合模式 - 模式组(Groups of Patterns)
将一个组合模式作为条件嵌套在个体模式里
近邻模式
- 严格近邻
期望所有匹配事件严格地一个接一个出现,中间没有任何不匹配的事件, API是.next() - 宽松近邻
允许中间出现不匹配的事件,API是.followedBy()
- 非确定性宽松近邻
可以忽略已经匹配的条件,API是followedByAny() - 指定时间约束
指定模式在多长时间内匹配有效,API是within - 如果您不希望事件类型直接跟随另一个,notNext()
- 如果您不希望事件类型介于其他两种事件类型之间,notFollowedBy()
模式分类
- 单次模式
接收一次一个事件 - 循环模式
接收一个或多个事件
其他参数
times:指定固定的循环执行次数
greedy:贪婪模式,尽可能多触发
oneOrMore:指定触发一次或多次
timesOrMore:指定触发固定以上的次数
optional:要么不触发要么触发指定的次数
5.代码示例:
需求:
同个账号,在5秒内连续登录失败2次,则认为存在而已登录问题
数据格式 jack,2021-12-23 10:00:01,-2
public static void main(String[] args) throws Exception {
//构建执行任务环境以及任务的启动的入口, 存储全局相关的参数
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(1);
DataStream<String> ds = env.socketTextStream("127.0.0.1", 8888);
DataStream<Tuple3<String, String, Integer>> flatMapDS = ds.flatMap(new FlatMapFunction<String, Tuple3<String, String, Integer>>() {
@Override
public void flatMap(String value, Collector<Tuple3<String, String, Integer>> out) throws Exception {
String[] arr = value.split(",");
out.collect(Tuple3.of(arr[0], arr[1], Integer.parseInt(arr[2])));
}
});
SingleOutputStreamOperator<Tuple3<String, String, Integer>> watermakerDS = flatMapDS.assignTimestampsAndWatermarks(WatermarkStrategy
//延迟策略去掉了延迟时间,时间是单调递增,event中的时间戳充当了水印
.<Tuple3<String, String, Integer>>forMonotonousTimestamps()
//生成一个延迟3s的固定水印
//.<Tuple3<String, String, Integer>>forBoundedOutOfOrderness(Duration.ofSeconds(3))
.withTimestampAssigner(
(event, timestamp) -> {
//指定POJO的事件时间列
return TimeUtil.strToDate(event.f1).getTime();
}
));
KeyedStream<Tuple3<String, String, Integer>, String> keyedStream = watermakerDS.keyBy(new KeySelector<Tuple3<String, String, Integer>, String>() {
@Override
public String getKey(Tuple3<String, String, Integer> value) throws Exception {
return value.f0;
}
});
//定义模式
Pattern<Tuple3<String, String, Integer>, Tuple3<String, String, Integer>> pattern = Pattern
.<Tuple3<String, String, Integer>>begin("firstTimeLogin")
.where(new SimpleCondition<Tuple3<String, String, Integer>>() {
@Override
public boolean filter(Tuple3<String, String, Integer> value) throws Exception {
// -2 是登录失败错误码
return value.f2 == -2;
}
})//.times(2).within(Time.seconds(10));//不是严格近邻
.next("secondTimeLogin")
.where(new SimpleCondition<Tuple3<String, String, Integer>>() {
@Override
public boolean filter(Tuple3<String, String, Integer> value) throws Exception {
return value.f2 == -2;
}
}).within(Time.seconds(5));
//匹配检查
PatternStream<Tuple3<String, String, Integer>> patternStream = CEP.pattern(keyedStream, pattern);
SingleOutputStreamOperator<Tuple3<String, String,String>> select = patternStream.select(new PatternSelectFunction<Tuple3<String, String, Integer>, Tuple3<String, String,String>>() {
@Override
public Tuple3<String, String,String> select(Map<String, List<Tuple3<String, String, Integer>>> map) throws Exception {
Tuple3<String, String, Integer> firstLoginFail = map.get("firstTimeLogin").get(0);
Tuple3<String, String, Integer> secondLoginFail = map.get("secondTimeLogin").get(0);
return Tuple3.of(firstLoginFail.f0,firstLoginFail.f1,secondLoginFail.f1);
}
});
select.print("匹配结果");
env.execute("CEP job");
}