【大数据】Flink 详解(一):基础篇

Flink 详解(一):基础篇

1、什么是 Flink ?

【大数据】Flink 详解(一):基础篇_第1张图片

Flink 是一个以 为核心的高可用、高性能的分布式计算引擎。具备 流批一体,高吞吐、低延迟,容错能力,大规模复杂计算等特点,在数据流上提供 数据分发、通信等功能。

2、能否详细解释一下其中的 数据流、流批一体、容错能力 等概念?

数据流:所有产生的 数据 都天然带有 时间概念,把 事件 按照时间顺序排列起来,就形成了一个事件流,也被称作数据流。

流批一体

首先必须先明白什么是 有界数据无界数据

在这里插入图片描述

  • 有界数据:就是在一个确定的时间范围内的数据流,有开始,有结束,一旦确定就不会再改变,一般 批处理 用来处理有界数据,如上图的 bounded stream
  • 无界数据:就是持续产生的数据流,数据是无限的,有开始,无结束,一般 流处理 用来处理无界数据。如图 unbounded stream

Flink 的设计思想是以 为核心,批是流的特例,擅长处理 无界有界 数据, Flink 提供 精确的时间控制能力有状态 计算机制,可以轻松应对无界数据流,同时提供 窗口 处理有界数据流。所以被成为流批一体。

容错能力:在分布式系统中,硬件故障、进程异常、应用异常、网络故障等异常无处不在,Flink 引擎必须保证故障发生后 不仅可以 重启应用程序,还要 确保其内部状态保持一致,从最后一次正确的时间点重新出发。

Flink 提供 集群级容错 和 应用级容错 能力:

  • 集群级容错: Flink 与 集群管理器 紧密连接,如 YARNKubernetes,当进程挂掉后,自动重启新进程接管之前的工作。同时具备 高可用性 ,可消除所有单点故障,
  • 应用级容错:Flink 使用 轻量级分布式快照,设计检查点(checkpoint)实现可靠容错。

Flink 利用检查点特性,在框架层面提供 Exactly-once 语义,即端到端的一致性,确保数据仅处理一次,不会重复也不会丢失,即使出现故障,也能保证数据只写一次。

3、Flink 和 Spark Streaming 的区别?

Flink 和 Spark Sreaming 最大的区别在于:Flink 是标准的实时处理引擎,基于事件驱动,以流为核心;而 Spark Streaming 的 RDD 实际是一组小批次的 RDD 集合,是微批(Micro-Batch)的模型,以批为核心

下面我们介绍两个框架的主要区别:

1.架构模型

Spark Streaming 在运行时的主要角色包括:

  • 服务架构集群和资源管理 Master / Yarn Application Master;
  • 工作节点 Work / Node Manager;
  • 任务调度器 Driver;任务执行器 Executor

【大数据】Flink 详解(一):基础篇_第2张图片

Flink 在运行时主要包含:客户端 Client、作业管理 Jobmanager、任务管理 Taskmanager

【大数据】Flink 详解(一):基础篇_第3张图片
2. 任务调度

Spark Streaming 连续不断的生成微小的数据批次,构建有向无环图 DAG,Spark Streaming 会依次创建 DStreamGraph、JobScheduler。

【大数据】Flink 详解(一):基础篇_第4张图片
Flink 根据用户提交的代码生成 StreamGraph,经过优化生成 JobGraph,然后提交给 JobManager进行处理,JobManager 会根据 JobGraph 生成 ExecutionGraph,ExecutionGraph 是 Flink 调度最核心的数据结构,JobManager 根据 ExecutionGraph 对 Job 进行调度,根据物理执行图部署到Taskmanager上形成具体的 Task 执行。

【大数据】Flink 详解(一):基础篇_第5张图片
3. 时间机制

Spark Streaming 支持的时间机制有限,只支持 处理时间

Flink 支持了流处理程序在时间上的三个定义:事件时间 EventTime摄入时间 IngestionTime处理时间 ProcessingTime。同时也支持 watermark 机制来处理滞后数据。

