目录
package api
import java.util
import java.util.concurrent.TimeUnit
import org.apache.flink.api.common.functions.{RichFlatMapFunction, RichMapFunction}
import org.apache.flink.api.common.restartstrategy.RestartStrategies
import org.apache.flink.api.common.state.StateTtlConfig.RocksdbCompactFilterCleanupStrategy
import org.apache.flink.api.common.state.{ListStateDescriptor, MapStateDescriptor, ReducingStateDescriptor, ValueState, ValueStateDescriptor}
import org.apache.flink.api.common.time.Time
import org.apache.flink.configuration.Configuration
import org.apache.flink.runtime.state.filesystem.FsStateBackend
import org.apache.flink.runtime.state.memory.MemoryStateBackend
import org.apache.flink.streaming.api.CheckpointingMode
import org.apache.flink.streaming.api.scala._
import org.apache.flink.util.Collector
/**
*
* @PROJECT_NAME: Flink
* @PACKAGE_NAME: api
* @author: 赵嘉盟-HONOR
* @data: 2025-5-15 21:06
* @DESCRIPTION
*
*/
object State {
def main(args: Array[String]): Unit = {
val env=StreamExecutionEnvironment.getExecutionEnvironment
env.setParallelism(1)
//状态后端配置
//env.setStateBackend( new MemoryStateBackend()) //内存状态后端
//env.setStateBackend( new FsStateBackend("保存地址",异步快照)) //文件状态后端
//env.setStateBackend ( new RocksDBStateBackend())
//检查点配置
env.enableCheckpointing(1000L) //启用检查点
env.getCheckpointConfig.setCheckpointingMode(CheckpointingMode.EXACTLY_ONCE) //配置检查点算法
env.getCheckpointConfig.setCheckpointTimeout(60000L) //设置超时时间
env.getCheckpointConfig.setMaxConcurrentCheckpoints(5) //最多允许几个检查点同时工作
env.getCheckpointConfig.setMinPauseBetweenCheckpoints(500) //两个检查点最小间隔(与max冲突)
//env.getCheckpointConfig.setPreferCheckpointForRecovery(true) //是否只用检查点(默认false)
env.getCheckpointConfig.setTolerableCheckpointFailureNumber(3) //最大失败次数
//重启策略配置
env.setRestartStrategy(RestartStrategies.fixedDelayRestart(3,10000L)) //重启三次,每次间隔10s
env.setRestartStrategy(RestartStrategies.failureRateRestart(5,Time.of(5,TimeUnit.MINUTES),Time.of(10,TimeUnit.SECONDS)))//五分钟内重启五次,每次间隔10s
val inputStream=env.socketTextStream("localhost",7777)
val dataStream=inputStream
.map( data=>{
val arr=data.split(",")
SensorReading(arr(0),arr(1).toLong,arr(2).toDouble)
})
//需求:对于温度传感器温度值跳变,超过十度报警
val alertState=dataStream
.keyBy(_.id)
//.flatMap( new TempChangeAlert(10.0))
.flatMapWithState[(String,Double,Double),Double]{
case (data:SensorReading,None) =>( List.empty ,Some(data.temperature)) //第一次没有状态的初始化
case (data,lastTemp:Some[Double])=>{
val diff=(data.temperature-lastTemp.get).abs
if(diff>10.0)
(List((data.id,lastTemp.get,data.temperature)),Some(data.temperature)) //超过阈值报警
else
(List.empty,Some(data.temperature)) //没有超过阈值更新状态
}
}
alertState.print()
env.execute("state")
}
}
//实现自定义RichFlatMapFunction
class TempChangeAlert(threshold :Double) extends RichFlatMapFunction[SensorReading,(String,Double,Double)]{
//定义状态保存上一次的温度值
lazy val lastTempState=getRuntimeContext.getState( new ValueStateDescriptor[Double]("last_temp",classOf[Double]))
var first = false
override def flatMap(in: SensorReading, collector: Collector[(String, Double, Double)]): Unit = {
//判断是否第一次运行
if(first==false){
first=true
lastTempState.update(in.temperature)
}
//获取上一次的温度值
val lastTemp=lastTempState.value()
//跟最新的温度值作比较
val diff=(in.temperature-lastTemp).abs
if (diff > threshold){
collector.collect((in.id,lastTemp,in.temperature))
}
//更新状态
lastTempState.update(in.temperature)
}
}
//keyed satte 测试:必须定义在RichFunction中,因为需要运行时上下文
class MyRichMapper1 extends RichMapFunction[SensorReading,String]{
var valueState:ValueState[Double]=_
lazy val listState=getRuntimeContext.getListState( new ListStateDescriptor[Int]("list",classOf[Int])) //listState简易定义方法
lazy val mapState=getRuntimeContext.getMapState(new MapStateDescriptor[String,Double]("map",classOf[String],classOf[Double]))
lazy val reduceState=getRuntimeContext.getReducingState(new ReducingStateDescriptor[SensorReading]("reduce",new MyReduceFunction,classOf[SensorReading]))
override def open(parameters: Configuration): Unit = {
valueState=getRuntimeContext.getState( new ValueStateDescriptor[Double]("value",classOf[Double]))
}
override def map(in: SensorReading): String = {
//状态的读写
val myV=valueState.value() //读取
valueState.update(in.temperature) //更改
//listState方法
listState.add(2)
val list = new util.ArrayList[Int]()
list.add(3)
list.add(4)
listState.addAll(list) //追加
listState.update(list) //覆盖
listState.get() //获取
//map方法
mapState.contains("sensor_1") //判断存在
mapState.get("sensor_1") //获取value
mapState.put("sensor_1",1.2) //修改value
//reduce方法
reduceState.get() //聚合后的值
reduceState.add(in) //聚合新输入的值
in.id
}
}
这段代码是一个基于 Apache Flink 的流处理程序,主要功能是通过 状态管理 和 检查点机制 实现传感器数据的温度跳变检测。以下是代码的总结和原理拓展:
1. 代码结构总结
主要功能模块
- 数据源读取:
- 从 Socket 读取数据流(
localhost:7777
),数据格式为id,timestamp,temperature
。
- 从 Socket 读取数据流(
- 数据处理:
- 使用
map()
方法将输入数据转换为SensorReading
对象。 - 使用 Keyed State 和 FlatMapWithState 实现温度跳变检测,当温度变化超过阈值时触发报警。
- 使用
- 状态管理:
- 使用
ValueState
保存上一次的温度值,并通过RichFlatMapFunction
实现状态的管理和更新。
- 使用
- 检查点与重启策略:
- 配置检查点机制和重启策略,确保程序的容错性和可靠性。
核心类与方法
SensorReading
:- 样例类,用于表示传感器的数据(ID、时间戳、温度)。
RichFlatMapFunction
:- Flink 提供的富函数,可以访问状态、运行时上下文等底层功能。
ValueState
:- 用于保存单个值的状态,通过
ValueStateDescriptor
定义。
- 用于保存单个值的状态,通过
FlatMapWithState
:- 结合状态管理的流处理函数,支持状态初始化、更新和输出。
CheckpointConfig
:- 配置检查点机制,包括检查点间隔、超时时间、并发检查点等。
RestartStrategies
:- 配置重启策略,包括固定延迟重启和失败率重启。
2. 代码原理拓展
Flink 状态管理的核心概念
-
Keyed State:
- 与 Key 绑定的状态,只能在 KeyedStream 中使用。
- 支持的类型包括
ValueState
、ListState
、MapState
、ReducingState
等。 - 通过
getRuntimeContext().getState()
获取状态。
-
Operator State:
- 与算子绑定的状态,所有并行实例共享相同的状态。
- 支持的类型包括
ListState
、BroadcastState
等。
-
状态后端(State Backend):
- 负责状态的存储和访问,支持的类型包括:
MemoryStateBackend
:状态存储在内存中,适用于小规模数据。FsStateBackend
:状态存储在文件系统中,适用于大规模数据。RocksDBStateBackend
:状态存储在 RocksDB 中,适用于超大规模数据。
- 负责状态的存储和访问,支持的类型包括:
-
状态生命周期:
- 状态在算子初始化时创建,在算子关闭时销毁。
- 通过
open()
和close()
方法管理状态的初始化和清理。
Flink 检查点机制的核心概念
-
检查点(Checkpoint):
- 定期将状态保存到持久化存储中,用于故障恢复。
- 支持
EXACTLY_ONCE
和AT_LEAST_ONCE
两种语义。
-
检查点配置:
enableCheckpointing()
:启用检查点,并设置检查点间隔。setCheckpointTimeout()
:设置检查点超时时间。setMaxConcurrentCheckpoints()
:设置最大并发检查点数。setMinPauseBetweenCheckpoints()
:设置检查点之间的最小间隔。
-
重启策略(Restart Strategy):
fixedDelayRestart()
:固定延迟重启,指定重启次数和延迟时间。failureRateRestart()
:失败率重启,指定时间窗口内的最大失败次数和延迟时间。
3. 代码优化与扩展
优化建议
-
状态清理:
- 使用
StateTtlConfig
配置状态的生存时间(TTL),自动清理过期状态。
- 使用
-
状态序列化:
- 优化状态的序列化方式,减少状态存储和传输的开销。
-
检查点性能:
- 根据数据量和硬件资源,调整检查点间隔和并发检查点数,平衡性能和可靠性。
功能扩展
-
复杂状态管理:
- 使用
MapState
或ListState
实现更复杂的状态管理逻辑。
- 使用
-
事件时间处理:
- 结合事件时间和水位线(Watermark),实现基于时间的状态管理。
-
状态查询:
- 使用
QueryableState
实现状态的实时查询,便于监控和调试。
- 使用
-
自定义状态后端:
- 实现自定义的状态后端,支持特定的存储系统或优化策略。
4. 示例代码扩展
以下是一个扩展示例,展示如何使用 StateTtlConfig
配置状态的生存时间:
import org.apache.flink.api.common.state.StateTtlConfig
import org.apache.flink.api.common.time.Time
// 配置状态的生存时间(TTL)
val ttlConfig = StateTtlConfig
.newBuilder(Time.minutes(5)) // 状态生存时间为 5 分钟
.setUpdateType(StateTtlConfig.UpdateType.OnCreateAndWrite) // 每次创建和写入时更新 TTL
.setStateVisibility(StateTtlConfig.StateVisibility.NeverReturnExpired) // 不返回过期状态
.build()
// 定义 ValueStateDescriptor 并设置 TTL
val stateDescriptor = new ValueStateDescriptor[Double]("last_temp", classOf[Double])
stateDescriptor.enableTimeToLive(ttlConfig)
5. 总结
- 核心功能:
- 通过
ValueState
和RichFlatMapFunction
实现温度跳变检测,当温度变化超过阈值时触发报警。
- 通过
- 状态管理:
- 使用
Keyed State
保存和管理状态,支持ValueState
、ListState
、MapState
等多种类型。
- 使用
- 检查点与重启策略:
- 配置检查点机制和重启策略,确保程序的容错性和可靠性。
- 优化与扩展:
- 通过状态清理、序列化优化、检查点性能调优等功能优化程序,通过复杂状态管理、事件时间处理、状态查询等功能扩展程序。
通过理解 Flink 的状态管理和检查点机制,可以更好地开发和优化流处理程序。