【Flink】入门Demo实现、Flink运行架构之运行时组件,任务提交流程,任务调度原理

文章目录

  • 一 快速上手
    • 1 创建maven项目
    • 2 引入依赖
    • 3 代码实现
      • (1)从在线数据读取
      • (2)从离线数据读取
    • 4 将程序提交到单点集群运行
  • 二 Flink运行架构
    • 1 Flink运行时组件
      • (1)作业管理器(JobManager)
      • (2)资源管理器(ResourceManager)
      • (3)分发器(Dispatcher)
      • (4)JobMaster
      • (5)任务管理器(TaskManager)
    • 2 任务提交流程
    • 3 任务调度原理

一 快速上手

1 创建maven项目

创建maven项目flink2022tutorial。

2 引入依赖

    <properties>
        <flink.version>1.13.0flink.version>
        <java.version>1.8java.version>
        <scala.binary.version>2.12scala.binary.version>
        <slf4j.version>1.7.30slf4j.version>
    properties>

    <dependencies>
        <dependency>
            <groupId>org.apache.flinkgroupId>
            <artifactId>flink-scala_2.11artifactId>
            <version>1.10.0version>
        dependency>
        
        <dependency>
            <groupId>org.apache.flinkgroupId>
            <artifactId>flink-streaming-scala_2.11artifactId>
            <version>1.10.0version>
        dependency>
    dependencies>

<build>
    <plugins>
    
    <plugin>
        <groupId>net.alchim31.mavengroupId>
        <artifactId>scala-maven-pluginartifactId>
        <version>3.4.6version>
        <executions>
            <execution>
                
                <goals>
                    <goal>compilegoal>
                goals>
            execution>
        executions>
    plugin>
        <plugin>
            <groupId>org.apache.maven.pluginsgroupId>
            <artifactId>maven-assembly-pluginartifactId>
            <version>3.0.0version>
            <configuration>
                <descriptorRefs>
                    <descriptorRef>jar-with-dependenciesdescriptorRef>
                descriptorRefs>
            configuration>
            <executions>
                <execution>
                    <id>make-assemblyid>
                    <phase>packagephase>
                    <goals>
                        <goal>singlegoal>
                    goals>
                execution>
            executions>
        plugin>
    plugins>
build>

在编写程序时需要注意:Flink程序支持java和scala两种语言。在引入包中,有java和scala两种包时注意要使用对应语言的包。

3 代码实现

(1)从在线数据读取

package day01;


import org.apache.flink.api.common.functions.FlatMapFunction;
import org.apache.flink.api.common.functions.ReduceFunction;
import org.apache.flink.api.java.functions.KeySelector;
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;

public class Example1 {
    // 抛出异常
    public static void main(String[] args) throws Exception{
        // 获取流处理的运行环境
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        // 设置并行任务的数量为1
        env.setParallelism(1);

        // 读取数据源,先在终端启动'nc -lk 9999',再执行程序
        DataStreamSource<String> stream = env.socketTextStream("hadoop101", 9999);

        // map操作,这里使用flatMap方法
        // map操作:将数据中的每一条文本,切分,然后转换成元组或POJO类
        // 这里定义一个POJO类
        // map:针对流中的每一个元素,输出一个元素
        // flatMap:针对流中的每一个元素,输出0个,1个或者多个元素
        // Collector是Flink的集合类型
        SingleOutputStreamOperator<WordWithCount> mappedStream = stream
                // 输入泛型:String;输出泛型:WordWithCount
                .flatMap(new FlatMapFunction<String, WordWithCount>() {
                    public void flatMap(String value, Collector<WordWithCount> out) throws Exception {
                        String[] arr = value.split(" ");
                        // 使用collect方法向下游发送数据
                        for (String s : arr) {
                            out.collect(new WordWithCount(s, 1L));
                        }
                    }
                });
        
        // 分组:shuffle
        KeyedStream<WordWithCount, String> keyedStream = mappedStream
                // 第一个泛型:流中元素的泛型
                // 第二个泛型:key的泛型
                .keyBy(new KeySelector<WordWithCount, String>() {
                    public String getKey(WordWithCount value) throws Exception {
                        return value.word;
                    }
                });

        // 累加,reduce操作
        // reduce会维护一个累加器,第一条数据到来,作为累加器输出
        // 第二条数据到来,和累加器进行聚合操作,然后输出累加器
        // 累加器和流中元素的类型是一样的
        SingleOutputStreamOperator<WordWithCount> result = keyedStream
                .reduce(new ReduceFunction<WordWithCount>() {
                    // 定义了聚合的逻辑
                    public WordWithCount reduce(WordWithCount value1, WordWithCount value2) throws Exception {
                        return new WordWithCount(value1.word, value1.count + value2.count);
                    }
                });

        // 输出
        result.print();

        // 执行程序
        env.execute("Socket stream word count");
    }

