学习目标
1.Time与window
2.EventTime与window
3.Flink的容错(checkpoint)
4.Flink的状态管理 state
1.Time与window
1.1Time
Event Time: 数据产生的时间
Ingestion Time: 是数据进入 Flink 的时间。
Processing Time :算子操作使用的时间
1.2window
Window 可以分成两类:
- CountWindow: 按照指定的数据条数生成一个 Window, 与时间无关。
- TimeWindow: 按照时间生成 Window。
滚动窗口( Tumbling Window)
将数据依据固定的窗口长度对数据进行切片。
特点: 时间对齐, 窗口长度固定, 没有重叠 。
滑动窗口( Sliding Window)
滑动窗口是固定窗口的更广义的一种形式, 滑动窗口由固定的窗口长度和滑动间隔组成。
特点: 时间对齐, 窗口长度固定, 有重叠。
分为三种情况:
窗口大小>滑动距离 =====》有重叠,重复消费数据
窗口大小=滑动距离 =====》没有重复,不重叠,也不丢失
窗口大小<滑动距离 =====》丢失数据
会话窗口( Session Window)
由一系列事件组合一个指定时间长度的 timeout 间隙组成, 类似于 web 应用的 session,
也 就是一段时间没有接收到新数据就会生成新的窗口。
特点: 时间无对齐。
Window API
CountWindow案例
package com.czxy.flink.stream.window
import org.apache.flink.streaming.api.scala.{DataStream, KeyedStream, StreamExecutionEnvironment, WindowedStream}
import org.apache.flink.streaming.api.windowing.windows.GlobalWindow
/**
* 思路步骤:
* 1.获取执行环境
* 2.创建 SocketSource
* 3.对 stream 进行处理并按 key 聚合
* 4.countWindow 操作
* 5.执行聚合操作
* 6.将聚合数据输出
* 7.执行程序
*/
object StreamCountWindow {
def main(args: Array[String]): Unit = {
//1.创建执行环境
val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
//2.构建数据源,创建 SocketSource
val socketSource: DataStream[String] = env.socketTextStream("node01",9999)
//3.对 stream 进行处理并按 key 聚合
import org.apache.flink.api.scala._
val groupKeyedStream: KeyedStream[(String, Int), String] = socketSource.flatMap(x=>x.split(" ")).map((_,1)).keyBy(_._1)
//4.引入countWindow 操作,每5条数据计算一次
val countWindowStream: WindowedStream[(String, Int), String, GlobalWindow] = groupKeyedStream.countWindow(5)
//5.执行聚合操作
val resultDataStream: DataStream[(String, Int)] = countWindowStream.sum(1)
//6.将聚合数据输出
resultDataStream.print()
//7.执行程序
env.execute("StreamCountWindow")
}
}
TimeWindow案例
package com.czxy.flink.stream.window
import org.apache.flink.streaming.api.scala.{DataStream, KeyedStream, StreamExecutionEnvironment, WindowedStream}
import org.apache.flink.streaming.api.windowing.time.Time
import org.apache.flink.streaming.api.windowing.windows.TimeWindow
/**
* 思路步骤:
* 1.获取执行环境
* 2.创建你 socket 链接获取数据
* 3.进行数据转换处理并按 key 聚合
* 4.引入 timeWindow
* 5.执行聚合操作
* 6.输出打印数据
* 7.执行程序
*/
object StreamTimeWindow {
def main(args: Array[String]): Unit = {
//1.获取执行环境
val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
//2.创建你 socket 链接获取数据
val socketSource = env.socketTextStream("node01",9999)
//3.进行数据转换处理并按 key 聚合
import org.apache.flink.api.scala._
val groupKeyedStream: KeyedStream[(String, Int), String] = socketSource.flatMap(x=>x.split(" ")).map((_,1)).keyBy(_._1)
//4.引入 滚动窗口timeWindow,每3秒钟计算一次
//val timeWindowStream: WindowedStream[(String, Int), String, TimeWindow] = groupKeyedStream.timeWindow(Time.seconds(3))
//4.引入 滑动窗口timeWindow,窗口大小为10秒,滑动距离为5秒=>重复消费
val timeWindowStream: WindowedStream[(String, Int), String, TimeWindow] = groupKeyedStream.timeWindow(Time.seconds(10),Time.seconds(5))
//5.执行聚合操作
val result: DataStream[(String, Int)] = timeWindowStream.sum(1)
//6.打印输出
result.print()
//7.执行程序
env.execute("StreamTimeWindow")
}
}
SreamReduceWindow案例
package com.czxy.flink.stream.window
import org.apache.flink.streaming.api.scala.{DataStream, StreamExecutionEnvironment, WindowedStream}
import org.apache.flink.streaming.api.windowing.time.Time
import org.apache.flink.streaming.api.windowing.windows.TimeWindow
object StreamReduceWindow {
def main(args: Array[String]): Unit = {
//1.获取执行环境
val env = StreamExecutionEnvironment.getExecutionEnvironment
//2.构建数据集
val socketSource = env.socketTextStream("node01",9999)
//3.分组
import org.apache.flink.api.scala._
val group = socketSource.flatMap(x=>x.split(" ")).map((_,1)).keyBy(_._1)
//4.引入窗口timeWindow
val timeWindow: WindowedStream[(String, Int), String, TimeWindow] = group.timeWindow(Time.seconds(5))
//5.聚合操作
val result: DataStream[(String, Int)] = timeWindow.reduce((v1, v2)=>(v1._1,v1._2+v2._2))
//6.输出打印
result.print()
//7.执行程序
env.execute()
}
}
Window Apply案例
package com.czxy.flink.stream.window
import org.apache.flink.streaming.api.scala.function.RichWindowFunction
import org.apache.flink.streaming.api.scala.{DataStream, KeyedStream, StreamExecutionEnvironment, WindowedStream}
import org.apache.flink.streaming.api.windowing.time.Time
import org.apache.flink.streaming.api.windowing.windows.TimeWindow
import org.apache.flink.util.Collector
//使用 apply 方法来实现单词统计
object StreamApplyWindow {
def main(args: Array[String]): Unit = {
/**
* 思路步骤:
* 1) 获取流处理运行环境
* 2) 构建 socket 流数据源, 并指定 IP 地址和端口号
* 3) 对接收到的数据转换成单词元组
* 4) 使用 keyBy 进行分流( 分组)
* 5) 使用 timeWindow 指定窗口的长度( 每 3 秒计算一次)
* 6) 实现一个 WindowFunction 匿名内部类
* a. apply 方法中实现聚合计算
* b. 使用 Collector.collect 收集数据
* 7) 打印输出
* 8) 启动执行
* 9) 在 Linux 中, 使用 nc -lk 端口号 监听端口, 并发送单词
*/
//1.获取流处理运行环境
val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
//2. 构建 socket 流数据源, 并指定 IP 地址和端口号
val socketSource: DataStream[String] = env.socketTextStream("node01",9999)
//3.对接收到的数据转换成单词元组
import org.apache.flink.api.scala._
val wordAndOne: DataStream[(String, Int)] = socketSource.flatMap(x=>x.split(" ")).map((_,1))
//4.使用 keyBy 进行分流( 分组)
val groupKeyedStream: KeyedStream[(String, Int), String] = wordAndOne.keyBy(_._1)
//5.使用 timeWindow 指定窗口的长度( 每 3 秒计算一次)
val timeWindow: WindowedStream[(String, Int), String, TimeWindow] = groupKeyedStream.timeWindow(Time.seconds(3))
//6.实现一个 WindowFunction 匿名内部类
val result: DataStream[(String, Int)] = timeWindow.apply(new RichWindowFunction[(String, Int), (String, Int), String, TimeWindow] {
override def apply(key: String, window: TimeWindow, input: Iterable[(String, Int)], out: Collector[(String, Int)]): Unit = {
//apply 方法中实现聚合计算
val tuple: (String, Int) = input.reduce((v1, v2) => (v1._1, v1._2 + v2._2))
//使用 Collector.collect 收集数据
out.collect(tuple)
}
})
//7.打印输出
result.print()
//8.执行程序
env.execute("StreamApplyWindow")
}
}
StreamAggregationWindow案例
package com.czxy.flink.stream.window
import org.apache.flink.streaming.api.scala.{DataStream, KeyedStream, StreamExecutionEnvironment, WindowedStream}
import org.apache.flink.streaming.api.windowing.time.Time
import org.apache.flink.streaming.api.windowing.windows.TimeWindow
object StreamAggregationWindow {
def main(args: Array[String]): Unit = {
//1.获取流处理运行环境
val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
//2. 构建 socket 流数据源, 并指定 IP 地址和端口号
val socketSource: DataStream[String] = env.socketTextStream("node01",9999)
//3.对接收到的数据转换成单词元组
import org.apache.flink.api.scala._
val wordAndOne: DataStream[(String, Int)] = socketSource.flatMap(x=>x.split(" ")).map((_,1))
//4.使用 keyBy 进行分流( 分组)
val groupKeyedStream: KeyedStream[(String, Int), String] = wordAndOne.keyBy(_._1)
//5.使用 timeWindow 指定窗口的长度( 每 3 秒计算一次)
val timeWindow: WindowedStream[(String, Int), String, TimeWindow] = groupKeyedStream.timeWindow(Time.seconds(3))
//6.执行聚合操作
val result: DataStream[(String, Int)] = timeWindow.max(1)
//7.打印输出
result.print()
//8.执行程序
env.execute(this.getClass.getSimpleName)
}
}
2.EventTime 与 Window
2.1EventTime 的引入
如果要使用 EventTime, 那么需要引入 EventTime 的时间属性
在这里插入代码片val env = StreamExecutionEnvironment.getExecutionEnvironment
// 从调用时刻开始给 env 创建的每一个 stream 追加时间特征
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTim
2.2 Watermark
Watermark 是用于处理乱序事件的, 而正确的处理乱序事件, 通常用 Watermark 机制结合 window 来实现 。
2.3EventTimeWindow API
滚动窗口
package com.czxy.flink.stream.waterwindow
import org.apache.flink.streaming.api.TimeCharacteristic
import org.apache.flink.streaming.api.functions.timestamps.BoundedOutOfOrdernessTimestampExtractor
import org.apache.flink.streaming.api.scala.{DataStream, KeyedStream, StreamExecutionEnvironment, WindowedStream}
import org.apache.flink.streaming.api.windowing.assigners.TumblingEventTimeWindows
import org.apache.flink.streaming.api.windowing.time.Time
import org.apache.flink.streaming.api.windowing.windows.TimeWindow
object TumblingEventTimeWindowsDemo {
def main(args: Array[String]): Unit = {
/**
* 步骤:
* 1.创建流处理环境
* 2.设置EventTime
* 3.构建数据源
* 4.设置水印
* 5.逻辑处理
* 6.引入滚动窗口TumblingEventTimeWindows
* 7.聚合操作
* 8.输出打印
* 9.执行程序
*/
//1.创建流处理环境
val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
//2.设置EventTime
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
//3.构建数据源
//数据格式: 1000 hello
val socketSource = env.socketTextStream("node01",9999)
//4.设置水印
val waterMark: DataStream[String] = socketSource.assignTimestampsAndWatermarks(new BoundedOutOfOrdernessTimestampExtractor[String](Time.seconds(0)) {
override def extractTimestamp(element: String): Long = {
val eventTime: Long = element.split(" ")(0).toLong
eventTime
}
})
//5.逻辑处理
import org.apache.flink.api.scala._
val groupStream: KeyedStream[(String, Int), String] = waterMark.map(x=>x.split(" ")(1)).map((_,1)).keyBy(_._1)
//6.引入滚动窗口TumblingEventTimeWindows
val windowStream: WindowedStream[(String, Int), String, TimeWindow] = groupStream.window(TumblingEventTimeWindows.of(Time.seconds(3)))
//7.聚合操作
val result: DataStream[(String, Int)] = windowStream.sum(1)
//8.输出打印
result.print()
//9.执行程序
env.execute(this.getClass.getSimpleName)
}
}
滑动窗口
package com.czxy.flink.stream.waterwindow
import org.apache.flink.streaming.api.TimeCharacteristic
import org.apache.flink.streaming.api.functions.timestamps.BoundedOutOfOrdernessTimestampExtractor
import org.apache.flink.streaming.api.scala.{DataStream, KeyedStream, StreamExecutionEnvironment, WindowedStream}
import org.apache.flink.streaming.api.windowing.assigners.SlidingEventTimeWindows
import org.apache.flink.streaming.api.windowing.time.Time
import org.apache.flink.streaming.api.windowing.windows.TimeWindow
object SlidingEventTimeWindowsDemo {
def main(args: Array[String]): Unit = {
/** * 步骤:
* 1.创建流处理环境
* 2.设置EventTime
* 3.构建数据源
* 4.设置水印
* 5.逻辑处理
* 6.引入滑动窗口SlidingEventTimeWindows
* 7.聚合操作
* 8.输出打印
* 9.执行程序
*/
//1.创建流处理环境
val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
//2.设置EventTime
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
//3.构建数据源
//数据格式为:1000 hello
val socketSource: DataStream[String] = env.socketTextStream("node01", 9999)
//4.设置水印
val waterMark: DataStream[String] = socketSource.assignTimestampsAndWatermarks(new BoundedOutOfOrdernessTimestampExtractor[String](Time.seconds(0)) {
override def extractTimestamp(element: String): Long = {
val eventTime: Long = element.split(" ")(0).toLong
eventTime
}
})
//5.逻辑处理
import org.apache.flink.api.scala._
val groupStream: KeyedStream[(String, Int), String] = waterMark.map(x => x.split(" ")(1)).map((_, 1)).keyBy(_._1)
//6.引入滑动窗口SlidingEventTimeWindows
val windowStream: WindowedStream[(String, Int), String, TimeWindow] = groupStream.window(SlidingEventTimeWindows.of(Time.seconds(5), Time.seconds(2)))
//7.聚合计算
val result: DataStream[(String, Int)] = windowStream.sum(1)
//8.打印输出
result.print()
env.execute(this.getClass.getSimpleName)
}
}
会话窗口
package com.czxy.flink.stream.waterwindow
import org.apache.flink.streaming.api.TimeCharacteristic
import org.apache.flink.streaming.api.functions.timestamps.BoundedOutOfOrdernessTimestampExtractor
import org.apache.flink.streaming.api.scala.{DataStream, KeyedStream, StreamExecutionEnvironment, WindowedStream}
import org.apache.flink.streaming.api.windowing.assigners.EventTimeSessionWindows
import org.apache.flink.streaming.api.windowing.time.Time
import org.apache.flink.streaming.api.windowing.windows.TimeWindow
object EventTimeSessionWindowsDemo {
def main(args: Array[String]): Unit = {
/**
* 步骤:
* 1.创建流处理环境
* 2.设置EventTime
* 3.构建数据源
* 4.设置水印
* 5.逻辑处理
* 6.引入会话窗口EventTimeSessionWindows
* 7.聚合操作
* 8.输出打印
*/
//1.创建流处理环境
val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
//2.设置EventTime
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
//3.构建数据源
//数据格式为:1000 hello
val socketSource: DataStream[String] = env.socketTextStream("node01", 9999)
//4.设置水印
val waterMark: DataStream[String] = socketSource.assignTimestampsAndWatermarks(new BoundedOutOfOrdernessTimestampExtractor[String](Time.seconds(0)) {
override def extractTimestamp(element: String): Long = {
val eventTime: Long = element.split(" ")(0).toLong
eventTime
}
})
//5.逻辑处理
import org.apache.flink.api.scala._
val groupStream: KeyedStream[(String, Int), String] = waterMark.map(x => x.split(" ")(1)).map((_, 1)).keyBy(_._1)
//6.引入会话窗口EventTimeSessionWindows
val windowStream: WindowedStream[(String, Int), String, TimeWindow] = groupStream.window(EventTimeSessionWindows.withGap(Time.seconds(3)))
//7.聚合计算
val result = windowStream.sum(1)
//8.打印输出
result.print()
//9.执行程序
env.execute("EventTimeSessionWindowsDemo")
}
}
3.Flink 的容错checkpoint
3.1Checkpoint 介绍
- CheckpointCoordinator 周期性的向该流应用的所有 source 算子发送 barrier。
- 当某个 source 算子收到一个 barrier 时, 便暂停数据处理过程, 然后将自己的当前状态制作成快照, 并保存到指定的持久化存储中, 最后向CheckpointCoordinator 报告 自己快照制作情况, 同时向自身所有下游算子广播该 barrier, 恢复数据处理 。
- 下游算子收到 barrier 之后, 会暂停自己的数据处理过程, 然后将自身的相关状态制作成快照, 并保存到指定的持久化存储中, 最后向 CheckpointCoordinator 报告自身 快照情况, 同时向自身所有下游算子广播该 barrier, 恢复数据处理。
- 每个算子按照步骤 3 不断制作快照并向下游广播, 直到最后 barrier 传递到 sink 算子, 快照制作完成。
- 当 CheckpointCoordinator 收到所有算子的报告之后, 认为该周期的快照制作成功; 否则, 如果在规定的时间内没有收到所有算子的报告, 则认为本周期快照制作失败。如果一个算子有两个输入源, 则暂时阻塞先收到 barrier 的输入源, 等到第二个输入源相同编号的 barrier 到来时, 再制作自身快照并向下游广播该 barrier。
3.2持久化存储
3.2.1MemStateBackend(默认)
3.2.2FsStateBackend(建议使用)
3.2.3RocksDBStateBackend
3.3Checkpoint 的高级选项
3.4Flink 的重启策略
1.固定延迟重启策略(Fixed Delay Restart Strategy)
2.失败率重启策略
3.失败率重启策略
4.Flink 的状态管理
1.State-Keyed State
2.State-Operator State
3.Broadcast State
奥利给!下次是flink完结了!