YRAN 调度流程介绍

151 阅读8分钟

yarn 的基础架构和调度流程

yarn 的基础架构主要包括三大组件:ResourceManager、Nodemanager、ApplicationMaster

  • ResourceManager 是一个全局的资源管理器,负责整个系统的资源管理和分配,主要包括两大组件:调度器(scheduler)和 应用程序管理器(Application Manager)。
  • ApplicationMaster 是 ResourceManager 根据用户提交了作业按照作业的上下文信息等分配出的一个 Container 资源,然后通知 Nodemanager 为用户作业创建出一个 ApplicationMaster。
  • Nodemanager 是对 yarn 。

yarn 的调度流程可以简单总结如下:

  1. 客户端提交应用程序给 ResourceManager,ResourceManager 收到请求后会分配一个 Container 资源并通知对应的 NodeManager 启动一个 ApplicationMaster。
  2. ApplicationMaster 来运行和管理 Container 里面的任务,其中 Container 会通过心跳机制向 ApplicationMaster 发送运行的信息。
  3. 当任务完成后 ApplicationMaster 会向 ResourceManager 报告任务已完成,申请 Container 资源释放。

一、MapReduce On YARN

  1. 用户向 YARN 中提交应用程序,其中包括 ApplicationMaster 程序、启动 ApplicationMaster 程序的命令、用户程序等。
  2. ResourceManager 为该应用程序分配第一个 Container ,并与对应的 NodeManager 通信,要求它在这个 Container 中启动 应用程序的 ApplicationMaster。
  3. ApplicationMaster 首先向 ResourceManager 注册(这样用户可以直接通过 ResourceManager 查看应用程序的运行状态),然后它将为各个任务申请资源,并监控它们的运行状态,直到任务运行结束,即重复步骤4-7。
  4. ApplicationMaster 采用轮询的方式通过 RPC 协议向 ResourceManager 申请和领取资源。
  5. 一旦 ApplicationMaster 申请到资源,便与对应的 NodeManager 通信,要求它启动任务。
  6. NodeManager 为任务设置好运行环境(包括环境变量、JAR包、二进制程序等)后,将任务启动命令写到一个脚本中,并通过运行该脚本启动任务。
  7. 各个任务通过RPC协议向 ApplicationMaster 汇报自己的状态和进度,让 ApplicationMaster 随时掌握各个任务的运行状态,从而在任务失败时重新启动任务。在应用程序运行过程中,用户可随时通过RPC向 ApplicationMaster 查询应用程序当前的运行状态。
  8. 应用程序运行完成后,ApplicationMaster 向 ResourceManager 申请注销并关闭自己。

Spark On YARN

当在命令行执行spark-submit --master xxx.jar 的命令后会执行以下操作:

  1. 客户端向资源管器Master发送注册和申请资源的请求,其中Master主要负责任务、资源的分配,是Spark集群的老大。
  2. Master 收到资源申请的请求后它会向指定的Worker节点发送请求,然后Worker节点会开启对应的 Executor 进程。
  3. Executor 进程会向 Driver 发送一个注册请求,然后申请要计算的Task。
  4. 在 Driver 的内部会执行一些操作最终都会通过 TaskScheduler 提交 Task 到 Executor 进程里面去执行,具体细节如下:
  • 第一步、Driver会运行客户端程序中的 main() 方法
  • 第二部、在 main() 方法中会创建一个 SparkContext 上下文对象,该对象是所有Spark程序的一个执行入口,在构建 SparkContext 的上下文的内部他也会构建两个对象,分别是:DAGScheduler 和 TaskScheduler。
  • 第三部、因为在用户代码中RDD算子会涉及大量的转换操作,然后会通过一个(action)操作触发任务的真正执行,在这里会按照RDD与RDD之间的依赖关系首先会生成一张DAG有向无环图,图的方向就是RDD算子的操作顺序,最终会将RDD的DAG有向无环图发送给 DAGScheduler 对象。
  • 第四部、DAGScheduler 他在获取DAG有向无环图之后会按照宽依赖进行 stage 的划分,由于RDD算子中包含了大量的宽依赖,所以在划分出多个 stage 之后,每一个 stage 的内部有很多个可以并行运行的 task 的线程,然后将这些并行的task线程封装在一个 taskSet 集合中,最后将多个 taskSet 的集合发送给 TaskScheduler 对象。
  • 第五部、TaskScheduler 对象在获取到这些TaskSet集合后之后会按照多个 stage 之间的依赖关系运行,前面的 stage 的 task 先运行,后面的再运行,然后 TaskScheduler 对象依次遍历每一个 taskSet 集合获取每一个 task,最后将每一个 task 提交到 Worker 节点的 Executor 进程运行。

上面5步就是 Driver 内部的一个执行逻辑,然后我们回到主线程 5. 当所有的task任务在 Executor 进程里面依次运行完成后 Driver 端会向 Master发送一个注销请求。 6. Master 收到这个请求后通知对应的 Worker 节点关闭 Executor 进程,最后 Executor 节点上的计算资源会得到释放。

