大数据-130 - Flink CEP 详解 - 捕获超时事件提取全解析:从原理到完整实战代码教程 恶意登录案例实现

点一下关注吧!!!非常感谢!!持续更新!!!

🚀 AI篇持续更新中!(长期更新)

AI炼丹日志-31- 千呼万唤始出来 GPT-5 发布!"快的模型 + 深度思考模型 + 实时路由",持续打造实用AI工具指南!📐🤖

💻 Java篇正式开启!(300篇)

目前2025年10月20日更新到: Java-153 深入浅出 MongoDB 全面的适用场景分析与选型指南 场景应用指南 MyBatis 已完结,Spring 已完结,Nginx已完结,Tomcat已完结,分布式服务正在更新!深入浅出助你打牢基础!

📊 大数据板块已完成多项干货更新(300篇):

包括 Hadoop、Hive、Kafka、Flink、ClickHouse、Elasticsearch 等二十余项核心组件,覆盖离线+实时数仓全栈! 大数据-278 Spark MLib - 基础介绍 机器学习算法 梯度提升树 GBDT案例 详解

章节内容

上节我们完成了如下的内容:

  • Flink CEP 核心组件
  • CEP 的应用场景
  • CEP 的优势

超时事件提取

当一个模式通过within关键字定义了检测窗口时间时,部分事件序列可能因为超过窗口长度而被丢弃,为了能够处理这些超时的部分匹配,select和flatSelectAPI调用允许制定超时处理程序。

FlinkCEP开发流程详解

1. 数据源转换

  • 从数据源(Kafka、文件、Socket等)获取原始数据流
  • 将数据转换为DataStream:
java 复制代码
  DataStream<Event> inputStream = env.addSource(source)
      .map(record -> new Event(record));
  • 常见数据源处理:
    • Kafka数据源:使用FlinkKafkaConsumer
    • 文件数据源:使用readTextFile/readFile方法

2. 模式定义与模式流创建

  • 定义事件模式(Pattern):
java 复制代码
  Pattern<Event, ?> pattern = Pattern.<Event>begin("start")
      .where(new SimpleCondition<Event>() {
          @Override
          public boolean filter(Event event) {
              return event.getType().equals("login");
          }
      })
      .next("middle")
      .within(Time.seconds(10));
  • 将DataStream与Pattern组合:
java 复制代码
  PatternStream<Event> patternStream = CEP.pattern(inputStream.keyBy(Event::getUserId), pattern);

3. 模式流处理

  • 使用Select/Process算子处理匹配事件:
java 复制代码
  DataStream<Alert> alerts = patternStream.process(
      new PatternProcessFunction<Event, Alert>() {
          @Override
          public void processMatch(
              Map<String, List<Event>> match,
              Context ctx,
              Collector<Alert> out) {
              out.collect(new Alert(match));
          }
      });
  • 处理方式选择:
    • select():简单提取匹配数据
    • process():复杂处理匹配数据
    • flatSelect():一对多处理

4. 结果输出

  • 结果数据流处理:
java 复制代码
  alerts.map(alert -> alert.toString())
  • 输出到目标库:
java 复制代码
  alerts.addSink(new SinkFunction<Alert>() {
      @Override
      public void invoke(Alert value, Context context) {
          // 写入数据库/消息队列等
      }
  });
  • 常见输出目标:
    • Kafka:使用FlinkKafkaProducer
    • 数据库:使用JDBCSink
    • 文件系统:使用StreamingFileSink

应用场景示例

  1. 风险控制:检测连续登录失败
  2. 运维监控:识别异常日志序列
  3. 物联网:设备状态异常检测
  4. 金融交易:可疑交易模式识别

SELECT 方法:

java 复制代码
SingleOutputStreamOperator<PayEvent> result =
    patternStream.select(orderTimeoutOutput, new PatternTimeoutFunction<PayEvent, PayEvent>() {
    @Override
    public PayEvent timeout(Map<String, List<PayEvent>> map, long l) throws Exception {
        return map.get("begin").get(0);
    }
}, new PatternSelectFunction<PayEvent, PayEvent>() {
    @Override
    public PayEvent select(Map<String, List<PayEvent>> map) throws Exception {
        return map.get("pay").get(0);
    }
});

对检测到的序列模式序列应用选择函数,对于每个模式序列,调用提供的 PatternSelectFunction,模式选择函数只能产生一个结果元素。 对超时的部分模式序列应用超时函数,对于每个部分模式序列,调用提供的 PatternTimeoutFunction,模式超时函数只能产生一个结果元素。 你可以在使用相同 OutputTag 进行 Select 操作 SingleOutputStreamOperator上获得SingleOutputStreamOperator生成的超时数据流。

非确定有限自动机

FlinkCEP 在运行时会将用户的逻辑转换为这样一个 NFA Graph(NFA对象) 所以有限状态机的工作过程,就是从开始状态,根据不同的输入,自动进行转换的过程。

