flink checkpoint 窗口函数

1.checkpoint

object checkpointMain {

  def main(args: Array[String]): Unit = {


    //创建执行的环境
    val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
    //设置并行度
    env.setParallelism(1)
    env.enableCheckpointing(1000) //开启1s保存一个快照
    //env.setStateBackend(new FsStateBackend("hdfs://192.168.1.150:9000/flink-checkpoint")) //设置保存的路径  todo:新的版本的flink还没有和Hadoop集成
    env.getCheckpointConfig.setCheckpointingMode(CheckpointingMode.EXACTLY_ONCE) //最少或者最多执行一次
    env.getCheckpointConfig.setMaxConcurrentCheckpoints(1) //设置同时可以执行多少checkpoint


val orderDataStream = env.addSource(new RichSourceFunction[(Int, String, Double)] {
      var runing = true

      override def run(sourceContext: SourceFunction.SourceContext[(Int, String, Double)]): Unit = {
        while (runing) {
          for (i <- 0 until 10000) {
            sourceContext.collect {
              (i, "zhangsan", RandomStringUtils.randomNumeric(3).toDouble)
            }
            TimeUnit.SECONDS.sleep(1)
          }
        }
      }
  override def cancel(): Unit = {
        runing = false
      }
    })
  orderDataStream.keyBy(_._2)
      .timeWindow(Time.seconds(3))
      .apply(new myWindowFunction )
      .print()
    //执行job
    env.execute()
  }
  class myWindowFunction extends  WindowFunction[(Int, String, Double), Double, String,
    TimeWindow] with  ListCheckpointed[UDFState] {

    var totalMoney = 0.0
    override def apply(key: String, window: TimeWindow, input: Iterable[(Int, String, Double)], out: Collector[Double]): Unit = {

      for (elem <- input) {
        totalMoney =totalMoney+elem._3
      }
     out.collect(totalMoney)
    }
    override def snapshotState(l: Long, l1: Long): util.List[UDFState] = {
      val states = new util.ArrayList[UDFState]()
      states
    }
    override def restoreState(list: util.List[UDFState]): Unit = {
      val totalMoney: Double = list.get(0).totalMoney
    }
  }
  case class UDFState(var totalMoney:Double)
}

2.窗口函数

IngestionTime    flink的source接收到数据的时间

ProcessingTime   flink的节点执行某个operation的时间

 

窗口函数可以分为两大类 CountWindow(指定条数生成一个window) TimeWindow(设置好的时间生成一个window)

TimeWindow可以分为:滚动窗口,滑动窗口和会话窗口

滚动窗口:

滚动窗口分配器将每个元素分配到一个指定窗口大小的窗口中,滚动窗口有一个固定的大小,并且不会出现重叠。例如:如果你指定了一个5分钟大小的滚动窗口,窗口的创建如下图所示:

 

滑动窗口

滑动窗口分配器将元素分配到固定长度的窗口中,与滚动窗口类似,窗口的大小由窗口大小参数来配置,另一个窗口滑动参数控制滑动窗口开始的频率。因此,滑动窗口如果滑动参数小于窗口大小的话,窗口是可以重叠的,在这种情况下元素会被分配到多个窗口中。

 

会话窗口: 会话结束窗口就会消失

 

//创建执行的环境
    val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
    //设置并行度
  var resout=  env.setParallelism(1)
       env.socketTextStream("localhost",9999)
         .flatMap(x=>x.split(","))
         .map((_,1))
         .keyBy(_._1)
         .timeWindow(Time.seconds(10),Time.seconds(5))   
           .sum(1)                                                  
           .print()
    //执行job
    env.execute()

注意:Window 执行 nc -L -p 9999 

当窗口计算时间大于了窗口时间 数据就回丢失

当窗口计算时间小于了窗口时间,数据就会重复计算

3.apply方法

实现一个windowFunction

指定该类型泛型[输入数据类型, 输出数据类型, keyBy中使用分组字段的类型, 窗口类型]

