Flink事件时间与窗口操作实战指南

Scala 复制代码
package api

import org.apache.flink.streaming.api.TimeCharacteristic
import org.apache.flink.streaming.api.functions.timestamps.BoundedOutOfOrdernessTimestampExtractor
import org.apache.flink.streaming.api.scala._
import org.apache.flink.streaming.api.windowing.assigners.{EventTimeSessionWindows, SlidingProcessingTimeWindows, TumblingEventTimeWindows}
import org.apache.flink.streaming.api.windowing.time.Time

/**
 *
 * @PROJECT_NAME: Flink
 * @PACKAGE_NAME: api
 * @author: 赵嘉盟-HONOR
 * @data: 2025-05-14 1:46
 * @DESCRIPTION
 *
 */
object Window {
  def main(args: Array[String]): Unit = {
    val env=StreamExecutionEnvironment.getExecutionEnvironment
//    val inputPath="H:\\Scala程序\\Flink\\src\\main\\resources\\source.txt"
//    val stream=env.readTextFile(inputPath)
    env.setParallelism(1)
    env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)   //添加事件时间语义,不添加默认Processing time(操作时间)
    env.getConfig.setAutoWatermarkInterval(500)   //设置水平线生成周期
    val inputStream=env.socketTextStream("localhost",7777)
    //先转换样例类(简单转换操作)
    val dataStream=inputStream
      .map(data=>{
        val arr=data.split(",")
        SensorReading(arr(0),arr(1).toLong,arr(2).toDouble)
      })
      //.assignAscendingTimestamps(_.timestamp*1000l)    //升序数据提取时间戳方法,直接使用事件时间戳
      .assignTimestampsAndWatermarks( new BoundedOutOfOrdernessTimestampExtractor[SensorReading](Time.seconds(3)) {   //最大乱序程度
        override def extractTimestamp(t: SensorReading): Long = t.timestamp*1000l   //提取时间戳
      })

    //每十五秒统计一下窗口内各个传感器温度的最小值,以及最新的时间戳
    val resultStream=dataStream
      .map( data=>(data.id,data.temperature,data.timestamp))
      .keyBy(_._1)
      //.window( TumblingEventTimeWindows.of(Time.days(1),Time.hours(-8)))   //滚动时间窗口
      //.window( SlidingProcessingTimeWindows.of(Time.seconds(15),Time.seconds(10)))  //滑动时间窗口
      //.window( EventTimeSessionWindows.withGap(Time.seconds(10)))   //绘画窗口
      .timeWindow(Time.seconds(15))   //简写方法
      .allowedLateness(Time.minutes(1)) //允许处理迟到数据
      .sideOutputLateData(new OutputTag[(String, Double, Long)]("late"))    //写进侧输出流
      //.minBy(1)
      .reduce(
        (curRes,newData)=>(curRes._1,curRes._2.min(newData._2),newData._3))

    resultStream.getSideOutput(new OutputTag[(String, Double, Long)]("late")).print("late")   //获取测输出流
    resultStream.print("resulta")
    env.execute("window")
  }
}

这段代码是一个使用 Apache Flink 进行流处理的示例,重点展示了如何基于事件时间(Event Time)处理数据流,并使用窗口操作进行聚合。以下是代码的详细解释和背景知识拓展。


代码解释

1. 环境设置
Scala 复制代码
val env = StreamExecutionEnvironment.getExecutionEnvironment
env.setParallelism(1)
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
env.getConfig.setAutoWatermarkInterval(500)
  • StreamExecutionEnvironment.getExecutionEnvironment:获取流处理执行环境。
  • env.setParallelism(1):设置并行度为 1,方便调试。
  • env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime):设置时间语义为事件时间(Event Time),即使用数据本身的时间戳。
  • env.getConfig.setAutoWatermarkInterval(500):设置水位线(Watermark)生成周期为 500 毫秒。
2. 数据源
Scala 复制代码
val inputStream = env.socketTextStream("localhost", 7777)
  • 从本地主机的 7777 端口读取数据流。
3. 数据转换
Scala 复制代码
val dataStream = inputStream
  .map(data => {
    val arr = data.split(",")
    SensorReading(arr(0), arr(1).toLong, arr(2).toDouble)
  })
  .assignTimestampsAndWatermarks(new BoundedOutOfOrdernessTimestampExtractor[SensorReading](Time.seconds(3)) {
    override def extractTimestamp(t: SensorReading): Long = t.timestamp * 1000L
  })
  • map:将每行数据转换为 SensorReading 对象,包含 idtimestamptemperature 字段。
  • assignTimestampsAndWatermarks:为数据流分配时间戳和水位线。
    • BoundedOutOfOrdernessTimestampExtractor:允许数据乱序,最大乱序时间为 3 秒。
    • extractTimestamp:从 SensorReading 对象中提取时间戳,并转换为毫秒。
