文章目錄
本文主要針對 Flink 源碼中 flink-cep
模塊,帶大家跑一下其中的測試類 CEPITCase.java
,可以直觀的瞭解到 flink cep 的功能。
爲了方便分析,對源碼中的示例會有些許改動,並把代碼粘貼在了文中。
1.testSimplePatternCEP
public class CEPITCase extends AbstractTestBase{
/**
* Checks that a certain event sequence is recognized.
* 全局匹配一組先後發生的事件序列
*/
@Test
public void testSimplePatternCEP() throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
DataStream<Event> input = env.fromElements(
new Event(1, "barfoo", 1.0),
new Event(2, "start", 2.0),
new Event(3, "foobar", 3.0),
new SubEvent(4, "foo", 4.0, 1.0),
new Event(5, "middle", 5.0),
new SubEvent(6, "middle", 6.0, 2.0),
new SubEvent(7, "bar", 3.0, 3.0),
new Event(42, "42", 42.0),
new Event(8, "end", 1.0)
);
// Pattern 組
Pattern<Event, ?> pattern = Pattern.<Event>begin("start").where(new SimpleCondition<Event>() {
@Override
public boolean filter(Event value) throws Exception {
// start Pattern
System.out.println("testSimplePatternCEP start -> " + value + ", " + value.getName().equals("start"));
return value.getName().equals("start");
}
})
// 1. 前一個模式
// 2. 創建一個新的模式
// 3. 模式名
// 4. where 指定模式內容
.followedByAny("middle").subtype(SubEvent.class).where(
new SimpleCondition<SubEvent>() {
@Override
public boolean filter(SubEvent value) throws Exception {
// middle Pattern
// 5. 核心處理邏輯
System.out.println("testSimplePatternCEP middle -> " + value + ", " + value.getName().equals(
"middle"));
return value.getName().equals("middle");
}
}
)
.followedByAny("end").where(new SimpleCondition<Event>() {
@Override
public boolean filter(Event value) throws Exception {
// end Pattern
System.out.println("testSimplePatternCEP end -> " + value + ", " + value.getName().equals("end"));
return value.getName().equals("end");
}
});
// CEP 匹配
PatternStream<Event> patternStream = CEP.pattern(input, pattern);
// 經過 CEP 匹配之後的輸出流
DataStream<String> result = patternStream.flatSelect((p, o) -> {
StringBuilder builder = new StringBuilder();
builder.append(p.get("start").get(0).getId()).append(",")
.append(p.get("middle").get(0).getId()).append(",")
.append(p.get("end").get(0).getId());
o.collect(builder.toString());
System.out.println("testSimplePatternCEP -> " + builder.toString());
}, Types.STRING);
List<String> resultList = new ArrayList<>();
DataStreamUtils.collect(result).forEachRemaining(resultList::add);
// 根據設置的 Pattern,返回 id 列表 2,6,8
assertEquals(Arrays.asList("2,6,8"), resultList);
}
}
輸出結果,匹配出 <2,6,8> 序列:
testSimplePatternCEP start -> Event(1, barfoo, 1.0), false
testSimplePatternCEP start -> Event(2, start, 2.0), true
testSimplePatternCEP start -> Event(3, foobar, 3.0), false
testSimplePatternCEP middle -> SubEvent(4, foo, 4.0, 1.0), false
testSimplePatternCEP start -> SubEvent(4, foo, 4.0, 1.0), false
testSimplePatternCEP start -> Event(5, middle, 5.0), false
testSimplePatternCEP middle -> SubEvent(6, middle, 6.0, 2.0), true
testSimplePatternCEP start -> SubEvent(6, middle, 6.0, 2.0), false
testSimplePatternCEP end -> SubEvent(7, bar, 3.0, 3.0), false
testSimplePatternCEP middle -> SubEvent(7, bar, 3.0, 3.0), false
testSimplePatternCEP start -> SubEvent(7, bar, 3.0, 3.0), false
testSimplePatternCEP end -> Event(42, 42, 42.0), false
testSimplePatternCEP start -> Event(42, 42, 42.0), false
testSimplePatternCEP end -> Event(8, end, 1.0), true
testSimplePatternCEP start -> Event(8, end, 1.0), false
testSimplePatternCEP -> 2,6,8
2.testSimpleKeyedPatternCEP
public class CEPITCase extends AbstractTestBase {
/**
* 先按照 id 分組,再去匹配各自的 Pattern
*/
@Test
public void testSimpleKeyedPatternCEP() throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(2);
DataStream<Event> input = env.fromElements(
new Event(1, "barfoo", 1.0),
new Event(2, "start", 2.0),
new Event(3, "start", 2.1),
new Event(3, "foobar", 3.0),
new SubEvent(4, "foo", 4.0, 1.0),
new SubEvent(3, "middle", 3.2, 1.0),
new Event(42, "start", 3.1),
new SubEvent(42, "middle", 3.3, 1.2),
new Event(5, "middle", 5.0),
new SubEvent(2, "middle", 6.0, 2.0),
new SubEvent(7, "bar", 3.0, 3.0),
new Event(42, "42", 42.0),
new Event(3, "end", 2.0),
new Event(2, "end", 1.0),
new Event(42, "end", 42.0)
).keyBy(new KeySelector<Event, Integer>() {
@Override
public Integer getKey(Event value) throws Exception {
return value.getId();
}
});
Pattern<Event, ?> pattern = Pattern.<Event>begin("start").where(new SimpleCondition<Event>() {
@Override
public boolean filter(Event value) throws Exception {
System.out.println("testSimpleKeyedPatternCEP start -> " + value + ", " + value.getName().equals(
"start"));
return value.getName().equals("start");
}
})
.followedByAny("middle").subtype(SubEvent.class).where(
new SimpleCondition<SubEvent>() {
@Override
public boolean filter(SubEvent value) throws Exception {
System.out.println("testSimpleKeyedPatternCEP middle -> " + value + ", " + value.getName().equals("middle"));
return value.getName().equals("middle");
}
}
)
.followedByAny("end").where(new SimpleCondition<Event>() {
@Override
public boolean filter(Event value) throws Exception {
System.out.println("testSimpleKeyedPatternCEP end -> " + value + ", " + value.getName().equals(
"end"));
return value.getName().equals("end");
}
});
// 處理同一個 input 中的不同 event
DataStream<String> result = CEP.pattern(input, pattern).select(p -> {
StringBuilder builder = new StringBuilder();
builder.append(p.get("start").get(0).getId()).append(",")
.append(p.get("middle").get(0).getId()).append(",")
.append(p.get("end").get(0).getId());
System.out.println("testSimpleKeyedPatternCEP -> " + builder.toString());
return builder.toString();
});
List<String> resultList = new ArrayList<>();
DataStreamUtils.collect(result).forEachRemaining(resultList::add);
resultList.sort(String::compareTo);
// 先按照 key 分組形成 KeyedStream,再去匹配各自的 Pattern
assertEquals(Arrays.asList("2,2,2", "3,3,3", "42,42,42"), resultList);
}
}
輸出結果,分別按 key 劃分匹配出 <2,2,2>,< 3,3,3>,<42,42,42> 序列:
testSimpleKeyedPatternCEP start -> Event(1, barfoo, 1.0), false
testSimpleKeyedPatternCEP start -> SubEvent(4, foo, 4.0, 1.0), false
testSimpleKeyedPatternCEP start -> Event(2, start, 2.0), true
testSimpleKeyedPatternCEP start -> Event(42, start, 3.1), true
testSimpleKeyedPatternCEP start -> Event(3, start, 2.1), true
testSimpleKeyedPatternCEP middle -> SubEvent(42, middle, 3.3, 1.2), true
testSimpleKeyedPatternCEP start -> SubEvent(42, middle, 3.3, 1.2), false
testSimpleKeyedPatternCEP start -> Event(3, foobar, 3.0), false
testSimpleKeyedPatternCEP end -> Event(42, 42, 42.0), false
testSimpleKeyedPatternCEP start -> Event(42, 42, 42.0), false
testSimpleKeyedPatternCEP end -> Event(42, end, 42.0), true
testSimpleKeyedPatternCEP start -> Event(42, end, 42.0), false
testSimpleKeyedPatternCEP middle -> SubEvent(3, middle, 3.2, 1.0), true
testSimpleKeyedPatternCEP start -> SubEvent(3, middle, 3.2, 1.0), false
42,42,42
testSimpleKeyedPatternCEP start -> Event(5, middle, 5.0), false
testSimpleKeyedPatternCEP middle -> SubEvent(2, middle, 6.0, 2.0), true
testSimpleKeyedPatternCEP start -> SubEvent(2, middle, 6.0, 2.0), false
testSimpleKeyedPatternCEP start -> SubEvent(7, bar, 3.0, 3.0), false
testSimpleKeyedPatternCEP end -> Event(3, end, 2.0), true
testSimpleKeyedPatternCEP start -> Event(3, end, 2.0), false
3,3,3
testSimpleKeyedPatternCEP end -> Event(2, end, 1.0), true
testSimpleKeyedPatternCEP start -> Event(2, end, 1.0), false
2,2,2
爲了方便分析,我們把相同的 key 放到一起看:
testSimpleKeyedPatternCEP start -> Event(1, barfoo, 1.0), false
testSimpleKeyedPatternCEP start -> SubEvent(4, foo, 4.0, 1.0), false
testSimpleKeyedPatternCEP start -> Event(2, start, 2.0), true
testSimpleKeyedPatternCEP middle -> SubEvent(2, middle, 6.0, 2.0), true
testSimpleKeyedPatternCEP start -> SubEvent(2, middle, 6.0, 2.0), false
testSimpleKeyedPatternCEP end -> Event(2, end, 1.0), true
testSimpleKeyedPatternCEP start -> Event(2, end, 1.0), false
2,2,2
testSimpleKeyedPatternCEP start -> Event(42, start, 3.1), true
testSimpleKeyedPatternCEP middle -> SubEvent(42, middle, 3.3, 1.2), true
testSimpleKeyedPatternCEP start -> SubEvent(42, middle, 3.3, 1.2), false
testSimpleKeyedPatternCEP end -> Event(42, 42, 42.0), false
testSimpleKeyedPatternCEP start -> Event(42, 42, 42.0), false
testSimpleKeyedPatternCEP end -> Event(42, end, 42.0), true
testSimpleKeyedPatternCEP start -> Event(42, end, 42.0), false
42,42,42
testSimpleKeyedPatternCEP start -> Event(3, start, 2.1), true
testSimpleKeyedPatternCEP start -> Event(3, foobar, 3.0), false
testSimpleKeyedPatternCEP middle -> SubEvent(3, middle, 3.2, 1.0), true
testSimpleKeyedPatternCEP start -> SubEvent(3, middle, 3.2, 1.0), false
testSimpleKeyedPatternCEP end -> Event(3, end, 2.0), true
testSimpleKeyedPatternCEP start -> Event(3, end, 2.0), false
3,3,3
testSimpleKeyedPatternCEP start -> Event(5, middle, 5.0), false
testSimpleKeyedPatternCEP start -> SubEvent(7, bar, 3.0, 3.0), false
3.testSimplePatternEventTime
public class CEPITCase extends AbstractTestBase {
/**
* 全局匹配一組先後發生的事件序列,基於 EventTime 排序
*/
@Test
public void testSimplePatternEventTime() throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime);
// Tuple2 模擬事件以及事件發生的時間
// (Event, timestamp)
// 事件匹配順序,2 -> 3 -> 1 -> 5 -> 4 -> 5
DataStream<Event> input = env.fromElements(
Tuple2.of(new Event(1, "start", 1.0), 5L),
Tuple2.of(new Event(2, "middle", 2.0), 1L),
Tuple2.of(new Event(3, "end", 3.0), 3L),
Tuple2.of(new Event(4, "end", 4.0), 10L),
Tuple2.of(new Event(5, "middle", 5.0), 7L),
// last element for high final watermark
Tuple2.of(new Event(5, "middle", 5.0), 100L)
).assignTimestampsAndWatermarks(new AssignerWithPunctuatedWatermarks<Tuple2<Event, Long>>() {
@Override
public long extractTimestamp(Tuple2<Event, Long> element, long previousTimestamp) {
// 抽取出時間戳
System.out.print("Extract timestamp -> " + element.f1 + ", previousTimestamp -> " + previousTimestamp
+ ", ");
return element.f1;
}
@Override
public Watermark checkAndGetNextWatermark(Tuple2<Event, Long> lastElement, long extractedTimestamp) {
// 發射出新的 watermark
System.out.println("Emit watermark -> " + (lastElement.f1 - 5));
return new Watermark(lastElement.f1 - 5);
}
}).map(new MapFunction<Tuple2<Event, Long>, Event>() {
@Override
public Event map(Tuple2<Event, Long> value) throws Exception {
return value.f0;
}
});
Pattern<Event, ?> pattern = Pattern.<Event>begin("start").where(new SimpleCondition<Event>() {
@Override
public boolean filter(Event value) throws Exception {
System.out.println("testSimplePatternEventTime start -> " + value + ", " + value.getName().equals(
"start"));
return value.getName().equals("start");
}
}).followedByAny("middle").where(new SimpleCondition<Event>() {
@Override
public boolean filter(Event value) throws Exception {
System.out.println("testSimplePatternEventTime middle -> " + value + ", " + value.getName().equals("middle"));
return value.getName().equals("middle");
}
}).followedByAny("end").where(new SimpleCondition<Event>() {
@Override
public boolean filter(Event value) throws Exception {
System.out.println("testSimplePatternEventTime end -> " + value + ", " + value.getName().equals("end"));
return value.getName().equals("end");
}
});
DataStream<String> result = CEP.pattern(input, pattern).select(
new PatternSelectFunction<Event, String>() {
@Override
public String select(Map<String, List<Event>> pattern) {
StringBuilder builder = new StringBuilder();
builder.append(pattern.get("start").get(0).getId()).append(",")
.append(pattern.get("middle").get(0).getId()).append(",")
.append(pattern.get("end").get(0).getId());
System.out.println("testSimplePatternEventTime -> " + builder.toString());
return builder.toString();
}
}
);
List<String> resultList = new ArrayList<>();
DataStreamUtils.collect(result).forEachRemaining(resultList::add);
resultList.sort(String::compareTo);
assertEquals(Arrays.asList("1,5,4"), resultList);
}
}
輸出結果,匹配出 <1,5,4> 序列:
Extract timestamp -> 5, previousTimestamp -> -9223372036854775808, Emit watermark -> 0
Extract timestamp -> 1, previousTimestamp -> -9223372036854775808, Emit watermark -> -4
Extract timestamp -> 3, previousTimestamp -> -9223372036854775808, Emit watermark -> -2
Extract timestamp -> 10, previousTimestamp -> -9223372036854775808, Emit watermark -> 5
Extract timestamp -> 7, previousTimestamp -> -9223372036854775808, Emit watermark -> 2
Extract timestamp -> 100, previousTimestamp -> -9223372036854775808, Emit watermark -> 95
testSimplePatternEventTime start -> Event(2, middle, 2.0), false
testSimplePatternEventTime start -> Event(3, end, 3.0), false
testSimplePatternEventTime start -> Event(1, start, 1.0), true
testSimplePatternEventTime middle -> Event(5, middle, 5.0), true
testSimplePatternEventTime start -> Event(5, middle, 5.0), false
testSimplePatternEventTime end -> Event(4, end, 4.0), true
testSimplePatternEventTime middle -> Event(4, end, 4.0), false
testSimplePatternEventTime start -> Event(4, end, 4.0), false
testSimplePatternEventTime -> 1,5,4
testSimplePatternEventTime end -> Event(5, middle, 5.0), false
testSimplePatternEventTime middle -> Event(5, middle, 5.0), true
testSimplePatternEventTime start -> Event(5, middle, 5.0), false
4.testSimpleKeyedPatternEventTime
public class CEPITCase extends AbstractTestBase {
/**
* 先按照 id 分組,再去匹配各自的 Pattern,基於 EventTime
*/
@Test
public void testSimpleKeyedPatternEventTime() throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime);
env.setParallelism(2);
// (Event, timestamp)
DataStream<Event> input = env.fromElements(
Tuple2.of(new Event(1, "start", 1.0), 5L),
Tuple2.of(new Event(1, "middle", 2.0), 1L),
Tuple2.of(new Event(2, "middle", 2.0), 4L),
Tuple2.of(new Event(2, "start", 2.0), 3L),
Tuple2.of(new Event(1, "end", 3.0), 3L),
Tuple2.of(new Event(3, "start", 4.1), 5L),
Tuple2.of(new Event(1, "end", 4.0), 10L),
Tuple2.of(new Event(2, "end", 2.0), 8L),
Tuple2.of(new Event(1, "middle", 5.0), 7L),
Tuple2.of(new Event(3, "middle", 6.0), 9L),
Tuple2.of(new Event(3, "end", 7.0), 7L)
).assignTimestampsAndWatermarks(new AssignerWithPunctuatedWatermarks<Tuple2<Event, Long>>() {
@Override
public long extractTimestamp(Tuple2<Event, Long> element, long currentTimestamp) {
return element.f1;
}
@Override
public Watermark checkAndGetNextWatermark(Tuple2<Event, Long> lastElement, long extractedTimestamp) {
return new Watermark(lastElement.f1 - 5);
}
}).map(new MapFunction<Tuple2<Event, Long>, Event>() {
@Override
public Event map(Tuple2<Event, Long> value) throws Exception {
return value.f0;
}
}).keyBy(new KeySelector<Event, Integer>() {
@Override
public Integer getKey(Event value) throws Exception {
return value.getId();
}
});
Pattern<Event, ?> pattern = Pattern.<Event>begin("start").where(new SimpleCondition<Event>() {
@Override
public boolean filter(Event value) throws Exception {
System.out.println("testSimpleKeyedPatternEventTime start -> " + value + ", " + value.getName().equals(
"start"));
return value.getName().equals("start");
}
}).followedByAny("middle").where(new SimpleCondition<Event>() {
@Override
public boolean filter(Event value) throws Exception {
System.out.println("testSimpleKeyedPatternEventTime middle -> " + value + ", " + value.getName().equals(
"middle"));
return value.getName().equals("middle");
}
}).followedByAny("end").where(new SimpleCondition<Event>() {
@Override
public boolean filter(Event value) throws Exception {
System.out.println("testSimpleKeyedPatternEventTime end -> " + value + ", " + value.getName().equals("end"));
return value.getName().equals("end");
}
});
DataStream<String> result = CEP.pattern(input, pattern).select(
new PatternSelectFunction<Event, String>() {
@Override
public String select(Map<String, List<Event>> pattern) {
StringBuilder builder = new StringBuilder();
builder.append(pattern.get("start").get(0).getId()).append(",")
.append(pattern.get("middle").get(0).getId()).append(",")
.append(pattern.get("end").get(0).getId());
System.out.println("testSimpleKeyedPatternEventTime -> " + builder.toString());
return builder.toString();
}
}
);
List<String> resultList = new ArrayList<>();
DataStreamUtils.collect(result).forEachRemaining(resultList::add);
resultList.sort(String::compareTo);
assertEquals(Arrays.asList("1,1,1", "2,2,2"), resultList);
}
}
輸出結果,分別按 key 劃分匹配出 <2,2,2>,<1,1,1> 序列:
testSimpleKeyedPatternEventTime start -> Event(3, start, 4.1), true
testSimpleKeyedPatternEventTime start -> Event(2, start, 2.0), true
testSimpleKeyedPatternEventTime middle -> Event(2, middle, 2.0), true
testSimpleKeyedPatternEventTime start -> Event(2, middle, 2.0), false
testSimpleKeyedPatternEventTime start -> Event(1, middle, 2.0), false
testSimpleKeyedPatternEventTime start -> Event(1, end, 3.0), false
testSimpleKeyedPatternEventTime start -> Event(1, start, 1.0), true
testSimpleKeyedPatternEventTime end -> Event(2, end, 2.0), true
testSimpleKeyedPatternEventTime middle -> Event(2, end, 2.0), false
testSimpleKeyedPatternEventTime start -> Event(2, end, 2.0), false
testSimpleKeyedPatternEventTime -> 2,2,2
testSimpleKeyedPatternEventTime middle -> Event(1, middle, 5.0), true
testSimpleKeyedPatternEventTime start -> Event(1, middle, 5.0), false
testSimpleKeyedPatternEventTime end -> Event(1, end, 4.0), true
testSimpleKeyedPatternEventTime middle -> Event(1, end, 4.0), false
testSimpleKeyedPatternEventTime start -> Event(1, end, 4.0), false
testSimpleKeyedPatternEventTime -> 1,1,1
testSimpleKeyedPatternEventTime middle -> Event(3, end, 7.0), false
testSimpleKeyedPatternEventTime start -> Event(3, end, 7.0), false
testSimpleKeyedPatternEventTime middle -> Event(3, middle, 6.0), true
testSimpleKeyedPatternEventTime start -> Event(3, middle, 6.0), false
5.testProcessingTimeWithWindow
public class CEPITCase extends AbstractTestBase {
/**
* 使用 ProcessingTime
* 獲取時間窗口中的開始事件和結束事件,1、2、3 3個元素依次累加
*/
@Test
public void testProcessingTimeWithWindow() throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(1);
DataStream<Integer> input = env.fromElements(1, 2, 3);
Pattern<Integer, ?> pattern = Pattern.<Integer>begin("start")
.followedByAny("end")
// 1天的窗口
.within(Time.days(1));
DataStream<Integer> result = CEP.pattern(input, pattern).select(new PatternSelectFunction<Integer, Integer>() {
@Override
public Integer select(Map<String, List<Integer>> pattern) throws Exception {
// 返回開始元素和結束元素的累加和
int start = pattern.get("start").get(0);
int end = pattern.get("end").get(0);
int sum = start + end;
System.out.println("testProcessingTimeWithWindow start -> " + start + ", end -> " + end + ", sum -> " + sum);
return sum;
}
});
List<Integer> resultList = new ArrayList<>();
DataStreamUtils.collect(result).forEachRemaining(resultList::add);
assertEquals(Arrays.asList(3, 4, 5), resultList);
}
}
輸出結果,element 兩兩累加的求和值爲 3,4,5:
testProcessingTimeWithWindow start -> 1, end -> 2, sum -> 3
testProcessingTimeWithWindow start -> 1, end -> 3, sum -> 4
testProcessingTimeWithWindow start -> 2, end -> 3, sum -> 5
6.testTimeoutHandling
public class CEPITCase extends AbstractTestBase {
/**
* 使用 EventTime
* 處理規則匹配超時的事件,旁路輸出
*/
@Test
public void testTimeoutHandling() throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(1);
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime);
// (Event, timestamp)
DataStream<Event> input = env.fromElements(
Tuple2.of(new Event(1, "start", 1.0), 1L),
Tuple2.of(new Event(1, "middle", 2.0), 5L),
Tuple2.of(new Event(1, "start", 3.0), 4L),
Tuple2.of(new Event(1, "end", 4.0), 6L)
).assignTimestampsAndWatermarks(new AssignerWithPunctuatedWatermarks<Tuple2<Event, Long>>() {
@Override
public long extractTimestamp(Tuple2<Event, Long> element, long currentTimestamp) {
return element.f1;
}
@Override
public Watermark checkAndGetNextWatermark(Tuple2<Event, Long> lastElement, long extractedTimestamp) {
return new Watermark(lastElement.f1 - 5);
}
}).map(new MapFunction<Tuple2<Event, Long>, Event>() {
@Override
public Event map(Tuple2<Event, Long> value) throws Exception {
return value.f0;
}
});
Pattern<Event, ?> pattern = Pattern.<Event>begin("start").where(new SimpleCondition<Event>() {
@Override
public boolean filter(Event value) throws Exception {
return value.getName().equals("start");
}
}).followedByAny("middle").where(new SimpleCondition<Event>() {
@Override
public boolean filter(Event value) throws Exception {
return value.getName().equals("middle");
}
}).followedByAny("end").where(new SimpleCondition<Event>() {
@Override
public boolean filter(Event value) throws Exception {
return value.getName().equals("end");
}
}).within(Time.milliseconds(3));
DataStream<Either<String, String>> result = CEP.pattern(input, pattern).select(
new PatternTimeoutFunction<Event, String>() {
// 超時事件旁路輸出
@Override
public String timeout(Map<String, List<Event>> pattern, long timeoutTimestamp) throws Exception {
System.out.println("testTimeoutHandling start timeout -> " + pattern.get("start").get(0) + ", " + timeoutTimestamp);
if (pattern.get("middle") != null) {
System.out.println("testTimeoutHandling middle timeout -> " + pattern.get("middle").get(0) +
", " + timeoutTimestamp);
}
return pattern.get("start").get(0).getPrice() + "";
}
},
new PatternSelectFunction<Event, String>() {
// 主流程正常匹配事件輸出
@Override
public String select(Map<String, List<Event>> pattern) {
StringBuilder builder = new StringBuilder();
builder.append(pattern.get("start").get(0).getPrice()).append(",")
.append(pattern.get("middle").get(0).getPrice()).append(",")
.append(pattern.get("end").get(0).getPrice());
System.out.println("testTimeoutHandling -> " + builder.toString());
return builder.toString();
}
}
);
List<Either<String, String>> resultList = new ArrayList<>();
DataStreamUtils.collect(result).forEachRemaining(resultList::add);
resultList.sort(Comparator.comparing(either -> either.toString()));
List<Either<String, String>> expected = Arrays.asList(
Either.Left.of("1.0"),
Either.Left.of("3.0"),
Either.Left.of("3.0"),
Either.Right.of("3.0,2.0,4.0")
);
assertEquals(expected, resultList);
}
}
輸出結果,匹配出 ❤️.0,2.0,4.0> 序列,超時事件旁路輸出:
testTimeoutHandling start timeout -> Event(1, start, 1.0), 4
testTimeoutHandling -> 3.0,2.0,4.0
testTimeoutHandling start timeout -> Event(1, start, 3.0), 7
testTimeoutHandling middle timeout -> Event(1, middle, 2.0), 7
testTimeoutHandling start timeout -> Event(1, start, 3.0), 7
7.testSimpleOrFilterPatternCEP
public class CEPITCase extends AbstractTestBase {
/**
* Checks that a certain event sequence is recognized with an OR filter.
* Pattern 中事件的過濾條件是可以用 or 的
*/
@Test
public void testSimpleOrFilterPatternCEP() throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
DataStream<Event> input = env.fromElements(
new Event(1, "start", 1.0),
new Event(2, "middle", 2.0),
new Event(3, "end", 3.0),
new Event(4, "start", 4.0),
new Event(5, "middle", 5.0),
new Event(6, "end", 6.0)
);
Pattern<Event, ?> pattern = Pattern.<Event>begin("start")
.where(new SimpleCondition<Event>() {
@Override
public boolean filter(Event value) throws Exception {
return value.getName().equals("start");
}
})
.followedByAny("middle")
.where(new SimpleCondition<Event>() {
@Override
public boolean filter(Event value) throws Exception {
return value.getPrice() == 2.0;
}
})
.or(new SimpleCondition<Event>() {
@Override
public boolean filter(Event value) throws Exception {
return value.getPrice() == 5.0;
}
})
.followedByAny("end").where(new SimpleCondition<Event>() {
@Override
public boolean filter(Event value) throws Exception {
return value.getName().equals("end");
}
});
DataStream<String> result = CEP.pattern(input, pattern).select(new PatternSelectFunction<Event, String>() {
@Override
public String select(Map<String, List<Event>> pattern) {
StringBuilder builder = new StringBuilder();
builder.append(pattern.get("start").get(0).getId()).append(",")
.append(pattern.get("middle").get(0).getId()).append(",")
.append(pattern.get("end").get(0).getId());
System.out.println("testSimpleOrFilterPatternCEP -> " + builder.toString());
return builder.toString();
}
});
List<String> resultList = new ArrayList<>();
DataStreamUtils.collect(result).forEachRemaining(resultList::add);
List<String> expected = Arrays.asList(
"1,5,6",
"1,2,3",
"4,5,6",
"1,2,6"
);
expected.sort(String::compareTo);
resultList.sort(String::compareTo);
assertEquals(expected, resultList);
}
}
輸出結果,匹配出 <1,2,3>,<1,2,6>,<1,5,6>,<4,5,6> 4個序列:
testSimpleOrFilterPatternCEP -> 1,2,3
testSimpleOrFilterPatternCEP -> 1,2,6
testSimpleOrFilterPatternCEP -> 1,5,6
testSimpleOrFilterPatternCEP -> 4,5,6
8.testSimplePatternEventTimeWithComparator
public class CEPITCase extends AbstractTestBase {
/**
* Checks that a certain event sequence is recognized.
* 先把事件按 EventTime 排序,再按自定義的 EventComparator 排序
*/
@Test
public void testSimplePatternEventTimeWithComparator() throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime);
// (Event, timestamp)
// 事件匹配順序,2 -> 3 -> 1 -> 6 -> 5 -> 4 -> 7
DataStream<Event> input = env.fromElements(
Tuple2.of(new Event(1, "start", 1.0), 5L),
Tuple2.of(new Event(2, "middle", 2.0), 1L),
Tuple2.of(new Event(3, "end", 3.0), 3L),
Tuple2.of(new Event(4, "end", 4.0), 10L),
Tuple2.of(new Event(5, "middle", 6.0), 7L),
Tuple2.of(new Event(6, "middle", 5.0), 7L),
// last element for high final watermark
Tuple2.of(new Event(7, "middle", 5.0), 100L)
).assignTimestampsAndWatermarks(new AssignerWithPunctuatedWatermarks<Tuple2<Event, Long>>() {
@Override
public long extractTimestamp(Tuple2<Event, Long> element, long previousTimestamp) {
return element.f1;
}
@Override
public Watermark checkAndGetNextWatermark(Tuple2<Event, Long> lastElement, long extractedTimestamp) {
return new Watermark(lastElement.f1 - 5);
}
}).map(new MapFunction<Tuple2<Event, Long>, Event>() {
@Override
public Event map(Tuple2<Event, Long> value) throws Exception {
return value.f0;
}
});
EventComparator<Event> comparator = new CustomEventComparator();
Pattern<Event, ? extends Event> pattern = Pattern.<Event>begin("start").where(new SimpleCondition<Event>() {
@Override
public boolean filter(Event value) throws Exception {
System.out.println("testSimplePatternEventTimeWithComparator start -> " + value);
return value.getName().equals("start");
}
}).followedByAny("middle").where(new SimpleCondition<Event>() {
@Override
public boolean filter(Event value) throws Exception {
System.out.println("testSimplePatternEventTimeWithComparator middle -> " + value);
return value.getName().equals("middle");
}
}).followedByAny("end").where(new SimpleCondition<Event>() {
@Override
public boolean filter(Event value) throws Exception {
System.out.println("testSimplePatternEventTimeWithComparator end -> " + value);
return value.getName().equals("end");
}
});
// 這裏使用了一個 CustomEventComparator ,事件比較器
// 先按照事件發生的 EventTime 排序,再按價格排序來依次處理
DataStream<String> result = CEP.pattern(input, pattern, comparator).select(
new PatternSelectFunction<Event, String>() {
@Override
public String select(Map<String, List<Event>> pattern) {
StringBuilder builder = new StringBuilder();
builder.append(pattern.get("start").get(0).getId()).append(",")
.append(pattern.get("middle").get(0).getId()).append(",")
.append(pattern.get("end").get(0).getId());
System.out.println("testSimplePatternEventTimeWithComparator -> " + builder.toString());
return builder.toString();
}
}
);
List<String> resultList = new ArrayList<>();
DataStreamUtils.collect(result).forEachRemaining(resultList::add);
List<String> expected = Arrays.asList(
"1,6,4",
"1,5,4"
);
expected.sort(String::compareTo);
resultList.sort(String::compareTo);
assertEquals(expected, resultList);
}
/**
* Event 比較器,按事件中的 Price 值大小從低到高排序
*/
private static class CustomEventComparator implements EventComparator<Event> {
@Override
public int compare(Event o1, Event o2) {
return Double.compare(o1.getPrice(), o2.getPrice());
}
}
}
輸出結果,匹配出 <1,6,4>,<1,5,4> 序列:
testSimplePatternEventTimeWithComparator start -> Event(2, middle, 2.0)
testSimplePatternEventTimeWithComparator start -> Event(3, end, 3.0)
testSimplePatternEventTimeWithComparator start -> Event(1, start, 1.0)
testSimplePatternEventTimeWithComparator middle -> Event(6, middle, 5.0)
testSimplePatternEventTimeWithComparator start -> Event(6, middle, 5.0)
testSimplePatternEventTimeWithComparator end -> Event(5, middle, 6.0)
testSimplePatternEventTimeWithComparator middle -> Event(5, middle, 6.0)
testSimplePatternEventTimeWithComparator start -> Event(5, middle, 6.0)
testSimplePatternEventTimeWithComparator end -> Event(4, end, 4.0)
testSimplePatternEventTimeWithComparator end -> Event(4, end, 4.0)
testSimplePatternEventTimeWithComparator middle -> Event(4, end, 4.0)
testSimplePatternEventTimeWithComparator start -> Event(4, end, 4.0)
testSimplePatternEventTimeWithComparator -> 1,6,4
testSimplePatternEventTimeWithComparator -> 1,5,4
testSimplePatternEventTimeWithComparator end -> Event(7, middle, 5.0)
testSimplePatternEventTimeWithComparator end -> Event(7, middle, 5.0)
testSimplePatternEventTimeWithComparator middle -> Event(7, middle, 5.0)
testSimplePatternEventTimeWithComparator start -> Event(7, middle, 5.0)
9.testSimpleAfterMatchSkip
public class CEPITCase extends AbstractTestBase {
/**
* Pattern 中設置匹配次數,依次匹配兩次 "a" ,重新開始一次新的匹配
*/
@Test
public void testSimpleAfterMatchSkip() throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
DataStream<Tuple2<Integer, String>> input = env.fromElements(
new Tuple2<>(1, "a"),
new Tuple2<>(2, "a"),
new Tuple2<>(3, "a"),
new Tuple2<>(4, "a"));
Pattern<Tuple2<Integer, String>, ?> pattern =
Pattern.<Tuple2<Integer, String>>begin("start", AfterMatchSkipStrategy.skipPastLastEvent())
.where(new SimpleCondition<Tuple2<Integer, String>>() {
@Override
public boolean filter(Tuple2<Integer, String> rec) throws Exception {
return rec.f1.equals("a");
}
}).times(2);
PatternStream<Tuple2<Integer, String>> pStream = CEP.pattern(input, pattern);
DataStream<Tuple2<Integer, String>> result = pStream.select(new PatternSelectFunction<Tuple2<Integer, String>, Tuple2<Integer, String>>() {
@Override
public Tuple2<Integer, String> select(Map<String, List<Tuple2<Integer, String>>> pattern) throws Exception {
return pattern.get("start").get(0);
}
});
List<Tuple2<Integer, String>> resultList = new ArrayList<>();
DataStreamUtils.collect(result).forEachRemaining(resultList::add);
resultList.sort(Comparator.comparing(tuple2 -> tuple2.toString()));
List<Tuple2<Integer, String>> expected = Arrays.asList(Tuple2.of(1, "a"), Tuple2.of(3, "a"));
assertEquals(expected, resultList);
}
}