1. 一句话描述Flink
有状态的流式计算框架
2. 手写Wordcount程序
package com.atguigu.flink.wordcount;
import org.apache.flink.api.common.functions.FlatMapFunction;
import org.apache.flink.api.common.typeinfo.TypeHint;
import org.apache.flink.api.common.typeinfo.TypeInformation;
import org.apache.flink.api.common.typeinfo.Types;
import org.apache.flink.api.java.functions.KeySelector;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.api.java.utils.ParameterTool;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.KeyedStream;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.util.Collector;
/**
* @author WEIYUNHUI
* @date 2023/6/10 11:15
*
* Lambda表达式写法
*
* 泛型擦除问题:
* The generic type parameters of 'Collector' are missing.
* In many cases lambda methods don't provide enough information for automatic type extraction when Java generics are involved.
*
* The return type of function 'main(Flink05_LambdaWordCount.java:34)' could not be determined automatically,due to type erasure.
* You can give type information hints by using the returns(...) method on the result of the transformation call,
* or by letting your function implement the 'ResultTypeQueryable' interface.
*
*
* 解决方案:
* 在调用完转换算子后, 如果存在泛型擦除问题, 可以通过调用returns()方法, 明确指定类型即可。
* returns(Class<T> typeClass) : 明确指定类型。通过Class来指定。一般适用于明确的类(类中不包含泛型)
* returns(TypeHint<T> typeHint): 适用于任何情况。 通过TypeHit来明确指定类型。
* returns(TypeInformation<T> typeInfo) : 适用于任何情况,通过TypeInformation来明确类型。
*
*
*/
public class Flink05_LambdaWordCount {
public static void main(String[] args) throws Exception {
// 1. 创建执行环境
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
//设置并行度
env.setParallelism(1) ;
// 2.读取数据
// DataStreamSource => DataStream
DataStreamSource<String> ds = env.socketTextStream("hadoop102", 8888) ;
// 3.转换处理
// 3.1 切分数据, 处理成(word, 1)
SingleOutputStreamOperator<Tuple2<String, Integer>> flatMapDs = ds.flatMap(
//lambda写法
(String line, Collector<Tuple2<String, Integer>> out) -> {
String[] words = line.split(" ");
for (String word : words) {
out.collect(Tuple2.of(word, 1));
}
}
).returns(Types.TUPLE(Types.STRING , Types.INT));
/*
.returns(new TypeHint<Tuple2<String, Integer>>() {
@Override
public TypeInformation<Tuple2<String, Integer>> getTypeInfo() {
return super.getTypeInfo();
}
});
*/
// 3.2 按照单词分组
KeyedStream<Tuple2<String, Integer>, String> keyByDs = flatMapDs.keyBy(
tuple -> tuple.f0
);
// 3.3 汇总
// 使用Tuple中的第二个元素进行汇总
SingleOutputStreamOperator<Tuple2<String, Integer>> sumDs = keyByDs.sum(1);
// 4. 输出结果
sumDs.print();
// 5. 启动执行
env.execute();
}
}
3. 简述Flink的三种部署模式及区别
1. 会话模式
可以提交多个job到集群,要求每个job执行时间短
2. 单作业模式
1个job对应一个集群,main方法在client端执行
3. 应用模式
1个job对应一个集群,main方法在jobManager执行
区别:单作业模式已经不用了,在生产中,需要根据实际情况选择模式,如果job比较大,运行时间长,选择应用模式,如果job比较多,运行时间短,选择会话模式
4. 简述并行度的概念及设置方式
并行度是指算子的并行程度
1. 可以在算子后面设置
2. 在代码中,全局设置
3. 在提交job时设置
4. 在配置文件中设置
5. 简述算子链的概念、合并算子链的条件、如何禁用算子链
概念:把多个算子合并,就称为算子链
条件:2个算子的并行度相同,分发规则为forword
禁用:
1. 可以在代码中使用startNewChain,开启一个新链
2. 可以在算子后面调用禁用方法
3. 可以在代码中全局禁用
6. 一个作业的并行度如何确定?一个作业需要多少个slot如何确定?
1. 并行度最大的算子的并行度,就是一个作业的并行度
2. slot,根据并行度最大的算子的并行度确定
7. 手绘Yarn应用模式作业提交流程
8.Flink StreamExecutionEnvironment的执行模式有几种,分别是什么?
3种
- AUTOMATIC
- STREAMING
- BATCH
9. kafka消费者的相关参数解释
key.deserializer:key的反序列化方式
value.deserializer:value的反序列化方式
bootstrap.servers:集群地址
group.id:消费组id
auto.offset.reset:自动重置偏移量,头或者尾
enable.auto.commit:消费者自动提交
auto.commit.interval.ms:自动提交间隔
10.Flink消费kafka数据的代码
package com.atguigu.flink.datastream.source;
import org.apache.flink.api.common.eventtime.WatermarkStrategy;
import org.apache.flink.api.common.serialization.SimpleStringSchema;
import org.apache.flink.connector.file.src.FileSource;
import org.apache.flink.connector.file.src.reader.TextLineInputFormat;
import org.apache.flink.connector.kafka.source.KafkaSource;
import org.apache.flink.connector.kafka.source.enumerator.initializer.OffsetsInitializer;
import org.apache.flink.core.fs.Path;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.consumer.OffsetResetStrategy;
import org.apache.kafka.common.TopicPartition;
import org.apache.kafka.common.internals.Topic;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
/**
* @author WEIYUNHUI
* @date 2023/6/13 9:13
* <p>
* Kafka Connector :
* 1. 从kafka中消费数据
* 1.1 消费者对象:
* KafkaConsumer
* 1.2 消费者相关配置:
* key/value的反序列化器:
* key.deserializer => org.apache.kafka.common.serialization.StringDeserializer
* value.deserializer => org.apache.kafka.common.serialization.StringDeserializer
* 集群地址:
* bootstrap.servers => hadoop102:9092,hadoop103:9092,hadoop104:9092
* 消费者组:
* group.id => 随便
* offset自动提交:
* enable.auto.commit => true / false
* offset自动提交的间隔:
* auto.commit.interval.ms => 5000
* offset重置:
* auto.offset.reset => latest(尾) | earliest(头)
* 重置的情况:
* 1. 新的消费者组 , 之前没有消费过数据, 也就没有对应的offset
* 2. 旧的消费者组 , 之前有消费过数据,但是对应的offset已经在kafka中不存在(可能的原因是Kafka默认7天会清理数据)
* <p>
* 1.3 Kafka提供的消费者的配置类 ConsumerConfig
* <p>
* 2. 往kafka生产数据
*/
public class Flink03_KafkaSource {
public static void main(String[] args) {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(1);
/*
封装要从哪个主题那个分区的offset进行消费
TopicPartition topicPartition = new TopicPartition("topicA", 3);
HashMap<TopicPartition, Long> offsets = new HashMap<>();
offsets.put(topicPartition , 40L) ;
*/
KafkaSource<String> kafkaSource =
KafkaSource.<String>builder()
.setBootstrapServers("hadoop102:9092,hadoop103:9092")
.setGroupId("flink1")
.setTopics("topicA")
.setValueOnlyDeserializer(new SimpleStringSchema()) //仅针对于没有key的消息
//.setDeserializer() //针对于key和value
// 默认使用记录的offset进行消费 ,如果涉及到重置offset, 选择重置到尾
//.setStartingOffsets(OffsetsInitializer.committedOffsets(OffsetResetStrategy.LATEST))
// 默认使用记录的offset进行消费 ,如果涉及到重置offset, 选择重置到头
.setStartingOffsets(OffsetsInitializer.committedOffsets(OffsetResetStrategy.EARLIEST))
// 指定Offset消费
//.setStartingOffsets(OffsetsInitializer.offsets(offsets))
//如果其他的设置没有对应的方法, 统一使用.setProperty( 配置项 , 配置值)
// .setProperty("enable.auto.commit" , "true" )
.setProperty(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "true")
//.setProperty("auto.commit.interval.ms" , "6000")
.setProperty(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, "6000")
.build();
DataStreamSource<String> ds = env.fromSource(kafkaSource, WatermarkStrategy.noWatermarks(), "kafkaSource");
ds.print();
try {
env.execute();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
11.Flink提供的基本转换算子有哪些?分别解释每个算子的功能
- map:一对一转换
- flatmap:扁平化
- filter:过滤
12.Flink提供的基本聚合算子有哪些?分别解释每个算子的功能
- keyBy:分组
- sum:和
- max:最大值,只有一个字段变化
- min:最小值,只有一个字段变化
- maxBy:最大值,整条数据
- minBy:最小值,整条数据
- reduce:归约聚合
13.解释reduce算子的聚合原理
两两聚合
14.Flink支持的source有哪些?
- kafka
- 文件
- 集合
- 自定义
- Socket
- 数据生成器
15.Flink提供的分区算子有哪些?分别解释分区的规则
-
随机分配(Random):随机
-
轮询分配(Round-Robin):轮询
-
重缩放(Rescale):
重缩放分区和轮询分区非常相似。当调用rescale()方法时,其实底层也是使用Round-Robin算法进行轮询,但是只会将数据轮询发送到下游并行任务的一部分中。rescale的做法是分成小团体,发牌人只给自己团体内的所有人轮流发牌。 -
广播(Broadcast):将输入数据复制并发送到下游算子的所有并行任务中去。
16.简述RichFunction的功能
算子的生命周期,上下文对象
17.Flink如何实现分流
使用侧输出流
18.Flink如何使用侧输出流
定义一个OutputTag,调用context.output()方法
19.Flink合流的union和connect如何用,区别是什么?
区别:union要求两个流的数据类型必须相同,connect可以不同
20.kafka生产者相关参数解释
key.serializer:key的序列化方式
value.serializer:value的序列化方式
bootstrap.servers:集群地址
retries:重试次数
batch.size:缓冲区大小
linger.ms:超过多长时间就发送缓冲区的数据
acks:应答级别
21.kafka生产者分区规则
- 如果指定了分区,那么直接存
- 如果指定了key,那么用key的hash
- 否则用粘性分区
22.Flink中的时间语义有哪些?
- 事件时间
- 处理时间
23.Watermark的意义是什么?
基于processingTime的语义场景下,Watermark无意义。不需要!
基于EventTime的语义场景下,Watermark就是当前的时间。
意义: 通知算子,当前几点了。
1)基于EventTime的窗口运算
2)基于EventTime的定时器
3)CEP(特殊事件处理)
24.Watermark是什么?
watermark是一种特殊的数据。
可以在源头(source算子)产生,可以在中间环节产生,随着数据向下游流动。
下游的算子收到watermark时,就知道当前系统时间是多少了(EventTime语义场景)。
25.如何构造WaterMark的生成策略?
系统提供:
连续: WatermarkStrategy.<Event>forBoundedOutOfOrderness(0)
WatermarkStrategy.<WaterSensor>forMonotonousTimestamps()
乱序: WatermarkStrategy.<Event>forBoundedOutOfOrderness(1)
自定义:
自定义类 implements WatermarkGenerator.
参考Flink01_UserDefineWaterMark
26.多并行度下watermark的推进策略是什么?
上游Task向下游的每一个Task广播水印。
下游Task收到上游多个Task的水印,取最小的作为当前水印。
27.如果某一个并行度的watermark不推进导致整个下游的watermark无法推进,如何处理?
.withIdleness(Duration.ofSeconds(10)) //10秒没有推进,就取消资格
package com.atguigu.flink.timeAndwindow;
import com.atguigu.flink.func.WaterSensorMapFunction;
import com.atguigu.flink.pojo.WaterSensor;
import com.atguigu.flink.utils.MyUtil;
import org.apache.flink.api.common.eventtime.WatermarkStrategy;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.windowing.ProcessAllWindowFunction;
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;
import org.apache.flink.util.Collector;
import java.time.Duration;
/**
* Created by 黄凯 on 2023/6/20 0020 21:18
*
* @author 黄凯
* 永远相信美好的事情总会发生.
* <p>
* 如果某一个并行度的watermark不推进导致整个下游的watermark无法推荐,如何处理?
* *
* * 查看水印?
* * WEBUI: 当前看不到,看到下游收到的水印。
* *
* * ------------------------------
* * 6个MapTask,只有1个能收到数据,但是6个人都要发送水印。
* * 设置Task发送水印的资格。
* * 如果一个Task,长期无法收到数据,导致水印无法更新,推进。就取消这个Task的水印发送资格!
* *
* *
* * Idle: 空闲,赋闲。
*/
public class Flink13_MultiParilismWaterMark {
public static void main(String[] args) {
Configuration conf = new Configuration();
conf.setInteger("rest.port", 3333);
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(conf);
//修改水印的默认的发送频率
env.getConfig().setAutoWatermarkInterval(2000);
//模拟多并行度
env.setParallelism(6);
//声明水印策略
WatermarkStrategy<WaterSensor> watermarkStrategy = WatermarkStrategy
//第一部分: 水印的特征。 连续,乱序
.<WaterSensor>forMonotonousTimestamps()
//第二部分: 如何从数据中提取事件时间
.withTimestampAssigner((e, ts) -> e.getTs())
.withIdleness(Duration.ofSeconds(10)) //10秒没有推进,就取消资格
;
env
.socketTextStream("127.0.0.1", 8888)
//全局汇总 所有的数据都给下游的第一个Task
.global()
.map(new WaterSensorMapFunction())
.assignTimestampsAndWatermarks(watermarkStrategy)
/*
数据落入哪个窗口不看水印(没有半毛钱关系)。只看 数据的EventTime和窗口的范围。
[0,4999):
[5000,9999):
*/
.windowAll(TumblingEventTimeWindows.of(Time.milliseconds(5000)))
.process(new ProcessAllWindowFunction<WaterSensor, String, TimeWindow>() {
@Override
public void process(ProcessAllWindowFunction<WaterSensor, String, TimeWindow>.Context context,
Iterable<WaterSensor> iterable,
Collector<String> collector) throws Exception {
//输出窗口中的所有的元素
collector.collect(MyUtil.parseToList(iterable).toString());
}
})
.print();
try {
env.execute();
} catch (Exception e) {
e.printStackTrace();
}
}
}
28.如何处理乱序的场景?
1)推迟时钟(时间 | 水印)
WatermarkStrategy.<Event>forBoundedOutOfOrderness(n)
2)延迟窗口的关闭时间
Window.allowLateness(Time n)
3)使用侧流接收数据,之后再酌情处理。
29.什么是flink中的定时器?如何制定定时器?
定时器: 在固定的时间运行的一段程序。
指定定时器:
1)无keyBy不定时。
只有KeyedStream才能制定定时器
2)获取时钟服务
context.getTimeService().registXXXXTimer(时间)
30.什么是状态?
在flink的处理函数中声明的用于供Event读写的变量。
31.Flink中的状态分类?如何声明和使用状态?
RawState: 用户自己声明的变量。
ManagedState: flink提供了状态类型
KeyedState: 对KeyedStream进行处理,生命的状态都是KeyedState,每个key都有自己的状态。
各玩各的,互不干扰。
单个值: ValueState
Map集合: MapState
List集合: ListState
聚合功能: 输入类型=输出类型 ReducingState
输入类型=输出类型 AggregatingState
在处理函数对象中声明属性,在open()方法中从context中获取状态,赋值。
OperateState: 一个算子的Task上,所有处理的数据共用一个状态。
List集合:
ListState
元素的分配方式: 均匀分配
UnionListState
元素的分配方式: 每个Task获取全量
用于更新配置信息: BroadCastState
32.熟练掌握常见状态的API操作。
读: value()
写: add(), addAll(), update()
清空: clear()
33.Flink中状态的分类,列举每种类型的状态
- RawState
- 自定义
- ManagedState
- KeyedState
- ValueState
- ListState
- MapState
- ReducingState
- AggregatingState
- OperateState
- ListState
- UnionListState
- BroadcastState
- KeyedState
34.什么是Barrier?Barrier对齐和Barrier不对齐怎么理解?
栅栏,开始备份状态的标记
- 对齐
一个下游对多个上游,需要上游的所有barrier到齐,才能备份
2.不对齐
一个下游对多个上游,不需要上游的所有barrier到齐,才能备份,只要有一个到达,就可以备份
35.检查点和保存点的使用场景是什么?
- 检查点
- 用于系统意外故障恢复
- 保存点
- 用于系统升级,需要手动备份,通过命令
36.一致性的级别有几种,分别进行解释
- 精确一次(Exactly-Once)
- 数据不丢,也不重复
- 至少一次(At-Least-Once)
- 会重复
- 最多一次(At-Most-Once)
- 会丢
37.如何保证端到端一致性,从输入端,计算过程,输出端分别说明
- 输入端
- 数据可重放
- 计算过程
- Flink开启检查点,精确一次
- 输出端
- 幂等 或 事务
38.解释动态表和持续查询的概念
- 动态表
- 表中的数据会发生变化
- 持续查询
- 来一条数据查询一次
39.创建表处理环境
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
//将流式执行环境转换成表执行环境
StreamTableEnvironment tableEnv = StreamTableEnvironment.create(env);
40.创建表
package com.atguigu.flink.sql.connector;
import org.apache.flink.table.api.EnvironmentSettings;
import org.apache.flink.table.api.Table;
import org.apache.flink.table.api.TableEnvironment;
/**
* Created by on 2023/6/25 0025 18:22
*
* @author
* 永远相信美好的事情总会发生.
*
* FileConnector : 基于文件读写。
*/
public class Flink02_FileConnector {
public static void main(String[] args) {
//1. 创建表环境
TableEnvironment tableEnv = TableEnvironment.create(EnvironmentSettings.newInstance().build());
//2. File Connector read
String readSql=
" create table t1 (" +
" id STRING , " +
" vc INT , " +
" ts BIGINT, " +
//" `file.path` STRING NOT NULL METADATA ," +
//" `file.name` STRING NOT NULL METADATA " +
" `file.size` BIGINT NOT NULL METADATA " +
") WITH ( " +
" 'connector' = 'filesystem' , " +
" 'path' = 'input/ws.txt', " +
" 'format' = 'csv' " +
")" ;
tableEnv.executeSql(readSql);
Table table = tableEnv.sqlQuery("select id ,vc ,ts , `file.size` as fs from t1 where vc > 200");
tableEnv.createTemporaryView("t2",table);
//File Connector write
String writeSql =
" create table t3 (" +
" id STRING , " +
" vc INT , " +
" ts BIGINT, " +
" fs BIGINT " +
") WITH ( " +
" 'connector' = 'filesystem' , " +
" 'path' = 'output' ," +
" 'format' = 'json' " +
")" ;
tableEnv.executeSql(writeSql);
//从t2查询数据,写入到t3表
tableEnv.executeSql("insert into t3 select * from t2");
}
}
41.流表转换
//读取数据
DataStreamSource<Event> stream = env.addSource(new ClickSource());
//将流转换成表
Table table = tableEnv.fromDataStream(stream);
42. hbase-概念解释
- rowkey
- 一行数据的key,唯一标识
- region
- 被切分的若干行数据
- store
- 在切分行的基础上,再对列族进行切分
- namespace
- 库
- table
- 表
- row
- 一行
- column
- 一列
- columnFamily
- 列族
- timeStamp
- 版本
- cell
- 一个单元格的数据
43.简述master和regionserver的职责
- master
- 负责通过zk监控Reginaserver进程状态,同时是所有元数据变化的接口。内部启动监控执行region的故障转移和拆分的线程
- regionserver
- 主要负责数据cell的处理。同时在执行区域的拆分和合并的时候,由regionserver来实际执行。
44.Hbase shell namespace相关命令
-
create_namespace 'bigdata'
-
list_namespace
45.Hbase shell table ddl相关命令
-
create 'bigdata:student', {NAME => 'info', VERSIONS => 5}, {NAME => 'msg'}
-
create 'student1','info'
-
list
-
describe 'student1'
-
alter 'student1', {NAME => 'f1', VERSIONS => 3}
-
alter 'student1', NAME => 'f1', METHOD => 'delete'
-
alter 'student1', 'delete' => 'f1'
-
disable 'student1'
-
drop 'student1'
46.Hbase shell table dml相关命令
-
put 'bigdata:student','1001','info:name','zhangsan'
-
get 'bigdata:student','1001'
-
get 'bigdata:student','1001' , {COLUMN => 'info:name'}
-
get 'bigdata:student','1001' , {COLUMN => 'info:name', VERSIONS => 6}
-
scan 'bigdata:student',{STARTROW => '1001',STOPROW => '1002'}
-
delete 'bigdata:student','1001','info:name'
-
deleteall 'bigdata:student','1001','info:name'