4. Flink CEP 快速入门:环境搭建、第一个CEP程序与运行流程
本章是Flink CEP实战的起点。我们将从零搭建开发环境,编写第一个基于简单序列匹配的CEP程序,并拆解其代码结构与运行流程。掌握这些基础后,后续的复杂模式与阈值调优才能落地。
4.1 环境搭建:最小化依赖与版本选型
Flink CEP作为Flink的专用库,无需额外安装集群,只需在项目中引入正确的Maven/Gradle依赖即可。推荐版本组合如下:
| 组件 | 推荐版本 | 说明 |
|---|---|---|
| Java | 11 / 17 | Flink 1.17+ 对Java 17支持良好 |
| Flink | 1.17.2 / 1.18.0 | CEP库已内置,无需额外下载 |
| flink-cep | 同Flink版本 | 核心CEP依赖 |
| flink-streaming-java | 同Flink版本 | DataStream API基础 |
| flink-clients | 同Flink版本 | 本地执行与调试 |
避坑提示: flink-cep 与 flink-streaming-java 的版本必须严格一致,否则运行时会出现 NoSuchMethodError。建议在 pom.xml 中使用 属性统一管理。
Maven 核心依赖示例:
<properties>
<flink.version>1.17.2</flink.version>
<scala.binary.version>2.12</scala.binary.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-streaming-java</artifactId>
<version>${flink.version}</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-cep</artifactId>
<version>${flink.version}</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-clients</artifactId>
<version>${flink.version}</version>
</dependency>
</dependencies>
4.2 第一个CEP程序:简单序列匹配
我们以“连续两笔大额交易”作为检测场景:同一账户在5分钟内发生两笔金额超过10000的交易,则触发告警。这是CEP中最基础的严格连续(Strict Contiguity)模式。
4.2.1 数据模型与源
// 交易事件POJO
public class TransactionEvent {
public String accountId;
public double amount;
public long timestamp;
public TransactionEvent() {}
public TransactionEvent(String accountId, double amount, long timestamp) {
this.accountId = accountId;
this.amount = amount;
this.timestamp = timestamp;
}
@Override
public String toString() {
return "TransactionEvent{" +
"accountId='" + accountId + '\'' +
", amount=" + amount +
", timestamp=" + timestamp +
'}';
}
}
4.2.2 定义CEP模式
import org.apache.flink.cep.pattern.Pattern;
import org.apache.flink.cep.pattern.conditions.SimpleCondition;
import org.apache.flink.streaming.api.windowing.time.Time;
// 定义模式:严格连续的两笔大额交易
Pattern<TransactionEvent, ?> pattern = Pattern
.<TransactionEvent>begin("first")
.where(new SimpleCondition<TransactionEvent>() {
@Override
public boolean filter(TransactionEvent event) {
return event.amount > 10000;
}
})
.next("second") // 严格连续:second必须紧跟在first之后
.where(new SimpleCondition<TransactionEvent>() {
@Override
public boolean filter(TransactionEvent event) {
return event.amount > 10000;
}
})
.within(Time.minutes(5)); // 整体时间窗口
4.2.3 应用模式并输出告警
import org.apache.flink.cep.CEP;
import org.apache.flink.cep.PatternSelectFunction;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import java.util.List;
import java.util.Map;
public class CepQuickStart {
public static void main(String[] args) throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
// 模拟数据源(实际替换为Kafka/Socket)
DataStream<TransactionEvent> inputStream = env
.fromElements(
new TransactionEvent("acc_001", 15000, 1000L),
new TransactionEvent("acc_001", 20000, 1001L), // 匹配
new TransactionEvent("acc_002", 8000, 1002L),
new TransactionEvent("acc_001", 5000, 1003L)
)
.assignTimestampsAndWatermarks(
WatermarkStrategy.<TransactionEvent>forMonotonousTimestamps()
.withTimestampAssigner((event, ts) -> event.timestamp)
);
// 将模式应用到流上
DataStream<String> alertStream = CEP.pattern(inputStream.keyBy(e -> e.accountId), pattern)
.select(new PatternSelectFunction<TransactionEvent, String>() {
@Override
public String select(Map<String, List<TransactionEvent>> pattern) throws Exception {
TransactionEvent first = pattern.get("first").get(0);
TransactionEvent second = pattern.get("second").get(0);
return String.format("[ALERT] 账户 %s 连续大额交易: %.2f -> %.2f",
first.accountId, first.amount, second.amount);
}
});
alertStream.print();
env.execute("Flink CEP QuickStart");
}
}
运行结果: 控制台输出
[ALERT] 账户 acc_001 连续大额交易: 15000.00 -> 20000.00。acc_002 因第一笔金额不足10000,不触发匹配。
4.3 代码结构与运行流程拆解
一个完整的Flink CEP程序由以下五个核心环节构成,每个环节对应一段独立代码逻辑:
- 获取执行环境 —
StreamExecutionEnvironment.getExecutionEnvironment() - 构建数据源 — 从Kafka、Socket或本地集合读取事件流,并分配时间戳与水位线
- 定义模式 — 使用
Pattern.begin().next().where().within()链式API描述事件序列规则 - 应用模式检测 —
CEP.pattern(stream, pattern)生成PatternStream - 处理匹配结果 — 通过
.select()或.flatSelect()提取匹配事件并输出告警
4.4 模式匹配语义对比
CEP中“连续”有多种语义,初学者最容易混淆。下表对比了三种常用连续策略:
| 策略 | API方法 | 匹配要求 | 适用场景 |
|---|---|---|---|
| 严格连续 | .next() |
事件必须紧邻,中间不能有任何其他事件 | 连续两次操作、紧邻的异常行为 |
| 宽松连续 | .followedBy() |
允许中间出现不匹配的事件,但顺序必须保持 | 登录后一段时间内的异常操作 |
| 非确定性宽松连续 | .followedByAny() |
允许中间出现不匹配事件,且允许跳过匹配事件 | 重复模式、循环检测 |
常见误区: 使用
.next() 时,如果数据流中存在乱序或迟到数据,严格连续可能导致漏报。金融风控场景中,建议优先使用 .followedBy() 配合 .within() 时间约束,以容忍轻微乱序。
4.5 本章小结
通过本章,你已掌握:
- Flink CEP的最小依赖配置与版本选型
- 基于
Pattern.next()的严格连续序列匹配 - CEP程序的五步运行流程:环境→数据源→模式→检测→结果
- 三种连续策略的语义差异与选型建议