package com.atguigu.Dstate;
import com.atguigu.Fbeans.SensorReading;
import org.apache.flink.api.common.functions.MapFunction;
import org.apache.flink.api.common.functions.RichMapFunction;
import org.apache.flink.api.common.state.*;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.streaming.api.checkpoint.ListCheckpointed;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import java.util.Collections;
import java.util.List;
/**
*在flink中,所谓的状态就是一个本地变量,flink会自动使用managed state进行转态管理,你也可以使用raw state进行
* 自定义开发。相比raw state,managered state支持更加丰富的数据类型,且不需要自己序列化。状态的定义都是在
* 算子的匿名函数内。
*operator state:
* 状态的访问范围是算子内,匿名函数要实现ListCheckpointed接口和其内部的两个方法。也可以实现其他的接口,用得不多。
* 分类:
* 列表状态:将状态表示为一组数据的列表。
* 联合列表状态:将状态表示为一组数据的列表。只是从检查点故障恢复时和列表状态不一样。
* 广播状态:如果一个算子有多项任务,而它的每项任务状态又都相同,那么这种特殊情况最适合应用广播状态。
* keyed state:
* 状态访问范围只是跨算子的同一个key,使用前必须keyBy。
* 因为需要通过getRuntimeContext来给状态初始化,所以匿名函数需要继承富函数。
* 分类:
* 值状态:将状态表示为单个值。private ValueState list; getRuntimeContext().getState(...)
* 列表状态:将状态表示为一组数据的列表。 private ListState list; getListState(....)
* 映射状态:将状态表示为一组key-value对。private MapState list; getMapState(....)
* 集合状态(reducing/aggregating):将状态表示为一个用于聚合操作的列表。
* private ReducingState list; getReducingState(....)
状态常合定时器一起使用,定时器触发后应当清除状态。如果配置了allowedLateness,在事件时间到了
allowedLateness规定的延迟时间,再清除状态。
*状态后端:
* 负责状态的存储、访问和维护,检查点的写入都由一个可插拔的状态后端组件决定。
* 分类:
* MemoryStateBackend() state存tm内存, 检查点存jm内存。仅开发使用。
* FsStateBackend("检查点uri地址") state存tm内存, 检查点存外部文件系统。生产可用。
* RocksDBStateBackend("检查点uri地址") state存tm上RocksDB数据库(内存+磁盘),检查点存RocksDB数据库或者外部文件系统,
* 生产可用。需要引入RocksDB的依赖。适用于超大状态且对状态读写性能要求不高的的作业。
*env.setStateBackend(new MemoryStateBackend()) 其他的类似,可以传参指定异步还是同步,或者外部文件系统路径。
* 这种设置方法已经显示过期。
*/
public class AStateTest {
public static void main(String[] args) throws Exception {
//创建环境
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(1);
//读取数据并包装成pojo
DataStreamSource<String> inputStream = env. socketTextStream("localhost",7777);
DataStream<SensorReading> mapStream = inputStream.map(line -> {
String[] splits = line.split(",");
return new SensorReading(new String(splits[0]), new Long(splits[1]), new Double(splits[2]));
});
/**
* 通过map方法测试算子状态和键控状态
* 键控状态必须先分组
*
*/
SingleOutputStreamOperator<Integer> result1 = mapStream.map(new MyOperatorState());//算子状态
SingleOutputStreamOperator<Integer> result2 = mapStream.keyBy(SensorReading::getId)
.map(new MyKeyedValueState());
//输出与执行
result1.print("测试算子状态");
result2.print("测试键控状态");
env.execute("测试状态");
}
/**
* 算子状态的方法,必须实现ListCheckpointed接口,当然也可以其他的。用到再百度。
* ListCheckpointed接口的泛型是保存的状态的类型。
*
*/
public static class MyOperatorState implements MapFunction<SensorReading,Integer>, ListCheckpointed<Integer> {
private Integer count=0; //定义状态
@Override
public Integer map(SensorReading sen) throws Exception {
return count++; //每次状态加一
}
//保存状态的方法
@Override
public List<Integer> snapshotState(long checkpointId, long timestamp) throws Exception {
return Collections.singletonList(count);
}
//恢复状态的方法
@Override
public void restoreState(List<Integer> list) throws Exception {
for (Integer num:list) { //应用重启如何恢复状态
count+=num;
}
}
}
/**
* 键控状态的类,必须继承富函数,因为通过getRuntimeContext获取上下文给状态属性赋值。
* 以下实现的功能,按分组key统计个数,用键控状态中的值状态实现。
*
*/
public static class MyKeyedValueState extends RichMapFunction<SensorReading, Integer> {
private ValueState<Integer> keyCount; //值状态
// private ListState<String> listState; //列表状态
// private MapState<String,Double> mapState; //映射状态
// private ReducingState<SensorReading> reducingState; //聚合状态
@Override
public void open(Configuration parameters) throws Exception {
keyCount =getRuntimeContext().getState(new ValueStateDescriptor<Integer>("key_count",Integer.class));
// listState=getRuntimeContext().getListState(new ListStateDescriptor<String>("list_state",String.class));
// mapState=getRuntimeContext().getMapState(new MapStateDescriptor<String, Double>("map_state",String.class,Double.class));
// reducingState=getRuntimeContext().getReducingState(new ReducingStateDescriptor<SensorReading>());
}
@Override
public Integer map(SensorReading sen) throws Exception {
// /*
// ListState的获取和更新
// */
// for(String str:listState.get()){
// System.out.println(str);
// }
// listState.add("java");
// /*
// MapState的获取和更新
// */
// mapState.get("1");
// mapState.put("2",12.3);
/*
ValueState获取和更新
*/
Integer count = keyCount.value(); //获取count的值
if(count!=null)
keyCount.update(++count); //添加count值
return count;
}
}
}
小案例
package com.atguigu.Dstate;
import com.atguigu.Fbeans.SensorReading;
import org.apache.flink.api.common.functions.RichFlatMapFunction;
import org.apache.flink.api.common.state.ValueState;
import org.apache.flink.api.common.state.ValueStateDescriptor;
import org.apache.flink.api.java.tuple.Tuple3;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.util.Collector;
/**
* 测试温度跳变的一个小案例:
* 温差超过10度输出报警信息:id、当前温度、前一次温度
*/
public class BKeyedStateTest {
public static void main(String[] args) throws Exception {
//创建环境
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(1);
//读取数据并包装成pojo
DataStreamSource<String> inputStream = env. socketTextStream("localhost",7777);
DataStream<SensorReading> mapStream = inputStream.map(line -> {
String[] splits = line.split(",");
return new SensorReading(new String(splits[0]), new Long(splits[1]), new Double(splits[2]));
});
SingleOutputStreamOperator<Tuple3<String, Double, Double>> result = mapStream.keyBy(SensorReading::getId)
.flatMap(new KeyedStateFunction(10.0));
//输出执行
result.print("测试温度跳变的一个小案例");
env.execute("测试温度跳变的一个小案例");
}
public static class KeyedStateFunction extends RichFlatMapFunction<SensorReading, Tuple3<String,Double,Double>>{
private Double threshold; //温度跳变的阈值
private ValueState<Double> valueState; //保存上一次的温度值
public KeyedStateFunction(Double threshold){
this.threshold=threshold;
}
@Override
public void open(Configuration parameters) throws Exception {
valueState=getRuntimeContext().getState(new ValueStateDescriptor<Double>("温度监控",Double.class));
}
@Override
public void flatMap(SensorReading sen, Collector<Tuple3<String, Double, Double>> collector) throws Exception {
Double value = valueState.value();//获取上一次温度值
if(value!=null){ //判断两次的温度差值
Double diff=Math.abs(sen.getTemperature()-value);
if(diff > threshold){
collector.collect(new Tuple3<>(sen.getId(),sen.getTemperature(),value));
}
}
//将当前温度值加入状态
valueState.update(sen.getTemperature());
}
@Override
public void close() throws Exception {
valueState.clear();//清理状态
}
}
}