checkpoint:基于Chandy-Lamport算法,实现了分布式一致性快照,提供了一致性的语义。
State:丰富的State API。ValueState,ListState,MapState,BroadcastState.
Time:实现了Watemark机制,乱序数据处理,迟到数据容忍。
Window:开箱即用的滚动、滑动、会话窗口。以及灵活的自定义窗口。
预定义Source:
自定义Source
Sink:
算子:
map;FlatMap;Filte;KeyBy;Reduce;
定义:根据自定义的条件将部分数据发送到一个或多个额外的输出流中,这些额外的输出流就是侧道输出流。
用途:
Union
操作可以将多个流合并为一个流。这些流的数据类型必须相同。
Connect
操作允许将两个不同类型的流连接在一起,形成一个新的连接流
Window可以分成两类:
CountWindow:按照指定的数据条数生成一个Window,与时间无关。
滚动计数窗口,每隔N条数据,统计前N条数据
滑动计数窗口,每隔N条数据,统计前M条数据
TimeWindow:按照时间生成Window。(重点)
滚动时间窗口,每隔N时间,统计前N时间范围内的数据,窗口长度N,滑动距离N
滑动时间窗口,每隔N时间,统计前M时间范围内的数据,窗口长度M,滑动距离N
会话窗口,按照会话划定的窗口
Time的分类 (时间语义):
EventTime:事件(数据)时间,是事件/数据真真正正发生时/产生时的时间
IngestionTime:摄入时间,是事件/数据到达流处理系统的时间
ProcessingTime:处理时间,是事件/数据被处理/计算时的系统的时间
对于 event_time 中数据迟到的处理(数据乱序):
加水印,水印(watermark)就是一个时间戳,Flink可以给数据流添加水印,可以理解为:收到一条消息后,额外给这个消息添加了一个时间字段,这就是添加水印,一般人为添加的消息的水印都会比当前消息的事件时间小一些。
状态可以理解为-- 历史计算结果
无状态计算:
有状态计算:
checkpoint:基于Chandy-Lamport算法,实现了分布式一致性快照,提供了一致性的语义。
设置Checkpoint:
public class CheckPointDemo {
public static void main(String[] args) throws Exception {
//1. env-准备环境
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setRuntimeMode(RuntimeExecutionMode.AUTOMATIC);
// 在windows运行,将数据提交hdfs,会出现权限问题,使用这个语句解决。
System.setProperty("HADOOP_USER_NAME", "root");
// 在这个基础之上,添加快照
// 第一句:开启快照,每隔1s保存一次快照
env.enableCheckpointing(1000);
// 第二句:设置快照保存的位置
env.setStateBackend(new FsStateBackend("hdfs://bigdata01:9820/flink/checkpoint"));
// 第三句: 通过webui的cancel按钮,取消flink的job时,不删除HDFS的checkpoint目录
env.getCheckpointConfig().enableExternalizedCheckpoints(CheckpointConfig.ExternalizedCheckpointCleanup.RETAIN_ON_CANCELLATION);
//2. source-加载数据
DataStreamSource dataStreamSource = env.socketTextStream("localhost", 9999);
SingleOutputStreamOperator> mapStream = dataStreamSource.map(new MapFunction>() {
@Override
public Tuple2 map(String s) throws Exception {
String[] arr = s.split(",");
return Tuple2.of(arr[0], Integer.valueOf(arr[1]));
}
});
//3. transformation-数据处理转换
SingleOutputStreamOperator> result = mapStream.keyBy(0).sum(1);
result.print();
//4. sink-数据输出
//5. execute-执行
env.execute();
}
}
就是一个流在运行过程中,假如出现了程序异常问题,可以进行重启,比如,在代码中人为添加一些异常
代码演示:
public class Demo02 {
public static void main(String[] args) throws Exception {
//1. env-准备环境
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setRuntimeMode(RuntimeExecutionMode.AUTOMATIC);
// 代码中不能有checkpoint,不是说checkpoint不好,而是太好了,它已经自带重试机制了。而且是无限重启的
// 通过如下方式可将重试机制关掉
// env.setRestartStrategy(RestartStrategies.noRestart());
//
// 两种办法
// 第一种办法:重试3次,每一次间隔10S
//env.setRestartStrategy(RestartStrategies.fixedDelayRestart(3, Time.of(10, TimeUnit.SECONDS)));
// 第二种写法:在2分钟内,重启3次,每次间隔10s
env.setRestartStrategy(
RestartStrategies.failureRateRestart(3,
Time.of(2,TimeUnit.MINUTES),
Time.of(5,TimeUnit.SECONDS))
);
//2. source-加载数据
DataStreamSource streamSource = env.socketTextStream("bigdata01", 8899);
streamSource.map(new MapFunction>() {
@Override
public Tuple2 map(String value) throws Exception {
String[] arr = value.split(",");
String word = arr[0];
if(word.equals("bug")){
throw new Exception("有异常,服务会挂掉.....");
}
// 将一个字符串变为int类型
int num = Integer.valueOf(arr[1]);
// 第二种将字符串变为数字的方法
System.out.println(Integer.parseInt(arr[1]));
Tuple2 tuple2 = new Tuple2<>(word,num);
// 还有什么方法? 第二种创建tuple的方法
Tuple2 tuple2_2 = Tuple2.of(word,num);
return tuple2;
}
}).keyBy(tuple->tuple.f0).sum(1).print();
//3. transformation-数据处理转换
//4. sink-数据输出
//5. execute-执行
env.execute();
}
}
所谓的维表Join: 进入Flink的数据,需要关联另外一些存储设备的数据,才能计算出来结果,那么存储在外部设备上的表称之为维表,可能存储在mysql也可能存储在hbase 等。维表一般的特点是变化比较慢。
FlinkSQL-kafka
package com.bigdata.day08;
import org.apache.flink.api.common.RuntimeExecutionMode;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.table.api.Table;
import org.apache.flink.table.api.TableResult;
import org.apache.flink.table.api.bridge.java.StreamTableEnvironment;
/**
* @基本功能:
* @program:FlinkDemo
* @author: 闫哥
* @create:2023-11-28 11:00:51
**/
public class _02KafkaConnectorDemo {
public static void main(String[] args) throws Exception {
//1. env-准备环境
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
StreamTableEnvironment tEnv = StreamTableEnvironment.create(env);
// 如果是建表语句:executeSql 这个返回值是TableResult
// 如果是查询语句:sqlQuery 这个返回的是Table (有用)
// 新建一个表,用于存储 kafka消息
TableResult tableResult = tEnv.executeSql("CREATE TABLE table1 (\n" +
" `user_id` int,\n" +
" `page_id` int,\n" +
" `status` STRING\n" +
") WITH (\n" +
" 'connector' = 'kafka',\n" +
" 'topic' = 'topic1',\n" +
" 'properties.bootstrap.servers' = 'bigdata01:9092',\n" +
" 'properties.group.id' = 'testGroup',\n" +
" 'scan.startup.mode' = 'latest-offset',\n" +
" 'format' = 'json'\n" +
")");
// 新建一个表,用于存储kafka中的topic2中的数据
tEnv.executeSql("CREATE TABLE table2 (\n" +
" `user_id` int,\n" +
" `page_id` int,\n" +
" `status` STRING\n" +
") WITH (\n" +
" 'connector' = 'kafka',\n" +
" 'topic' = 'topic2',\n" +
" 'properties.bootstrap.servers' = 'bigdata01:9092',\n" +
" 'format' = 'json'\n" +
")");
tEnv.executeSql("insert into table2 select * from table1 where status ='success'");
// 以上代码已经写完了,下面是两个步骤分开的写法
//TODO 3.transformation/查询
// Table result = tEnv.sqlQuery("select user_id,page_id,status from table1 where status='success'");
//输出到Kafka DDL
// tEnv.executeSql("insert into table2 select * from " + result);
//2. source-加载数据
//3. transformation-数据处理转换
//4. sink-数据输出
//5. execute-执行
// env.execute();
}
}