4、Flink CEP核心API:Pattern API详解——个体模式、组合模式、循环模式

Flink CEP 的 Pattern API 是定义事件匹配规则的核心编程接口。它采用链式调用的方式,允许开发者像搭建积木一样,从最简单的单一事件条件开始,逐步组合出复杂的时序逻辑。本章将深入剖析 Pattern API 的三大核心构件:个体模式(Individual Pattern)、组合模式(Combining Pattern) 和 循环模式(Looping Pattern),并结合实时交易风控场景进行讲解。

核心认知: 在 Flink CEP 中,一个完整的规则(Pattern)是由多个“个体模式”通过“组合模式”连接,并配合“循环模式”定义重复次数而构成的。理解这三者的关系是掌握 CEP 的关键。

4.1 个体模式(Individual Pattern)

个体模式是 Pattern API 中最基本的单元,它定义了对单个事件的匹配条件。每个个体模式都包含一个事件类型和一个过滤条件。在风控场景中,一个个体模式可以对应“一笔交易金额大于 10000 元”或“一个登录 IP 来自高风险地区”等单一规则。

4.1.1 核心方法

方法 描述 风控示例
.where(condition) 定义事件必须满足的条件,可多次调用形成 AND 逻辑。 .where(tx -> tx.getAmount() > 10000)
.or(condition) 添加一个可选条件,与已有条件形成 OR 逻辑。 .where(tx -> tx.getAmount() > 10000).or(tx -> tx.getRiskLevel() > 8)
.until(condition) 指定模式匹配的终止条件(通常用于循环模式)。 连续失败登录直到登录成功为止。
.subtype(subClass) 限制匹配的事件为指定类型的子类型。 .subtype(CreditCardTx.class)

4.1.2 个体模式分类

  • 单例模式(Singleton Pattern):默认类型,只匹配单个事件。例如:“检测到一笔单笔转账超过 5 万元”。
  • 循环模式(Looping Pattern):通过 .times()、.oneOrMore() 等方法定义,匹配多个同类事件。将在 4.3 节详细展开。
避坑指南: 在个体模式中使用 .where() 时,条件函数必须是可序列化的(Serializable),且应避免使用外部可变状态,以保证算子的容错性和一致性。

4.2 组合模式(Combining Pattern)

组合模式用于将多个个体模式按照时间顺序连接起来,形成事件序列。它定义了事件之间的时序关系和连续性策略。在风控中,组合模式用于描述“先登录,后交易”或“先下单,后支付,再发货”等业务流程。

4.2.1 组合方式

方法 语义 风控场景示例
.next() 严格连续:下一个事件必须紧接着上一个事件发生,中间不能有其他事件。 连续两次输错密码(中间不能有其他操作)。
.followedBy() 宽松连续:后续事件可以在任意时间点出现,不要求紧邻。 登录后,在 10 分钟内发生交易。
.followedByAny() 非确定性宽松连续:允许后续事件与前面的事件重叠匹配。 检测“A 事件后出现 B 事件”,允许 B 事件同时作为多个匹配的起点。
.notNext() 否定模式(严格):下一个事件不能是指定事件。 支付后,下一个事件不能是退款。
.notFollowedBy() 否定模式(宽松):在指定时间范围内,不能出现指定事件。 登录后 5 分钟内,不能有密码修改事件。
性能注意: .followedByAny() 由于会产生多个重叠匹配,其状态开销和计算复杂度远高于 .next() 和 .followedBy()。在风控高并发场景下,应优先使用 .next() 或 .followedBy(),仅在必要时(如检测“首次”与“后续”关系)才使用 .followedByAny()。

4.2.2 组合模式示例(风控:盗刷检测)

Pattern<Transaction, ?> fraudPattern = Pattern
    .<Transaction>begin("login")
        .where(evt -> evt.getType().equals("LOGIN"))
        .where(evt -> evt.getGeo().equals("abnormal"))
    .followedBy("txn")
        .where(evt -> evt.getAmount() > 5000)
    .within(Time.minutes(5));

上述规则表示:在 5 分钟内,先发生一次异常地理位置的登录,随后发生一笔超过 5000 元的交易,即触发告警。

4.3 循环模式(Looping Pattern)

循环模式是对个体模式的扩展,用于匹配多个相同类型的事件。它通过指定重复次数来定义“连续发生多次”的行为。在风控中,循环模式常用于检测“短时间内多次尝试”、“批量操作”等场景。

4.3.1 循环量词

量词方法 含义 风控示例
.times(n) 精确匹配 n 次。 连续 3 次输错密码。
.times(n, m) 匹配 n 到 m 次(包含边界)。 连续 2~5 次小额试探交易。
.oneOrMore() 匹配一次或多次(至少一次)。 连续多次异地登录。
.timesOrMore(n) 匹配 n 次或更多次。 连续 3 次及以上的失败支付。
.optional() 使当前模式可选(可匹配 0 次或 1 次)。 登录后,可能发生交易(也可能不发生)。
.greedy() 贪婪匹配:尽可能多地匹配事件(需配合循环量词使用)。 在满足条件的情况下,匹配尽可能多的失败尝试。
.consecutive() 要求匹配的事件在时间上是严格连续的(中间不能有不符合条件的事件)。 连续 3 次失败,中间不能有成功登录。

4.3.2 循环模式示例(风控:暴力破解检测)

Pattern<LoginEvent, ?> bruteForcePattern = Pattern
    .<LoginEvent>begin("failures")
        .where(evt -> evt.getStatus().equals("FAILED"))
        .times(3)
        .consecutive()
    .next("success")
        .where(evt -> evt.getStatus().equals("SUCCESS"))
    .within(Time.minutes(2));

该规则匹配:在 2 分钟内,连续 3 次登录失败(中间不能有成功登录),紧接着发生一次登录成功,这通常意味着攻击者已破解密码。

4.4 三种模式的关系与组合

下图展示了一个完整的 Flink CEP 规则是如何由个体模式、组合模式和循环模式构建而成的:

Pattern API 三层结构图 Pattern API 三层结构:从个体到组合再到循环 个体模式 .where() / .or() / .subtype() 组合模式 .next() / .followedBy() 循环模式 .times() / .oneOrMore() 组合示例:暴力破解检测规则 Pattern.begin("failures") // 个体模式:定义失败事件 .where(...).times(3) // 循环模式:匹配3次 .next("success") // 组合模式:严格连续 .where(...) // 个体模式:定义成功事件

4.5 实战对比:三种模式在风控中的典型应用

模式类型 典型风控场景 关键 API 匹配结果
个体模式 单笔大额交易告警 .where(amount > 100000) 单个事件
组合模式 登录后立即修改密码 .begin("login").next("changePwd") 事件序列(2个事件)
循环模式 5分钟内连续3次失败支付 .times(3).consecutive() 多个同类事件
组合+循环 连续失败后成功登录(暴力破解) .times(3).next("success") 混合事件序列
设计建议: 在编写风控规则时,建议先画出事件流的时序图,明确哪些事件是“必须连续”的(使用 .next()),哪些是“可以间隔”的(使用 .followedBy()),以及哪些事件需要“重复出现”(使用循环量词)。这能有效避免因连续性策略选择不当导致的误报或漏报。