    // POJO类:模拟了case class
    // 1 必须是公有类
    // 2 所有字段必须是public
    // 3 必须有空构造器
    // Flink在执行程序时,需要通过反射来获取字段类型
    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 +
                    '}';
        }
    }
}

总结:数据流的来源是DataStreamSource,执行map操作之后,变为单输出流的算子SingleOutputStreamOperator,经过shuffle操作之后,变为一个键控流(按照key进行逻辑分区的流)KeyedStream,分组之后的流再聚合完成之后,发送给下游,流又变成单输出流SingleOutputStreamOperator,最后输出结果,执行程序。

flatMap是无状态的算子,而reduce是有状态的算子,reduce针对每一个单词,内部都会维护一个累加器,当重复发送数据时,相同的的单词会经过这样一个运算:reduce先将累加器的值读取出来,然后和输入的数据进行聚合,聚合完成后,写回到累加器,最后输出。

(2)从离线数据读取

修改读取数据部分代码

// 读取数据源
DataStreamSource<String> stream = env.fromElements("hello word", "hello word");

4 将程序提交到单点集群运行

解压下载好的安装包,解压缩,进入bin目录、启动集群,使用‘./start-cluster.sh’命令。

可在Flink中查看。

【Flink】入门Demo实现、Flink运行架构之运行时组件,任务提交流程,任务调度原理_第1张图片

将程序打包,在flink网页端,点击上传新job,选择jar包,如果集群中包含程序运行的依赖,上传不带依赖的jar包,否则上传带依赖的jar包。

添加程序的入口类:

【Flink】入门Demo实现、Flink运行架构之运行时组件,任务提交流程,任务调度原理_第2张图片

执行完成之后可以在Task Managers 的Stdoout中查看运行结果。

或者通过命令行的方式进行提交任务:

‘./flink run -c day01.Example2 /opt/module/flink-1.13.1/lib/flink2022tutorial-1.0-SNAPSHOT.jar’

执行完成之后同样可以在Task Managers 的Stdoout中查看运行结果。

停止集群:‘./stop-cluster.sh ’

查看日志文件:‘[hzy@hadoop101 log]$ cat flink-hzy-taskexecutor-0-hadoop101.out’

二 Flink运行架构

Flink 运行时由两种类型的进程组成:一个 JobManager (主节点)和一个或者多个 TaskManager(从节点)。

是一种典型的 Master-Slave 架构,Flink运行架构如下图:

【Flink】入门Demo实现、Flink运行架构之运行时组件,任务提交流程,任务调度原理_第3张图片

1 Flink运行时组件

Flink运行时架构主要包括四个不同的组件,它们会在运行流处理应用程序时协同工作:作业管理器(JobManager)、资源管理器(ResourceManager)、任务管理器(TaskManager),以及分发器(Dispatcher)。

因为Flink是用Java和Scala实现的,所以所有组件都会运行在Java虚拟机上。每个组件的职责如下。

(1)作业管理器(JobManager)

控制一个应用程序执行的主进程,也就是说,每个应用程序都会被一个不同的JobMaster所控制执行。

JobManager是一个主节点,每接收一个作业就会产生一个JobMaster。

JobManager会先接收到要执行的应用程序,这个应用程序会包括:作业图(JobGraph)、逻辑数据流图(logical dataflow graph)和打包了所有的类、库和其它资源的JAR包。

JobManager会把JobGraph转换成一个物理层面的数据流图,这个图被叫做“执行图”(ExecutionGraph),包含了所有可以并发执行的任务。

JobManager会向Flink的资源管理器(ResourceManager)请求执行任务必要的资源,也就是任务管理器(TaskManager)上的任务插槽(slot)。

一旦它获取到了足够的资源,就会将执行图分(DAG)发到真正运行它们的TaskManager上。而在运行过程中,JobManager会负责所有需要中央协调的操作,比如说检查点(checkpoints)的协调。

作业管理器是一个主节点,是一个JVM进程,其内部存在的线程为:资源管理器,分发器和 JobMaster。

(2)资源管理器(ResourceManager)

主要负责Flink集群中资源的分配,对于Yarn来说,管理的资源是容器。Flink的资源管理器管理的资源是任务管理器(TaskManager)的插槽(slot),这是Flink集群中资源调度的单位。在上面两个例子中设置并行任务的数量为1,也就是需要1个任务插槽。

