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 组合描述事件“怎么选”。
Pattern API 整体架构图 Pattern API 核心组件架构 DataStream Pattern 定义 .where(条件) Pattern 序列 .next() / .followedBy() 匹配结果 Pattern 组合:AND / OR / NOT / 时间约束 begin("A").where(...).next("B").where(...).within(5.minutes) Pattern API 采用 Builder 模式:Pattern定义 → Pattern序列 → Pattern组合 → 应用到 DataStream

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);
Pattern 序列连接方式对比 Pattern 序列连接方式对比 .next("B") 严格连续 A → B(中间无其他事件) 例:连续两次输错密码 .followedBy("B") 宽松连续 A → ... → B(中间可有其他事件) 例:登录后任意时间转账 .followedByAny("B") 非确定性 A → B₁, B₂, B₃(B 可匹配多次) 例:同一设备登录多账户 选择建议 • 风控中优先使用 .next() 和 .followedBy(),避免 .followedByAny() 导致过多匹配 • 否定序列 .notNext() / .notFollowedBy() 不能作为序列的最后一个 Pattern

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() 循环模式必须配合时间窗口,避免状态爆炸