上图中的状态机的功能,是检测二进制数是否含有偶数个0。从图上可以看出,输入只有1和0两种。 从S1状态开始,只有输入0才会转换到S2状态,同样S2状态下只有输入0才会转换到S1。所以,二进制输入完毕,如果满足最终状态,也就是最后停在S1状态,那么输入的二进制数就含有偶数个0。

CEP开发流程

FlinkCEP开发流程:

  • DataSource中数据转换为DataStream、Watermark、keyby
  • 定义Pattern,并将DataStream和Pattern组合转换为PatternStream
  • PatternStream经过select、process等算子转换为 DataStream
  • 再次转换为 DataStream 经过处理后,Sink到目标库

添加依赖

xml 复制代码
<dependency>
    <groupId>org.apache.flink</groupId>
    <artifactId>flink-cep_2.12</artifactId>
    <version>${flink.version}</version>
</dependency>

案例1:恶意登录检测

找出5秒内,连续登录失败的账号 以下是数据:

shell 复制代码
new CepLoginBean(1L, "fail", 1597905234000L),
new CepLoginBean(1L, "success", 1597905235000L),
new CepLoginBean(2L, "fail", 1597905236000L),
new CepLoginBean(2L, "fail", 1597905237000L),
new CepLoginBean(2L, "fail", 1597905238000L),
new CepLoginBean(3L, "fail", 1597905239000L),
new CepLoginBean(3L, "success", 1597905240000L)

整体思路

  • 获取到数据
  • 在数据源上做Watermark
  • 在Watermark上根据ID分组keyBy
  • 做出模式Pattern
  • 在数据流上进行模式匹配
  • 提取匹配成功的数据

编写代码

java 复制代码
package icu.wzk;
/**
 * Flink CEP 登录失败检测示例
 * 
 * 功能说明:
 * 该示例模拟了用户登录事件流,利用 Flink CEP 检测"同一用户在 5 秒内连续两次登录失败"的情况。
 * 
 * 核心步骤:
 * 1. 构造模拟数据流
 * 2. 设置事件时间(EventTime)与水位线(Watermark)
 * 3. 定义 CEP 模式(Pattern)
 * 4. 应用模式并处理匹配结果
 */
public class FlinkCepLoginTest {

    public static void main(String[] args) throws Exception {

        // 1️⃣ 创建执行环境
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

        // 设置时间语义为事件时间(EventTime)
        env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime);

        // 并行度设置为 1,方便观察控制台输出
        env.setParallelism(1);

        // 2️⃣ 模拟输入数据流(用户登录事件)
        DataStreamSource<CepLoginBean> data = env.fromElements(
                new CepLoginBean(1L, "fail", 1597905234000L),
                new CepLoginBean(1L, "success", 1597905235000L),
                new CepLoginBean(2L, "fail", 1597905236000L),
                new CepLoginBean(2L, "fail", 1597905237000L),
                new CepLoginBean(2L, "fail", 1597905238000L),
                new CepLoginBean(3L, "fail", 1597905239000L),
                new CepLoginBean(3L, "success", 1597905240000L)
        );

        // 3️⃣ 为数据流分配时间戳与水位线
        SingleOutputStreamOperator<CepLoginBean> watermarks = data
                .assignTimestampsAndWatermarks(
                        new WatermarkStrategy<CepLoginBean>() {

                            @Override
                            public WatermarkGenerator<CepLoginBean> createWatermarkGenerator(WatermarkGeneratorSupplier.Context context) {
                                return new WatermarkGenerator<CepLoginBean>() {

                                    // 当前已观察到的最大事件时间戳
                                    long maxTimestamp = Long.MAX_VALUE;

                                    // 最大允许乱序时间(即事件允许延迟 500ms 到达)
                                    long maxOutOfOrderness = 500L;

                                    @Override
                                    public void onEvent(CepLoginBean event, long eventTimestamp, WatermarkOutput output) {
                                        // 更新当前最大时间戳
                                        maxTimestamp = Math.max(maxTimestamp, event.getTimestamp());
                                    }

                                    @Override
                                    public void onPeriodicEmit(WatermarkOutput output) {
                                        // 周期性发送水位线:当前最大时间戳 - 乱序时间
                                        output.emitWatermark(new Watermark(maxTimestamp - maxOutOfOrderness));
                                    }
                                };
                            }
                        }.withTimestampAssigner((element, recordTimestamp) -> element.getTimestamp())
                );

        // 4️⃣ 按用户 ID 分组(KeyBy)
        KeyedStream<CepLoginBean, Long> keyed = watermarks
                .keyBy(new KeySelector<CepLoginBean, Long>() {
                    @Override
                    public Long getKey(CepLoginBean value) {
                        return value.getUserId();
                    }
                });

