cep是flink为了从流中提取具有某种特征的复杂事件,所提供的一个包,这两天研究了一下,写个非常有意思的小例子来学习一下。
@Test
public void testEventTimeWithWindow() throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(1);
// f0是名字,f1是事件时间
DataStream<Tuple2<String, Long>> input = env.fromElements(
Tuple2.of("alice", 0L),
Tuple2.of("bob", 2L),
Tuple2.of("tom", 10L),
Tuple2.of("luna", 4L),
Tuple2.of("luck", 15L),
Tuple2.of("rice", 50L));
// 设置事件时间和watermark
DataStream<String> inputWithEventTime = input.assignTimestampsAndWatermarks(
new AssignerWithPunctuatedWatermarks<Tuple2<String, Long>>() {
@Override
public long extractTimestamp(
Tuple2<String, Long> element,
long recordTimestamp) {
return element.f1;
}
// watermark设置为5ms
@Override
public Watermark checkAndGetNextWatermark(
Tuple2<String, Long> lastElement,
long extractedTimestamp) {
return new Watermark(lastElement.f1 - 5);
}
}
)
.map((MapFunction<Tuple2<String, Long>, String>) value -> value.f0);
// 设置cep复杂事件模式,我们想找到5ms之内连续发生的事件
Pattern pattern = Pattern
.begin("first")
.followedByAny("second")
.within(Time.milliseconds(5));
// 在流中使用pattern进行发现
DataStream<String> result = CEP.pattern(inputWithEventTime, pattern)
.inEventTime()
.select((PatternSelectFunction<Tuple2<String, Long>, String>) pattern1 ->
String.format("first element: %s, second element: %s",
pattern1.get("first").get(0), pattern1.get("second").get(0))
);
result.print();
env.execute();
}
如上面的代码所示,事件时间的顺序是:
name | event time |
---|---|
alice | 0ms |
bob | 2ms |
luna | 4ms |
tom | 10ms |
luck | 15ms |
rice | 50ms |
因为上面的代码可以找出5ms内连续发生的事件,所以我一开始以为上面的代码的输出会是:
first element: alice, second element: bob
first element: bob, second element: luna
然而结果却是:
first element: alice, second element: bob
这是为啥呢?
后来发现,这是因为设置了5ms的watermark,因为流中元素(10ms,tom)来到之后,watermark就已经被推进到了
当
前
事
件
事
件
(
10
m
s
)
−
等
待
时
间
(
5
m
s
)
=
w
a
t
e
r
m
a
r
k
(
5
m
s
)
当前事件事件(10ms)-等待时间(5ms)=watermark(5ms)
当前事件事件(10ms)−等待时间(5ms)=watermark(5ms)
因此当前watermark已经被推进到了5ms,所有当元素(4ms, luna)来了之后,不会触发事件识别。
为了验证我的想法,我把input改为:
DataStream<Tuple2<String, Long>> input = env.fromElements(
Tuple2.of("alice", 0L),
Tuple2.of("bob", 2L),
Tuple2.of("tom", 10L),
Tuple2.of("luna", 6L),
Tuple2.of("luck", 15L),
Tuple2.of("rice", 50L));
将luna这一条事件事件改为6ms,比5ms的watermark要迟,因此还能够触发cep识别,这时候输出的结果就变成了:
first element: alice, second element: bob
first element: bob, second element: luna
first element: luna, second element: tom