以上就是Spark 任务的提交流程。

在 Spark 中任务调度的方式有哪些?

在 spark 中任务调度的方式主要包括stage级的调度和task级的调度。首先在创建 SparkContext 上下文对象时会创建出 DAGScheduler 和 TaskScheduler;其中 DAGScheduler 负责 stage 级的调度,主要是将Job 根据宽依赖切分出若干个stage,并将每个stage打包成 TaskSet 交给 TaskScheduler 调度;而 TaskScheduler 复杂 task 级的调度,将 DAGScheduler 发送过来的 TaskSet 按照指定的调度策略分配给 Executor 进行执行。TaskScheduler 支持两种调度策略:FIFO(默认的先进先出调度策略) 和 Fair(公平调度策略)。

Spark 数据倾斜的解决方案有哪些?

产生数据倾斜的主要原因是在 shuffle 过程中不同的key对应数据量不同,从而导致不同的 task 所分配的数据量不均匀所产生的。所以要解决 Spark 数据倾斜的问题可以从以下几个方面去处理:

  • 提高 shuffle 操作的并行度,用户只要直接去增加 shuffle 读 task 的数量,比如设置 reduceByKey[1000],一般默认是200。

    • 优点是:有效缓解数据倾斜
    • 缺点是:无法彻底解决数据倾斜
  • 使用随机数前缀进行 join 操作。对大量相同的key进行附加随机数前缀让它们变成不同的key,然后将这些不同的key分散到不同的task中去处理。

    • 优点:对 join 类型的数据倾斜大多数可以处理。
    • 缺点:对内存要求较高。
  • 将 reduce join 转为 map join。适用于两种表join时一张表的数据量较小的情况,通过将小表全量广播,然后通过 map 算子来实现与 join 相同的效果,也就是 map join。该方法不会发生数据倾斜。

    • 优点:不会发生 shuffle。
    • 缺点:只适用于大表加小表。
  • 过滤少数导致数据倾斜的key。该方法适用的前提条件是少数几个数据量特别多的key对任务的执行影响不会过大,可以直接通过 where 语句将它们过滤掉。

    • 优点:实现简单
    • 缺点:受限于特定的场景
  • 使用 hive 预处理数据。如果导致数据倾斜的是 hive 发生的,直接对 hive 进行预处理操作,从源头来规避数据倾斜问题。

    • 优点:提高 spark 的作业性能
    • 缺点:hive 也会发生数据倾斜
  • 使用两阶段聚合操作,适用于 reduceByKey、groupByKey 分组场景,先通过局部预聚合,再通过全局聚合来解决数据倾斜。

    • 优点:可以显著提高Spark的性能
    • 缺点:受限于最固定的场景

以上就是数据倾斜的解决方案。

Flink SQL 应用提交流程

Flink SQL提交流程总共包括两大阶段,分别是 SQL 到 operation 到转换、operation 到 transformations 的转换。

从 SQL 到 operation 的转换主要经过以下四个阶段:

  1. 调用 parse() 方法将SQL转化为未经校验的AST抽象语法树(即SQL Node),也就是SQl解析阶段,它主要会用到词法解析和语法解析,词法解析就是将sql语句转换为一组token;而语法解析是将token进行递归下降词法分析。
  2. 进行SQL校验,即未经校验的抽象语法树校验成已经校验过的抽象语法树。在校验阶段主要校验两部分:校验表名、字段名、函数名是否正确;校验特殊的类型是否正确,如 join、groupby操作是否有嵌套等。
  3. 会调用 rel() 方法。调用 rel() 方法主要是将抽象语法树SqlNode转为关系代数树RelNode(关系表达式)和 RexNode进行表达,在这个步骤中DDL它是不执行rel() 方法等,因为DDL实际上是对元数据对修改,不涉及复杂查询。
  4. 调用 convert() 方法将 RelNode转为Operation,operation 它包括多种类型,但最终都会生成根节点 modify operation。

经过以上第一大步骤之后,最终都会生成一个 modify operation,接下来进行第二大阶段的生成,也就是 operation 到 transformations 的转换。这个转换过程也会经过以下几个步骤:

  1. 将 modify operation 最终转换成 calcite 的逻辑计划树(calcite logicalPath),其次会将 calcite logicalPath 转为 Flink 的逻辑计划树(Flink logicalRel)。
  2. 调用 optimize() 方法将 Flink logicalRel 优化成物理计划树 flinkPhysicalRel,在这中间主要包括两大优化规则:基于规则优化RBO和基于代价优化CBO。
  3. 调用 translateToExecNodeGraph方法,该方法是将物理计划树转化为 ExecGraph。
  4. 调用 translateToPlan() 方法,会将最终的 ExecGraph 转化为 transformations。

以上就是 Flink SQL 的执行流程。