任务调度原理

Flink原理简介和使用_flink

客户端不是运行时和程序执行的一部分 

但它用于准备并发送dataflow(JobGraph)给Master(JobManager)

然后客户端断开连接或维持连接以等待接受计算结果

当Flink集群启动后 首先会启动一个JobManager和一个或多个TaskManager去执行

然后TaskManager将心跳和统计信息汇报给JobManager

TaskManager之间以流的形式进行数据传输

以上三者均为独立的JVM进程
  • Client
提交Job的客户端

可以是运行在任何机器上 (与JobManager环境连通即可)

提交Job后 Clinet可以结束进程(Streaming的任务)

也可以不结束并等待结果返回
  • JobManager
主要负责调度Job并协调Task做checkpoint 

职责很像Storm的Nimbus

从Clinet处接受Job和Jar包等资源后 会生成优化后的执行计划

并以Task的单元调度到各个TaskManager去执行
  • TaskManager
TaskManager启动的时候就设置好了槽位数(Slot) 

每个Slot能启动一个Task 。Task为线程

从JobManager处接受需要部署的Task

部署启动后 与自己上游建立Netty连接 接受数据并处理

TaskManager与Slots

Flink原理简介和使用_并行度_02

  • Flink中的每一个worker(TaskManager)都是一个JVM进程
它可能会在独立的线程上执行一个或多个subtask
  • 控制一个worker能接受多少个task ,worker通过task slot来进行控制
一个worker至少有一个task slot
  • 每个task slot表示TaskManager拥有资源的一个固定大小的子集
假如一个TaksManager有三个slot 那么它会将其管理的内存分成三份分给三个slot
  • 资源slot化意味着一个subtask将不需要跟来自其他job的subtask竞争被管理的内存 取而代之的是它将拥有一定数量的内存储备
需要注意的是 这里不会涉及到CPU的隔离 slot目前仅仅用来隔离task受管理的内存
  • 通过调整task slot的数量 允许用户定义subtask之间如何互相隔离
如果一个taskManager一个slot 那将意味着每个task group运行在独立的JVM中 (该JVM可能通过一个特定的容器启动的)

而一个TaskManager多个slot意味着更多的subtask可以共享同一个JVM

而同一个JVM进程中的task将共享TCP连接(基于多路复用)和心跳消息

它们也可能共享数据集和数据结构 因此这减少了每个task的负载

子任务共享slot

Flink原理简介和使用_kafka_03

  • 默认情况下 Flink允许子任务共享slot
即使它们是不同任务的子任务(前提是它们来自同一个job)

这样的结果是 一个slot可以保存业务的整个管道
  • Task slot是静态概念 是指TaskManager具有并发执行能力
可以通过taskmanager.numberOfTaskSlots进行配置
  • 并行度 parallelism是动态概念 即每个TaskManager运行程序时实际使用的并发能力
可以通过参数 parallelism.default进行配置
假设一共有3个TaskManager 每个TaskManager中分配3个TaskSlot

也就是说每个TaskManager可以接收3个task

一共9个TaskSlot 如果设置parralelism.default=1即运行程序默认的并行度为1

9个TaskSlot只用了一个 有8个空闲

因此需要设置合适的并行度才能提高效率

Flink原理简介和使用_并行度_04

程序和数据流

Flink原理简介和使用_flink_05

  • 所有的Flink由三部分组成 Source、Transformation、Sink
Source负责读取数据源
Transformation利用各种算子进行处理加工
Sink负责输出

在运行时 Flink上运行的程序会被映射成 "逻辑数据流" dataflows 它包含了这三部分

每一个dataflow以一个或多个sources开始
以一个或多个sinks结束
  • dataflow类似于任意的有向无环图(DAG)
在大部分情况下 程序的转换算法(transformations)跟dataflow中的算子(operator)是一一对应关系
但有时候一个transformations可能对应多个operator

