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

#新星杯·14天创作挑战营·第11期#

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. 环境设置
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. 数据源
val inputStream = env.socketTextStream("localhost", 7777)
  • 从本地主机的 7777 端口读取数据流。
3. 数据转换
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 对象,包含 idtimestamp 和 temperature 字段。
  • assignTimestampsAndWatermarks:为数据流分配时间戳和水位线。
    • BoundedOutOfOrdernessTimestampExtractor:允许数据乱序,最大乱序时间为 3 秒。
    • extractTimestamp:从 SensorReading 对象中提取时间戳,并转换为毫秒。
4. 窗口操作
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. 侧输出流
resultStream.getSideOutput(new OutputTag[(String, Double, Long)]("late")).print("late")
  • 获取侧输出流中的数据并打印。
6. 结果输出
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):将迟到数据写入侧输出流,以便后续处理。
5. Flink 的 API
  • DataStream API:用于处理无界数据流。
  • Table API & SQL:用于处理结构化数据。
  • State & Checkpoint:用于管理状态和实现容错。
6. 流处理应用场景
  • 实时监控:如传感器数据监控、日志分析。
  • 实时推荐:如电商平台的实时推荐系统。
  • 金融风控:如实时交易监控和欺诈检测。

进一步学习

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

渣渣盟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值