Flink笔记
2.Flink学习笔记
2.1流式处理对比
学习Spark Streaming后对实时处理有了浅显的认识,Flink主要也是用来做实时计算的,不过两者对实时计算的处理方式有所不同。
![在这里插入图片描述](https://img-blog.csdnimg.cn/bd546e6f132047d0b88247c97679f8a5.png
图2.1 Spark Streaming的微批处理
Spark Streaming是微批处理,根据时间将数据流划分为很小的数据集合再进行批处理。
图2.2 Flink的流式处理
而Flink是流处理,以一个事件为单位划分计算,是标准的流执行模式。
2.2 Flink核心概念
图2.3 Flink的运行过程
JobManager:JobManager是Flink集群中任务管理和调度的核心,是控制应用执行的主进程。
包含3个的组件:JobMaster负责处理单独的作业(Job)、ResourceManager主要负责资源的分配和管理、Dispatcher用来提交应用,并且负责为每一个新提交的作业启动一个新的JobMaster 组件。
TaskManager:TaskManager是Flink中的工作进程,数据流的具体计算就是它来做的。
图2.4 数据流并行视图
2.2.1并行度
在Flink执行过程中,每一个算子可以包含一个或多个子任务,这些子任务在不同的线程、不同的物理机或不同的容器中完全独立地执行。一个特定算子的子任务的个数被称之为其并行度。可以在算子后跟着调用setParallelism()方法,来设置当前算子的并行度。
2.2.2算子链
一个数据流在算子之间传输数据的形式可以是: 1、一对一的直通模式,数据流维护着分区以及元素的顺序,这种关系类似于Spark中的窄依赖。2、也可以是打乱的重分区模式,数据流的分区会发生改变,每一个算子的子任务,会根据数据传输的策略,把数据发送到不同的下游目标任务。类似于Spark中的shuffle。
合并算子链:在Flink中,并行度相同的一对一(one to one)算子操作,可以直接链接在一起形成一个"大"的任务,这样原来的算子就成为了真正任务里的一部分。每个task会被一个线程执行。这样的技术被称为"算子链"。
图2.5 合并算子链
将算子链接成task是非常有效的优化:可以减少线程之间的切换和基于缓存区的数据交换,在减少时延的同时提升吞吐量,Flink默认会按照算子链的原则进行链接合并。
2.2.3任务槽
为了控制并发量,我们需要在TaskManager上对每个任务运行所占用的资源做出明确的划分,就是任务槽。每个任务槽表示TaskManager拥有计算资源的一个固定大小的子集。这些资源就是用来独立执行一个子任务的。
图2.6 任务槽slot共享
当我们将资源密集型和非密集型的任务同时放到一个slot中,它们就可以自行分配对资源占用的比例,从而保证最重的活平均分配给所有的TaskManager。slot共享还有一个好处就是允许我们保存完整的作业管道。这样一来,即使某个TaskManager出现故障宕机,其他节点也可以完全不受影响,作业的任务可以继续执行。
2.3 DataStream
DataStream API是Flink的核心层API。一个Flink程序其实就是对DataStream的各种转换。DataStream的执行主要分为4步:获取执行环境、读取数据源、转换操作、输出。
图2.7 DataStream的四大构成部分
2.3.1 获取执行环境Environment
1、创建执行环境StreamExecutionEnvironment:
1)getExecutionEnvironment:会根据当前运行的上下文直接得到正确的结果:如果程序是独立运行的,就返回一个本地执行环境;如果是创建了jar包,然后从命令行调用它并提交到集群执行,那么就返回集群的执行环境。该方法会根据当前运行的方式,自行决定该返回什么样的运行环境。
2)createLocalEnvironment:本地执行环境
3)createRemoteEnvironment:集群执行环境
2、执行模式:
1)流执行模式(Streaming):这是DataStream API最经典的模式,一般用于需要持续实时处理的无界数据流。
2)批执行模式(Batch):专门用于批处理的执行模式。
3)自动模式(AutoMatic):在这种模式下,将由程序根据输入数据源是否有界,来自动选择执行模式。
1.StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
2.env.setRuntimeMode(RuntimeExecutionMode.BATCH);
3.env.execute();
2.3.2 读取数据源-源算子(Source)
Flink可以从各种来源获取数据,然后构建DataStream进行转换处理。
1.从集合中读取数据:
1.List data = Arrays.asList(1, 22, 3);
2.DataStreamSource ds = env.fromCollection(data);
2.从文件读取数据:
1.FileSource fileSource = FileSource.forRecordStreamFormat(new TextLineInputFormat(), new Path("input/word.txt")).build();
2.env.fromSource(fileSource,WatermarkStrategy.noWatermarks(),"file").print();
3.从Socket读取数据:
1.DataStream stream = env.socketTextStream("localhost", 7777);
4.从Kafka读取数据:
1.KafkaSource kafkaSource = KafkaSource.builder()
-
.setBootstrapServers("hadoop102:9092")
-
.setTopics("topic_1")
-
.setGroupId("atguigu")
-
.setStartingOffsets(OffsetsInitializer.latest())
-
.setValueOnlyDeserializer(new SimpleStringSchema())
-
.build();
8.DataStreamSource stream = env.fromSource(kafkaSource, WatermarkStrategy.noWatermarks(), "kafka-source");
9.stream.print("Kafka");
2.3.3 转换算子(Transformation)
1)基本转换算子:
1.map:主要用于将数据流中的数据进行转换,形成新的数据流。
2.filter:对数据流执行一个过滤,判断为true则元素正常输出,若为false则元素被过滤掉
3.flatMap:将数据流中的整体(一般是集合类型)拆分成一个一个的个体使用。
2)聚合算子:
1.keyBy:分区操作,keyBy通过指定键key,可以将一条流从逻辑上划分成不同的分区。基于不同的key,流中的数据将被分配到不同的分区中。
2.sum/min/max/minBy/maxBy:在输入流上,对指定的字段做叠加求和、求最小值、求最大值。
3.reduce:归约聚合reduce可以对已有的数据进行归约处理,把每一个新输入的数据和当前已经归约出来的值,再做一个聚合计算。
3)用户自定义函数(UDF):
用户可以根据自身需求,重新实现算子的逻辑。用户自定义函数分为:函数类、匿名函数、富函数类。
1.函数类(Function Classes):Flink暴露了所有UDF函数的接口,具体实现方式为接口或者抽象类,例如MapFunction、FilterFunction、ReduceFunction等。用户可以自定义一个函数类,实现对应的接口。
2.富函数类(Rich Function Classes):所有的Flink函数类都有其Rich版本。不同于函数类,富函数类可以获取运行环境的上下文,并拥有一些生命周期方法,所以可以实现更复杂的功能。open()方法,是Rich Function的初始化方法,会开启一个算子的生命周期。当一个算子如map()或filter()方法被调用之前,open()会首先被调用。close()方法,是生命周期中的最后一个调用的方法,类似于结束方法,用来做一些清理工作。
4)物理分区算子:
物理分区算子可以提高计算作业的并行处理性能,实现负载均衡,有效管理任务的状态。
1.随机分区shuffle:将数据随机地分配到下游算子的并行任务中去。
2.轮询分区rebalance:按照先后顺序将数据做依次分发。
3.重缩放分区rescale:底层也是使用轮询分区算法进行分区,但是只会将数据轮询发送到下游并行任务的一部分中,发牌人只给自己团体内的所有人轮询分区。
4.广播broadcast:广播之后,数据会在不同的分区都保留一份,可能进行重复处理。
5)分流操作:
将一条数据流拆分成完全独立的两条、甚至多条流。基于一个DataStream,定义一些筛选条件,将符合条件的数据拣选出来放到对应的流里。
6)合流操作:
将来源不同的多条流的数据进行联合处理。
1.Union(联合):数据类型必须相同,合并之后的新流会包括所有流中的元素,数据类型不变。
2.Connect(连接):为了处理更加灵活,连接操作允许流的数据类型不同。链接之前,每个链接流内部仍保持各自的数据形式不变,彼此之间是相互独立的。要想得到新的DataStream,需要进一步定义一个"同处理"转换操作,用来说明对于不同来源、不同类型的数据,分别进行处理转换、得到统一的输出类型。