Flink原理简介和使用_并行度_06

执行图(ExecutionGraph)

由Flink程序直接映射成数据流图 StreamGraph 也被成为逻辑流图 因为它们表示的是计算逻辑的高级视图 

为了执行一个流处理程序 Flink需要将逻辑流图转换为物理数据流图(也叫执行图)详细说明程序的执行方式
  • Flink执行图可以分为四层
StreamGraph -> JobGraph -> ExecutionGraph -> 物理执行图
  • StreamGraph
是根据用户通过Stream API编写的代码生成的最初的图 用来表示程序的拓扑结构
  • JobGraph
StreamGraph经过优化后生成了JobGraph 提交给JobManager的数据结构

主要优化为:

将多个符合条件的节点 chain在一起作为一个节点 这样可以减少数据在节点之间流动所需要的序列化/反序列化/传输消耗
  • ExecutionGraph
JobManager根据JobGraph生成了ExecutionGraph

ExecutionGraph是JobGraph的并行化版本 是调度层最核心的数据结构

  • 物理执行图
JobManager根据ExecutionGraph对Job进行调度后

在各个TaskManager上部署Task后形成的“图”

并不是一个具体的数据结构

Flink原理简介和使用_kafka_07

并行度(Parallelism)

Flink程序的执行具有并行、分布式特性 

一个流包含了一个或多个分区(stream partition)

而每一个算子(operator)可以包含一个或多个子任务(operator subtask)

这些子任务在不同的线程、不同物理机或不同的容器中不依赖的执行
  • 并行数据流

Flink原理简介和使用_flink_08

一个特定算子的子任务(subtask)的个数被称为并行度

一般情况下 一个流程序的并行度 可以认为其所有算子中最大的并行度

Flink原理简介和使用_并行度_09

一个程序中 不同的算子可能具有不同的并行度

Stream在算子之间传输数据的形式可以是 one-to-one(forwarding)的模式也可以是redistributing的模式 具体是哪一种形式 取决于算子的种类

one-to-one:

stream(比如在source和map operator之间) 维护着分区以及元素的顺序 那意味着map算子的子任务看到的元素的个数以及顺序跟source算子的子任务生产的元素的个数、顺序相同 map、filter、flatMap等算子都是one-to-one的对应关系


Redistributing:

stream(map()跟keyBy/window之间或者keyBy/window跟slink之间)的分区会发生改变

每一个算子的子任务依据所选择的transformation发送数据到不同的目标任务

例如:

keyBy()基于hashCode重分区
broadcast和rebalance会随机重新分区
这些算子都会引起redistribute过程
该过程就类似于spark中的shuffle
类似于spark的窄依赖、宽依赖

任务链

Flink原理简介和使用_并行度_10


a 、

Flink采用一种成为任务链的优化技术

可以在指定条件下减少本地通信开销

为了满足任务链的要求

必须将两个或多个算子设为相同的并行度

并通过本地转发的方式连接(local forward)


b、

相同并行度one to one操作 Flink这样相连的算子链接在一起形成一个task 原来的算子成为里面的subtask

c、

并行度相同、并且是One-to-One操作 两个条件缺一不可


d、

将算子链接成task是非常有效的优化

它能减少线程之间的切换和基于缓存区的数据交换

在减少时延的同时提升吞吐量

链接的行为可以在编程API中进行指定

上面是纯理论 下面实践下 才能对理论理解的更加透彻

Flink 流处理API

Environment

Flink原理简介和使用_kafka_11

  • getExecutionEnvironment
创建一个执行环境 表示当前执行程序的上下文

如果程序是独立调用的 则此方法返回本地执行环境

如果从命令行调用程序以提交到集群 则此方法返回集群的执行环境
  • 代码
val env: ExecutionEnvironment = ExecutionEnvironment.getExecutionEnvironment

val env = StreamExecutionEnvironment.getExecutionEnvironment


