4. Flink CEP 核心 API:Pattern API 概览
Flink CEP 的核心是 Pattern API,它提供了一套声明式、流式风格的编程接口,用于定义复杂事件匹配规则。本章将系统讲解 Pattern API 的四大核心概念:Pattern 定义、Pattern 序列、Pattern 组合以及整体 API 概览。掌握这些基础,是构建实时风控规则引擎的第一步。
4.1 Pattern API 整体架构
Pattern API 采用 Builder 模式,通过链式调用逐步构建匹配规则。其核心组件包括:
| 组件 | 说明 | 风控场景示例 |
|---|---|---|
| Pattern | 单个事件匹配条件,定义事件类型、属性约束 | “交易金额 > 10000” |
| PatternSequence | 多个 Pattern 按时间或逻辑顺序连接 | “登录失败 → 修改密码 → 大额转账” |
| Pattern组合 | 通过逻辑运算符(AND/OR/NOT)组合多个 Pattern | “(IP异常 OR 设备新) AND 金额超限” |
| CEP | 将 Pattern 应用到 DataStream 上,生成匹配结果流 | CEP.pattern(inputStream, pattern) |
💡 核心原则:Pattern API 的设计哲学是“先定义事件形态,再组合事件关系”。每个 Pattern 描述一个事件“是什么”,Pattern 序列描述事件“怎么连”,Pattern 组合描述事件“怎么选”。
4.2 Pattern 定义:单个事件的匹配条件
Pattern 定义是构建规则的最小单元,用于描述一个“事件”应该满足的条件。在风控场景中,每个 Pattern 对应一个可疑行为片段。
4.2.1 创建 Pattern
使用 Pattern.<T>begin("name") 创建初始 Pattern,其中泛型 T 是事件类型,name 是 Pattern 的唯一标识。
// 定义:交易金额超过 10000 的事件
Pattern<TransactionEvent, TransactionEvent> largeAmount =
Pattern.<TransactionEvent>begin("large_transaction")
.where(event -> event.getAmount() > 10000);
4.2.2 条件约束方法
| 方法 | 说明 | 风控使用示例 |
|---|---|---|
.where(condition) |
定义事件必须满足的条件(AND 语义) | .where(e -> e.getAmount() > 5000) |
.or(condition) |
定义事件的 OR 条件,满足其一即可 | .where(e -> e.getCountry().equals("CN")).or(e -> e.getCountry().equals("HK")) |
.until(condition) |
循环匹配时,指定终止条件 | .oneOrMore().until(e -> e.getAmount() < 100) |
.subtype(subClass) |
限制事件为指定子类型 | .subtype(CreditCardTransaction.class) |
⚠️ 避坑指南:多个
.where() 之间是 AND 关系,不要重复调用 .where() 来写 OR 逻辑。OR 逻辑必须使用 .or() 方法。
4.3 Pattern 序列:连接多个事件
Pattern 序列定义了事件之间的时间顺序和邻近关系。在风控场景中,序列用于描述“先 A 后 B”的异常行为链条。
4.3.1 序列连接方法
| 方法 | 语义 | 风控示例 |
|---|---|---|
.next("name") |
严格连续:A 后面必须紧跟着 B,中间不能有其他事件 | “连续两次输错密码” |
.followedBy("name") |
宽松连续:A 后面出现 B 即可,中间允许有其他事件 | “登录后任意时间发生转账” |
.followedByAny("name") |
非确定性宽松连续:A 后面出现 B,且 B 可以匹配多次 | “同一设备多次登录不同账户” |
.notNext("name") |
否定严格连续:A 后面不能紧跟着 B | “登录后不能立即修改密码” |
.notFollowedBy("name") |
否定宽松连续:A 后面不能出现 B | “登录后不能有异地登录” |
4.3.2 序列构建示例
// 风控规则:登录失败 → 修改密码 → 大额转账(严格连续)
Pattern<LoginEvent, LoginEvent> riskPattern =
Pattern.<LoginEvent>begin("login_fail")
.where(e -> e.getStatus().equals("FAIL"))
.next("change_pwd")
.where(e -> e.getType().equals("PASSWORD_CHANGE"))
.next("large_transfer")
.where(e -> e.getAmount() > 50000);
4.4 Pattern 组合:逻辑与时间约束
Pattern 组合提供了逻辑运算符和时间窗口,用于构建更复杂的风控规则。
4.4.1 逻辑组合
- AND 组合:多个条件同时满足,通过
.where()链式调用实现。 - OR 组合:多个条件满足其一,通过
.or()方法实现。 - NOT 组合:否定某个事件的出现,通过
.notNext()或.notFollowedBy()实现。 - 循环组合:事件重复出现,通过
.times(n)、.oneOrMore()、.timesOrMore(n)实现。
4.4.2 时间约束
使用 .within(time) 为整个 Pattern 序列设置时间窗口,超过时间未匹配则丢弃。
// 风控规则:5分钟内,同一用户连续3次交易失败 → 触发告警
Pattern<TransactionEvent, TransactionEvent> failPattern =
Pattern.<TransactionEvent>begin("fail1")
.where(e -> e.getStatus().equals("FAIL"))
.next("fail2")
.where(e -> e.getStatus().equals("FAIL"))
.next("fail3")
.where(e -> e.getStatus().equals("FAIL"))
.within(Time.minutes(5)); // 整个序列必须在5分钟内完成
4.4.3 循环模式与量词
| 量词 | 说明 | 风控示例 |
|---|---|---|
.times(n) |
精确匹配 n 次 | “连续 3 次输错密码” |
.times(n, m) |
匹配 n 到 m 次 | “2~5 次小额试探交易” |
.oneOrMore() |
匹配 1 次或多次 | “至少 1 次异地登录” |
.timesOrMore(n) |
匹配 n 次或更多 | “3 次以上大额转账” |
.optional() |
该事件可选出现 | “登录后(可选)修改密码” |
🚨 性能警告:循环模式(
.oneOrMore()、.timesOrMore())在风控场景中容易产生大量匹配状态,建议配合 .within() 时间窗口使用,并设置 .consecutive() 限制连续匹配。
4.5 完整风控 Pattern 示例
以下是一个综合使用 Pattern 定义、序列、组合的实时风控规则:
// 规则:10分钟内,同一用户从新设备登录 + 修改密码 + 大额转账(金额 > 20000)
Pattern<RiskEvent, RiskEvent> highRiskPattern =
Pattern.<RiskEvent>begin("new_device_login")
.where(e -> e.getType().equals("LOGIN"))
.where(e -> e.isNewDevice())
.followedBy("password_change")
.where(e -> e.getType().equals("PWD_CHANGE"))
.next("large_transfer")
.where(e -> e.getType().equals("TRANSFER"))
.where(e -> e.getAmount() > 20000)
.within(Time.minutes(10)); // 整个序列必须在10分钟内完成
// 应用到 DataStream
DataStream<RiskEvent> inputStream = ...;
PatternStream<RiskEvent> patternStream = CEP.pattern(inputStream, highRiskPattern);
4.6 本章小结
| 核心概念 | 关键方法 | 风控应用要点 |
|---|---|---|
| Pattern 定义 | begin()、.where()、.or() |
每个 Pattern 描述一个可疑行为片段 |
| Pattern 序列 | .next()、.followedBy() |
描述事件之间的时间顺序和邻近关系 |
| Pattern 组合 | .times()、.within()、.optional() |
逻辑组合 + 时间窗口,构建复杂规则 |
| 性能关注 | .consecutive()、.within() |
循环模式必须配合时间窗口,避免状态爆炸 |