//创建执行的环境
    val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
    //设置并行度
    env.setParallelism(1)
      var  resout =env.socketTextStream("localhost",9999)
           .flatMap(_.split((",")))
           .map((_,1))
           .keyBy(_._1)      //keyby 相当groupby 分组 分流
           .timeWindow(Time.seconds(5)) //设置5秒处理一个窗口
           .apply(new WindowFunction[(String,Int),(String,Int),String,TimeWindow] {
         override def apply(key: String, window: TimeWindow, input: Iterable[(String, Int)], out: Collector[(String, Int)]): Unit = {
          var inputDS= input.reduce{
             (r1,r2) =>(r1._1,r1._2+r2._2)}
           out.collect(inputDS)
         }
       }).print()
    //执行job
    env.execute()}

4.Watermark 案例

def main(args: Array[String]): Unit = {
    //创建执行的环境
    val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
    //设置并行度
    env.setParallelism(1)
    //设置处理时间是EventTime
    env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
    //创建自定义的数据源
    var orderDataStream =env.addSource(new myRichSourceFunction )
    orderDataStream.print() //测试输出
    //添加水印
    val orderDS: DataStream[Order] = orderDataStream.assignTimestampsAndWatermarks(new MyorderDataStream)
    
    orderDS.keyBy(_.userId)
        .timeWindow(Time.seconds(5))
        .reduce(
          (r1,r2)=> Order(r2.oderID,r2.userId,r2.monery+r1.monery,0))
        .writeAsText("./output/2.txt")
    //执行job
    env.execute()
case class  Order (oderID:String,userId:Int,monery:Long,timestamp:Long)
class myRichSourceFunction extends  RichSourceFunction[Order]{

  var running =true
  override def run(sourceContext: SourceFunction.SourceContext[Order]): Unit ={
    while(running){
      //订单ID UUID
      // 随机生成用户ID
      //随机生成订单金额
      //时间戳为系统时间
      //每隔1S秒生成一个订单
      var order =Order(UUID.randomUUID().toString,Random.nextInt(3),Random.nextInt(101),new Date().getTime)
      sourceContext.collect(order)
      //TimeUnit.SECONDS.sleep(1)
    }
  }
  override def cancel(): Unit = {
    running=false
  }
}
class MyorderDataStream  extends AssignerWithPeriodicWatermarks[Order]{

  var currentTimestamp =0L
  val delayTime =2000

  override def getCurrentWatermark: Watermark = {
       //水印的时间,当前事件时间,当前系统时间
       val watermark = new Watermark(currentTimestamp-delayTime)
       val dateFormat: FastDateFormat = FastDateFormat.getInstance("HH:mm:ss")
        println(
          s"当前的系统时间:${dateFormat.format(watermark.getTimestamp)}," +
          s"当前的事件时间:${dateFormat.format(currentTimestamp)}, " +
          s"当前系统时间:${dateFormat.format(System.currentTimeMillis())}"
        )
        watermark
  }

  override def extractTimestamp(t: Order, l: Long): Long = {
    val timestamp: Long = t.timestamp
    currentTimestamp=Math.max(currentTimestamp,timestamp)
    currentTimestamp
  }
}

 

<think>好的,我现在需要回答用户关于Flink窗口机制及其与状态机制关联的问题。用户之前已经询问过Flink的状态机制,现在想进一步了解窗口机制以及两者的关系。首先,我需要确保自己对这两个概念有清晰的理解,然后结合提供的引用和之前的知识来组织回答。 首先,Flink的窗口机制是处理无界流数据的关键,允许将无限的数据流划分为有限的窗口进行计算。根据引用[2]和[3],窗口的触发计算由触发器控制,窗口分配器如TumblingEventTimeWindows定义窗口的类型和大小。状态机制则是用来管理这些窗口处理过程中产生的中间状态,比如聚合结果或累积的数据,确保容错和恢复。 接下来,我需要将窗口机制和状态机制的关系梳理清楚。窗口操作需要维护每个窗口的状态,例如窗口内的元素计数、聚合结果等,这些状态会被Flink的状态后端管理。当窗口触发计算后,如何处理这些状态?可能涉及到状态的清除或保留,以便后续处理。同时,检查点机制(引用[1]和[4])会定期保存这些状态,保证故障恢复时的Exactly-Once语义。 用户提供的引用中,引用[1]提到检查点机制和状态容错,引用[2]和[3]涉及窗口触发器和分配器,引用[4]提到用户自定义状态。需要将这些内容整合到回答中,说明窗口如何依赖状态机制来存储中间数据,而状态机制如何通过检查点保证窗口计算的容错性。 可能需要注意的细节包括窗口的生命周期管理、状态过期(TTL)如何与窗口结合,以及不同的窗口类型(如滚动、滑动、会话)对状态存储的影响。此外,引用[2]中的触发器控制窗口计算时机,这可能影响状态的使用和清理策略。 需要确保回答结构清晰,分点阐述窗口机制、状态机制的角色,以及两者的协同工作。同时,生成的相关问题应引导用户进一步探索窗口类型、触发器优化或容错配置等。</think>### Flink 窗口机制与状态机制的关联解析 Flink 的窗口机制与状态机制协同工作,共同支撑流处理中对无界数据的**有限集计算**和**容错保障**。以下是两者的核心关联及运作原理: --- #### 一、窗口机制的核心组件 1. **窗口分配器(Window Assigner)** - **作用**:将数据流按规则(时间、数量等)划分为有限窗口,如滚动窗口、滑动窗口等。 - **示例**(引用[3]): ```java stream.keyBy(...) .window(TumblingEventTimeWindows.of(Time.seconds(5))) // 5秒滚动事件时间窗口 .aggregate(...); ``` 2. **窗口触发器(Trigger)** - **作用**:决定何时触发窗口计算(如基于时间、数据量或自定义条件)(引用[2])。 - **示例**: ```java .trigger(ContinuousProcessingTimeTrigger.of(Time.seconds(10))) // 每10秒触发一次 ``` 3. **窗口函数(Window Function)** - **作用**:对窗口内数据进行聚合(如 `sum`、`reduce`)或全量处理(如 `ProcessWindowFunction`)。 --- #### 二、状态机制在窗口中的角色 窗口处理过程中的**中间数据**(如聚合结果、窗口元素列表)需通过状态机制存储和管理: 1. **状态存储类型** - **Keyed State**:每个窗口的中间结果按键(Key)独立存储,例如: - `ValueState`:存储单个聚合值(如累加结果)。 - `ListState`:存储窗口内所有元素(需全量处理时)。 2. **状态生命周期** - **创建**:窗口首次接收数据时初始化状态。 - **更新**:数据进入窗口时更新状态(如累加值)。 - **清除**:窗口触发计算后,根据配置(如延迟时间)清理状态[^1]。 3. **容错与恢复** - **检查点(Checkpoint)**:定期将窗口状态持久化到外部存储(如 HDFS),确保故障时恢复至最近一致性状态(引用[1][4])。 - **Exactly-Once 语义**:通过状态快照和重放源数据保证窗口计算的精确一次处理[^1]。 --- #### 三、协同工作流程 以事件时间滚动窗口为例: 1. **数据分配**:数据按事件时间分配到对应窗口。 2. **状态更新**:窗口内数据触发聚合函数,更新 `ValueState` 中的累加值。 3. **触发计算**:水位线(Watermark)越过窗口结束时间时,触发器调用窗口函数输出结果。 4. **状态清理**:窗口关闭后,状态根据 TTL 配置自动清除(避免资源泄漏)。 --- #### 四、关键优化策略 1. **状态后端选择** - **RocksDBStateBackend**:适用于大窗口状态(如小时级窗口),通过磁盘存储降低内存压力[^3]。 2. **增量检查点** - 仅持久化窗口状态的增量变化,减少检查点耗时(尤其适合长窗口场景)。 3. **延迟数据处理** - 允许设置窗口允许延迟(Allowed Lateness),在此期间的迟到数据仍可更新状态并重新触发计算。 --- #### 五、典型问题与解决方案 1. **窗口状态膨胀** - **场景**:滑动窗口重叠率高,导致同一数据存入多个窗口状态。 - **解决**:优化窗口大小/滑动步长,或使用 `MapState` 按窗口 ID 分片存储。 2. **触发器频繁触发** - **场景**:基于数据量的触发器导致计算频繁,状态更新压力大。 - **解决**:改用时间触发器或合并触发条件(如 `CountTrigger` + `TimeTrigger`)。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值