Flink为不同的环境和资源管理工具提供了不同资源管理器,比如YARN、Mesos、K8s,以及standalone部署。

当JobManager申请插槽资源时,ResourceManager会将有空闲插槽的TaskManager分配给JobManager。如果ResourceManager没有足够的插槽来满足JobManager的请求,它还可以向资源提供平台发起会话,以提供启动TaskManager进程的容器。另外,ResourceManager还负责终止空闲的TaskManager,释放计算资源。

(3)分发器(Dispatcher)

可以跨作业运行,它为应用提交提供了REST接口(SpringBoot中处理get/post请求的接口就是REST接口)。分发器就是一个网页,也即前后端分离的一个服务。

当一个应用被提交执行时,分发器就会启动并将应用移交给一个JobMaster。

由于是REST接口,所以Dispatcher可以作为集群的一个HTTP接入点,这样就能够不受防火墙阻挡。

Dispatcher也会启动一个Web UI( hadoop101:8081),用来方便地展示和监控作业执行的信息。

Dispatcher在架构中可能并不是必需的,这取决于应用提交运行的方式。

(4)JobMaster

JobMaster 负责管理单个 JobGraph 的执行。Flink 集群中可以同时运行多个作业,每个作业都有自己的JobMaster。

(5)任务管理器(TaskManager)

Flink中的工作进程。通常在Flink中会有多个TaskManager运行,每一个TaskManager都包含了一定数量的插槽(slots)。插槽的数量限制了TaskManager能够执行的任务数量。

启动之后,TaskManager会向资源管理器注册它的插槽;收到资源管理器的指令后,TaskManager就会将一个或者多个插槽提供给JobManager调用。

JobManager就可以向插槽分配任务(tasks)来执行了。在执行过程中,一个TaskManager可以跟其它运行同一应用程序的TaskManager交换数据。

2 任务提交流程

整体架构如下:

【Flink】入门Demo实现、Flink运行架构之运行时组件,任务提交流程,任务调度原理_第4张图片

Flink是一个主从架构,主节点中有分发器,JobMaster,资源管理器,每接收一个作业就会产生一个JobMaster。

每一个任务管理器中都有N个任务插槽,至少一个,在进行任务提交时,主从节点会进行一些交流。

计算单元是从节点中的任务槽。

3 任务调度原理

任务调度流程如下:

【Flink】入门Demo实现、Flink运行架构之运行时组件,任务提交流程,任务调度原理_第5张图片

客户端的代码在编译期(打包过程中,java程序首先编译成字节码文件,然后将这些字节码文件和一些依赖压缩成一个包)会进行优化,我们编写的程序就是一个有向无环图,对有向无环图优化成另一种数据结构(作业图),使用客户端将作业提交到JobManager。

Actor System是一种通信的并发设计模式,在这里特指akka通信组件,也就是说jar包是通过使用了akka通信组件将作业提交到JobManager,之后JobManger将作业部署到不同的TaskManger的时候,使用的也是Actor System。

TaskManger之间也会有数据流的通信,其会交换数据,典型的就是shuffle,使用的是netty,是一个并发性能很好的网络IO通信库。

从节点要固定给主节点发送Heartbeats(TCP包)。

客户端不是运行时和程序执行的一部分,但它用于准备并发送dataflow(JobGraph)给Master(JobManager),然后,客户端断开连接或者维持连接以等待接收计算结果。

当 Flink 集群启动后,首先会启动一个 JobManger 和一个或多个的 TaskManager。由 Client 提交任务给 JobManager,JobManager 再调度任务到各个 TaskManager 去执行,然后 TaskManager 将心跳和统计信息汇报给 JobManager。TaskManager 之间以流的形式进行数据的传输。上述三者均为独立的 JVM 进程。

Client 为提交 Job 的客户端,可以是运行在任何机器上(与 JobManager 环境连通即可)。提交 Job 后,Client 可以结束进程(Streaming的任务),也可以不结束并等待结果返回。

JobManager 主要负责调度 Job 并协调 Task 做 checkpoint,职责上很像 Storm 的 Nimbus。从 Client 处接收到 Job 和 JAR 包等资源后,会生成优化后的执行计划,并以 Task 的单元调度到各个 TaskManager 去执行。

TaskManager 在启动的时候就设置好了槽位数(Slot),每个 slot 能启动一个 Task,Task 为线程。从 JobManager 处接收需要部署的 Task,部署启动后,与自己的上游建立 Netty 连接,接收数据并处理。

你可能感兴趣的:(Flink,flink,架构,java)