a 返回本地执行环境 需要在调用时指定默认的并行度

val env = StreamExecutionEnvironment.createLocalEnvironment(1)


b 返回集群环境 将jar包提交到远程服务器 需要在调用时指定 JobManager的IP和端口号 并指定要在集群中运行的jar包

val env = ExecutionEnvironment.createRemoteEnvironment("jobmanage-hostname",
6123,"YOURPATH//wordcount.jar")
  • 并行度
如果没有设置并行度 会以flink-conf.yaml中配置为准 默认是1

Flink原理简介和使用_kafka_12

Source

  • 从集合读取数据
//  定义样例类,传感器 id ,时间戳,温度
case class SensorReading(id: String, timestamp: Long, temperature: Double)
object Sensor {
def main(args: Array[String]): Unit = {
val env = StreamExecutionEnvironment.getExecutionEnvironment
val stream1 = env
.fromCollection(List(
SensorReading("sensor_1", 1547718199, 35.80018327300259),
SensorReading("sensor_6", 1547718201, 15.402984393403084),
SensorReading("sensor_7", 1547718202, 6.720945201171228),
SensorReading("sensor_10", 1547718205, 38.101067604893444)
))
stream1.print("stream1:").setParallelism(1)
env.execute()
}
}
  • 从文件读取数据
val stream2 = env.readTextFile("YOUR_FILE_PATH")
  • 以kafka消息队列的数据作为来源

需要引入kafka连接器的依赖 pom:

<dependency>
<groupId>org.apache.flink</ groupId>
<artifactId>flink-connector-kafka-0.11_2.11</artifactI d>
<version>1.7.2</version>
</dependency>


代码:

val properties = new Properties()
properties.setProperty("bootstrap.servers", "localhost:9092")
properties.setProperty("group.id", "consumer-group")
properties.setProperty("key.deserializer",
"org.apache.kafka.common.serialization.StringDeserializer")
properties.setProperty("value.deserializer",
"org.apache.kafka.common.serialization.StringDeserializer")
properties.setProperty("auto.offset.reset", "latest")
val stream3 = env.addSource(new FlinkKafkaConsumer011[String]("sensor", new
SimpleStringSchema(), properties))
  • 先测试下kafka发送消息 从zk中消费消息是否可以
cd /opt/kafka/kafka_2.10-0.8.2.1/bin


生产消息

./kafka-console-producer.sh --broker-list 192.168.84.128:9092 --topic test

消费消息

./kafka-console-consumer.sh --zookeeper 192.168.84.128:2181 --topic test --from-beginning

Flink原理简介和使用_并行度_13Flink原理简介和使用_flink_14

demo程序是用的kafka版本是 kafka-0.11_2.11

目前虚拟机上安装的版本是2.10-0.8.2.1 所以为了跑demo程序 所以安装下kafka-0.11_2.11版本

安装过程​​大数据处理工具Kafka、Zk、Spark​

  • 安装包下载路径
https://archive.apache.org/dist/kafka/0.11.0.1/kafka_2.12-0.11.0.1.tgz
  • 唯一的区别
/opt/kafka/版本号/config/server.properties 这个配置文件

配置zk集群的配置项名称

kafka_2.10-0.8.2.1: zookeeper.contact

kafka_2.12-0.11.0.1: zookeeper.connect
  • 生成一条消息
./kafka-console-producer.sh --broker-list 192.168.84.128:9092 --topic test

Flink原理简介和使用_并行度_15

  • 消费这条消息
./kafka-console-consumer.sh --zookeeper 192.168.84.128:2181 --topic test --from-beginning

Flink原理简介和使用_kafka_16

  • 通过Flink Kafka来消费

​代码​

https://gitee.com/pingfanrenbiji/Flink-UserBehaviorAnalysis/blob/master/FlinkTutorial/src/main/scala/com/xdl/apitest/SourceTest.scala

Flink原理简介和使用_flink_17

消费成功