【大数据】Flink 详解(一):基础篇_第6张图片
4. 容错机制

  • 对于 Spark Streaming 任务,我们可以设置 checkpoint,然后假如发生故障并重启,我们可以从上次 checkpoint 之处恢复,但是这个行为只能使得数据不丢失,可能会重复处理,不能做到恰好一次处理语义。
  • Flink 则使用 两阶段提交协议 来解决这个问题。

4、Flink 的架构包含哪些?

Flink 架构分为 技术架构运行架构 两部分。

5、简单介绍一下 Flink 的技术架构。

如下图为 Flink 技术架构:

【大数据】Flink 详解(一):基础篇_第7张图片
Flink 作为流批一体的分布式计算引擎,必须提供面向开发人员的 API 层,同时还需要跟外部数据存储进行交互,需要 连接器,作业开发、测试完毕后,需要提交集群执行,需要 部署层,同时还需要运维人员能够管理和监控,还提供图计算、机器学习、SQL 等,需要 应用框架层

6、详细介绍一下 Flink 的运行架构。

如下图为 Flink 运行架构:

【大数据】Flink 详解(一):基础篇_第8张图片
Flink 集群采取 Master - Slave 架构,Master 的角色为 JobManager,负责集群和作业管理,Slave 的角色是 TaskManager,负责执行计算任务,同时,Flink 提供客户端 Client 来管理集群和提交任务,JobManagerTaskManager 是集群的进程。

  • Client:Flink 客户端是 Flink 提供的 CLI 命令行工具,用来提交 Flink 作业到 Flink 集群,在客户端中负责 StreamGraph(流图)和 JobGraph(作业图)的构建。
  • JobManager:JobManager 根据并行度将 Flink 客户端提交的 Flink 应用分解为子任务,从资源管理器 ResourceManager 申请所需的计算资源,资源具备之后,开始分发任务到 TaskManager 执行 Task,并负责应用容错,跟踪作业的执行状态,发现异常则恢复作业等。
  • TaskManager:TaskManager 接收 JobManage 分发的子任务,根据自身的资源情况 管理子任务的启动、 停止、销毁、异常恢复等生命周期阶段。Flink 程序中必须有一个 TaskManager。

7、介绍一下 Flink 的并行度。

Flink 程序在执行的时候,会被映射成一个 Streaming Dataflow。一个 Streaming Dataflow 是由一组 Stream 和 Transformation Operator 组成的。在启动时从一个或多个 Source Operator 开始,结束于一个或多个 Sink Operator。

Flink 程序本质上是并行的和分布式的,在执行过程中,一个流(stream)包含一个或多个流分区,而每一个 operator 包含一个或多个 operator 子任务。操作子任务间彼此独立,在不同的线程中执行,甚至是在不同的机器或不同的容器上。

operator 子任务的数量是这一特定 operator 的并行度。相同程序中的不同 operator 有不同级别的并行度。

【大数据】Flink 详解(一):基础篇_第9张图片
一个 Stream 可以被分成多个 Stream 的分区,也就是 Stream Partition。一个 Operator 也可以被分为多个 Operator Subtask。如上图中,Source 被分成 Source1 和 Source2,它们分别为 Source 的 Operator Subtask。每一个 Operator Subtask 都是在不同的线程当中独立执行的。一个 Operator 的并行度,就等于 Operator Subtask 的个数

上图 Source 的并行度为 2 2 2。而一个 Stream 的并行度就等于它生成的 Operator 的并行度。数据在两个 operator 之间传递的时候有两种模式:

  • One to One 模式:两个 operator 用此模式传递的时候,会保持数据的分区数和数据的排序;如上图中的 Source1 到 Map1,它就保留的 Source 的分区特性,以及分区元素处理的有序性。
  • Redistributing (重新分配)模式:这种模式会改变数据的分区数;每个 operator subtask 会根据选择 transformation 把数据发送到不同的目标 subtasks,比如 keyBy() 会通过 hashcode 重新分区,broadcast()rebalance() 方法会随机重新分区。

8、Flink 的并行度的怎么设置的?