        // 5️⃣ 定义 CEP 模式:连续两次失败且间隔不超过 5 秒
        Pattern<CepLoginBean, CepLoginBean> pattern = Pattern
                .<CepLoginBean>begin("start")
                .where(new IterativeCondition<CepLoginBean>() {
                    @Override
                    public boolean filter(CepLoginBean cepLoginBean, Context<CepLoginBean> context) {
                        // 第一个事件:登录失败
                        return cepLoginBean.getOperation().equals("fail");
                    }
                })
                .next("next")
                .where(new IterativeCondition<CepLoginBean>() {
                    @Override
                    public boolean filter(CepLoginBean cepLoginBean, Context<CepLoginBean> context) {
                        // 第二个事件:同样是登录失败
                        return cepLoginBean.getOperation().equals("fail");
                    }
                })
                // 限定两次失败必须发生在 5 秒内
                .within(Time.seconds(5));

        // 6️⃣ 将模式应用到 keyed 流
        PatternStream<CepLoginBean> patternStream = CEP.pattern(keyed, pattern);

        // 7️⃣ 处理匹配到的事件(输出报警或进一步操作)
        SingleOutputStreamOperator<CepLoginBean> process = patternStream
                .process(new PatternProcessFunction<CepLoginBean, CepLoginBean>() {
                    @Override
                    public void processMatch(Map<String, List<CepLoginBean>> map, Context context, Collector<CepLoginBean> collector) {
                        System.out.println("map: " + map);
                        // 获取第一个"fail"事件并输出
                        List<CepLoginBean> start = map.get("start");
                        collector.collect(start.get(0));
                    }
                });

        // 8️⃣ 打印匹配结果
        process.print();

        // 9️⃣ 启动作业
        env.execute("FlinkCepLoginTest");
    }
}

/**
 * 自定义登录事件数据类
 * 包含用户ID、操作类型、时间戳等字段
 */
class CepLoginBean {

    private Long userId;      // 用户ID
    private String operation; // 操作类型(如 "fail", "success")
    private Long timestamp;   // 事件时间(毫秒)

    public CepLoginBean(Long userId, String operation, Long timestamp) {
        this.userId = userId;
        this.operation = operation;
        this.timestamp = timestamp;
    }

    public Long getUserId() {
        return userId;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }

    public String getOperation() {
        return operation;
    }

    public void setOperation(String operation) {
        this.operation = operation;
    }

    public Long getTimestamp() {
        return timestamp;
    }

    public void setTimestamp(Long timestamp) {
        this.timestamp = timestamp;
    }

    @Override
    public String toString() {
        return "CepLoginBean{" +
                "userId=" + userId +
                ", operation='" + operation + '\'' +
                ", timestamp=" + timestamp +
                '}';
    }
}

运行结果

可以看到程序输出:

shell 复制代码
map: {start=[CepLoginBean{userId=2, operation='fail', timestamp=1597905236000}], next=[CepLoginBean{userId=2, operation='fail', timestamp=1597905237000}]}...

Process finished with exit code 0

运行截图如下所示:

相关推荐
计算机编程指导师5 小时前
【计算机毕设】基于Hadoop+Spark的美国互联网银行贷款数据可视化分析系统源码 毕业设计 选题推荐 毕设选题 数据分析 机器学习
大数据·hadoop·python·数据分析·spark·毕业设计·贷款
data analyse 4566 小时前
服务端访问日志聚合 PV 与 UV 怎么实现?日志采集与口径设计
大数据·前端·数据分析
JavaGuide6 小时前
DeepSeek Harness 重磅更新,Codex、Claude Code 子智能体进官方插件列表了,还有 Git 工作树和思考翻译!
前端·后端
ZZ202601156 小时前
维普露禾平台免费开放,聊聊教育垂直大模型的RAG实现思路
大数据·人工智能·系统架构·知识图谱
小猴子爱上树6 小时前
跨境电商AI图片翻译工具:批量图片翻译、视频字幕翻译与智能抠图一站式解决
大数据·人工智能·python·音视频
七十二时9566 小时前
2026 企业级 AI 办公平台选型指南:如何判断哪些 AI 满足企业办公需求
大数据·人工智能
Rain的Java大神之路6 小时前
[特殊字符]8年Java老兵转型AI Agent:90%的人挂在同一个坑,根本不用学Python!(万字实战复盘,建议收藏)
java·数据库·人工智能·后端·python·spring·ai编程
康实训6 小时前
CGA老年综合评估是什么?评估体系、核心维度与行业应用标准解析
大数据·人工智能·实训室·实训室建设
高频因子挖掘机6 小时前
A股分钟K线数据占内存:先缩小请求范围,还是换 DataFrame 工具?
后端·github·api
高级程序源6 小时前
springboot大学生体测系统72170-计算机课程设计、毕业设计
java·vue.js·spring boot·后端·python·django·课程设计