4. 窗口操作
Scala 复制代码
val resultStream = dataStream
  .map(data => (data.id, data.temperature, data.timestamp))
  .keyBy(_._1)
  .timeWindow(Time.seconds(15))
  .allowedLateness(Time.minutes(1))
  .sideOutputLateData(new OutputTag[(String, Double, Long)]("late"))
  .reduce((curRes, newData) => (curRes._1, curRes._2.min(newData._2), newData._3))
  • map:将 SensorReading 对象转换为三元组 (id, temperature, timestamp)
  • keyBy:按照 id 字段对数据进行分组。
  • timeWindow:定义一个 15 秒的滚动窗口。
  • allowedLateness:允许处理迟到数据,最大延迟时间为 1 分钟。
  • sideOutputLateData:将迟到数据写入侧输出流。
  • reduce:对窗口内的数据进行聚合,保留 id、最小温度和最新时间戳。
5. 侧输出流
Scala 复制代码
resultStream.getSideOutput(new OutputTag[(String, Double, Long)]("late")).print("late")
  • 获取侧输出流中的数据并打印。
6. 结果输出
Scala 复制代码
resultStream.print("result")
env.execute("window")
  • 打印主数据流的结果。
  • env.execute("window"):启动流处理任务。

背景知识拓展

1. 时间语义
  • 事件时间(Event Time):数据本身的时间戳,通常用于处理乱序数据。
  • 处理时间(Processing Time):数据被处理时的时间,简单但无法处理乱序数据。
  • 摄入时间(Ingestion Time):数据进入 Flink 系统的时间。
2. 水位线(Watermark)
  • 作用:用于处理乱序数据,表示事件时间的进度。
  • 生成方式 :可以通过 BoundedOutOfOrdernessTimestampExtractor 等工具生成。
  • 乱序容忍:允许数据在一定时间内乱序,超过该时间的数据会被丢弃或写入侧输出流。
3. 窗口操作
  • 滚动窗口(Tumbling Window):固定大小的窗口,窗口之间不重叠。
  • 滑动窗口(Sliding Window):固定大小的窗口,窗口之间可以重叠。
  • 会话窗口(Session Window):根据数据之间的间隔动态划分窗口。
4. 迟到数据处理
  • 允许延迟(Allowed Lateness):允许窗口关闭后一段时间内处理迟到数据。
  • 侧输出流(Side Output):将迟到数据写入侧输出流,以便后续处理。
  • DataStream API:用于处理无界数据流。
  • Table API & SQL:用于处理结构化数据。
  • State & Checkpoint:用于管理状态和实现容错。
6. 流处理应用场景
  • 实时监控:如传感器数据监控、日志分析。
  • 实时推荐:如电商平台的实时推荐系统。
  • 金融风控:如实时交易监控和欺诈检测。

进一步学习

通过这段代码的学习,你可以掌握如何使用 Flink 处理基于事件时间的数据流,并了解窗口操作、水位线、迟到数据处理等核心概念。

相关推荐
Yyyyy123jsjs2 小时前
如何选用外汇接口实现稳定数据抓取?
大数据·python·金融
孟意昶3 小时前
Doris专题31-SQL手册-基础元素
大数据·数据库·数据仓库·分布式·sql·知识图谱·doris
飞飞传输3 小时前
国产化FTP替代方案哪个好?选对平台让传输更安全高效
大数据·运维·安全
2501_933329553 小时前
企业舆情处置实战:Infoseek数字公关AI中台技术架构与功能解析
大数据·人工智能·架构·数据库开发
编程迪4 小时前
基于Java和Vue开发的剪辑接单小程序APP软件系统源码
大数据
551只玄猫4 小时前
【模块1 建立认知2】金融数据的类型与获取方式(附实战)
大数据·金融·数据科学·数据处理
亿信华辰软件4 小时前
从单业态到多业态:主数据管理的策略、架构与实践
大数据
ctrigger4 小时前
中国水利水电工程局有限公司
大数据
iiiiyu5 小时前
常用API(SimpleDateFormat类 & Calendar类 & JDK8日期 时间 日期时间 & JDK8日期(时区) )
java·大数据·开发语言·数据结构·编程语言