我们在实际生产环境中可以从四个不同层面设置并行度:

  • 操作算子层面(Operator Level
  • 执行环境层面(Execution Environment Level
  • 客户端层面(Client Level
  • 系统层面(System Level

需要注意的优先级:算子层面 > 环境层面 > 客户端层面 > 系统层面。

9、Flink 编程模型了解不?

Flink 应用程序主要由三部分组成, source、转换 transformation、目的地 sink。这些流式 dataflows 形成了有向图,以一个或多个源(source)开始,并以一个或多个目的地(sink)结束。

【大数据】Flink 详解(一):基础篇_第10张图片
【大数据】Flink 详解(一):基础篇_第11张图片

10、Flink 作业中的 DataStream,Transformation 介绍一下?

Flink 作业中,包含两个基本的块:数据流DataStream)和 转换Transformation)。

DataStream 是逻辑概念,为开发者提供 API 接口,Transformation 是处理行为的抽象,包含了数据的读取、计算、写出。所以 Flink 作业中的 DataStream API 调用,实际上构建了多个由 Transformation 组成的数据处理流水线(Pipeline)。

DataStream API 和 Transformation 的转换如下图:

【大数据】Flink 详解(一):基础篇_第12张图片

11、Flink 的分区策略了解吗?

数据分区 在 Flink 中叫作 Partition。本质上来说,分布式计算就是把 一个作业 切分成子任务 Task, 将不同的数据交给不同的 Task 计算。

在分布式存储中, Partition 分区的概念就是把数据集切分成块,每一块数据存储在不同的机器上。同样 ,对于分布式计算引擎,也需要将数据切分,交给位于不同物理节点上的 Task 计算。

StreamPartitioner 是 Flink 中的数据流分区抽象接口,决定了在实际运行中的数据流分发模式, 将数据切分交给 Task 计算,每个 Task 负责计算一部分数据流。所有的数据分区器都实现了ChannelSelector 接口,该接口中定义了负载均衡选择行为。

// ChannelSelector 接口定义
public interfaceChannelSelector<T extends IOReadablewritable> { 
    //下游可选 Channel 的数量
    void setup (intnumberOfChannels); 
    //选路方法
    int selectChannel (T record); 
    //是否向下游广播
    boolean isBroadcast();
 }

在该接口中可以看到,每一个分区器都知道下游通道数量,该通道在一次作业运行中是固定的,除非修改作业的并行度,否则该值不会改变。

目前 Flink 支持 8 8 8 种分区策略的实现,数据分区体系如下图:
【大数据】Flink 详解(一):基础篇_第13张图片
(1)GlobalPartitioner

数据会被分发到下游算子的第一个实例中进行处理。

(2)ForwardPartitioner

在 API 层面上 ForwardPartitioner 应用在 DataStream 上,生成一个新的 DataStream。

该 Partitioner 比较特殊,用于在同一个 OperatorChain 中上下游算子之间的数据转发,实际上数据是直接传递给下游的,要求上下游并行度一样。

(3)ShufflePartitioner

随机的将元素进行分区,可以确保下游的 Task 能够均匀地获得数据,使用代码如下:

dataStream.shuffle();

(4)RebalancePartitioner

Round-robin 的方式为每个元素分配分区,确保下游的 Task 可以均匀地获得数据,避免数据倾斜。使用代码如下:

dataStream.rebalance();

(5)RescalePartitioner

根据上下游 Task 的数量进行分区, 使用 Round-robin 选择下游的一个Task 进行数据分区,如上游有 2 2 2 个 Source.,下游有 6 6 6 个 Map,那么每个 Source 会分配 3 3 3 个固定的下游 Map,不会向未分配给自己的分区写入数据。这一点与 ShufflePartitioner 和 RebalancePartitioner 不同, 后两者会写入下游所有的分区。
【大数据】Flink 详解(一):基础篇_第14张图片

运行代码如下:

dataStream.rescale();

(6)BroadcastPartitioner

将该记录广播给所有分区,即有 N N N 个分区,就把数据复制 N N N 份,每个分区 1 1 1 份,其使用代码如下:

dataStream.broadcast();

(7)KeyGroupStreamPartitioner

在 API 层面上,KeyGroupStreamPartitioner 应用在 KeyedStream上,生成一个新的 KeyedStream。

KeyedStream 根据 keyGroup 索引编号进行分区,会将数据按 Key 的 Hash 值输出到下游算子实例中。该分区器不是提供给用户来用的。

KeyedStream 在构造 Transformation 的时候默认使用 KeyedGroup 分区形式,从而在底层上支持作业 Rescale 功能。

(8)CustomPartitionerWrapper

用户自定义分区器。需要用户自己实现 Partitioner 接口,来定义自己的分区逻辑。

static class CustomPartitioner implements Partitioner<String> {
      @Override
      public int partition(String key, int numPartitions) {
          switch (key){
              case "1":
                  return 1;
              case "2":
                  return 2;
              case "3":
                  return 3;
              default:
                  return 4;
          }
      }
  }

12、描述一下 Flink Wordcount 执行包含的步骤有哪些?

主要包含以下几步:

  • 获取运行环境 StreamExecutionEnvironment;
  • 接入 source 源 ;
  • 执行转换操作,如 map()flatmap()keyby()sum()
  • 输出 sink 源,如 print();
  • 执行 execute。

提供一个示例:

import org.apache.flink.api.common.functions.FlatMapFunction;
import org.apache.flink.api.java.utils.ParameterTool;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.windowing.time.Time;
import org.apache.flink.util.Collector;


public class WordCount {

    public static void main(String[] args) throws Exception {
        //定义socket的端口号
        int port;
        try{
            ParameterTool parameterTool = ParameterTool.fromArgs(args);
            port = parameterTool.getInt("port");
        }catch (Exception e){
            System.err.println("没有指定port参数,使用默认值9000");
            port = 9000;
        }
        //获取运行环境
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        //连接socket获取输入的数据
        DataStreamSource<String> text = env.socketTextStream("10.192.12.106", port, "\n");
        //计算数据
        DataStream<WordWithCount> windowCount = text.flatMap(new FlatMapFunction<String, WordWithCount>() {
            public void flatMap(String value, Collector<WordWithCount> out) throws Exception {
                String[] splits = value.split("\\s");
                for (String word:splits) {
                    out.collect(new WordWithCount(word,1L));
                }
            }
        })//打平操作,把每行的单词转为类型的数据
                .keyBy("word")//针对相同的word数据进行分组
                .timeWindow(Time.seconds(2),Time.seconds(1))//指定计算数据的窗口大小和滑动窗口大小
                .sum("count");
        //把数据打印到控制台
        windowCount.print()
                .setParallelism(1);//使用一个并行度
        // 注意:因为flink是懒加载的,所以必须调用execute方法,上面的代码才会执行
        env.execute("streaming word count");
    }
    /**
     * 主要为了存储单词以及单词出现的次数
     */
    public static class WordWithCount{
        public String word;
        public long count;
        public WordWithCount(){}
        public WordWithCount(String word, long count) {
            this.word = word;
            this.count = count;
        }
        @Override
        public String toString() {
            return "WordWithCount{" + 
                    "word='" + word + '\'' +
                    ", count=" + count +
                    '}';
        }
    }
}

13、Flink 常用的算子有哪些?

分两部分:

(1)数据读取,这是 Flink 流计算应用的起点,常用算子有:

  • 从内存读:fromElements
  • 从文件读:readTextFile
  • Socket 接入 :socketTextStream
  • 自定义读取:createInput

(2)处理数据的算子,主要用于 转换 过程,常用的算子包括:

  • 单输入单输出:Map
  • 单输入、多输出:FlatMap
  • 过滤:Filter
  • 分组:KeyBy
  • 聚合:Reduce
  • 窗口:Window
  • 连接:Connect
  • 分割:Split

【1】Apache Flink - 数据流上的有状态计算:https://flink.apache.org/zh/

【2】Apache Flink 文档:https://nightlies.apache.org/flink/flink-docs-release-1.17/zh/

【3】Flink大全 - 洋群满满のBlog

【4】史上最全干货!Flink面试大全总结

你可能感兴趣的:(大数据,大数据,flink,分布式计算,流批一体)