• Spark 资源调度与任务调度的流程(Standalone):

    • 启动集群后, Worker 节点会向 Master 节点汇报资源情况, Master掌握了集群资源状况。

    • 当 Spark 提交一个 Application 后, 根据 RDD 之间的依赖关系将 Application 形成一个 DAG 有向无环图。

    • 任务提交后, Spark 会在任务端创建两个对象: DAGSchedular 和 TaskScheduler

    • DAGSchedular 是任务调度的高层调度器, 是一个对象

    • DAGScheduler 的主要作用是 将 DAG 根据 RDD 之间的宽窄依赖关系划分为一个个的Stage, 然后将 stage 以 TaskSet 的形式 提交给 TaskScheduler

      • TaskScheduler 是任务调度的底层调度器

      • TaskSet 其实就是一个集合, 里面封装的就是一个个task任务, 也就是stage中的并行度 task 任务

        package org.apache.spark.scheduler
        
        import java.util.Properties
        
        /**
        * A set of tasks submitted together to the low-level TaskScheduler,
        * usually representing missing partitions of a particular stage.
        * 一同被提交到低等级的任务调度器的 一组任务集, 通常代表了一个特定的 stage(阶
        * 段) 的 缺失的分区
        */
        private[spark] class TaskSet(
        // 任务数组
        val tasks: Array[Task[_]],
        // 阶段Id
        val stageId: Int,
        // 尝试的阶段Id(也就是下级Stage?)
        val stageAttemptId: Int,
        // 优先级
        val priority: Int,
        // 是个封装过的Hashtable
        val properties: Properties) {
        // 拼接 阶段Id 和 尝试的阶段Id
        val id: String = stageId + "." + stageAttemptId
        // 重写 toString
        override def toString: String = "TaskSet " + id
        }
      • TaskScheduler 会遍历 TaskSet 集合, 拿到每个 task 后将 task发送到 Executor 中执行

      • 其实就是发送到Executor中的线程池ThreadPool去执行

      • 当 task 执行失败时, 则由TaskSchedular负责重试, 将 task重新发送给 Executor 去执行, 默认重试 3 次

          //提交task,最后一行  backend.reviveOffers()  调用的是CoarseGrainedSchedulerBackend对象中的方法
        override def submitTasks(taskSet: TaskSet) {
        // 获取任务数组
        val tasks = taskSet.tasks
        logInfo("Adding task set " + taskSet.id + " with " + tasks.length + " tasks")
        // 加同步锁
        this.synchronized {
        // 创建任务集管理器 参数: 任务集, 最大容忍任务失败次数
        val manager = createTaskSetManager(taskSet, maxTaskFailures)
        // 阶段Id
        val stage = taskSet.stageId // taskSetsByStageIdAndAttempt 是一个 HashMap[Int, TaskSetManager]
        /* getOrElseUpdate(key: A, op: => B): B=
        * 如果 key 已经在这个 map 中, 就返回其对应的value
        * 否则就根据已知的表达式 'op' 计算其对应的value 并将其存储到 map中, 并返回该 value
        */
        val stageTaskSets =
        taskSetsByStageIdAndAttempt.getOrElseUpdate(stage, new HashMap[Int, TaskSetManager])
        // 将阶段任务集合设置为任务管理器
        stageTaskSets(taskSet.stageAttemptId) = manager
        // 获取冲突的任务集 如果 stageTaskSets 的任务集 不是传入的任务集 并且stageTaskSets的任务集不是僵尸进程 那么它就是冲突的任务集
        val conflictingTaskSet = stageTaskSets.exists { case (_, ts) =>
        ts.taskSet != taskSet && !ts.isZombie
        }
        // 如果有冲突的任务集
        if (conflictingTaskSet) {
        throw new IllegalStateException(s"more than one active taskSet for stage $stage:" +
        s" ${stageTaskSets.toSeq.map{_._2.taskSet.id}.mkString(",")}")
        }
        // 通过可调度的构造器创建一个任务集管理器
        schedulableBuilder.addTaskSetManager(manager, manager.taskSet.properties)
        // 如故不是本地提交 或者 没有接收到任务
        if (!isLocal && !hasReceivedTask) {
        // 通过饥饿的计时器 来 根据 固定的比例进行调度
        // scheduleAtFIxedRate 方法的三个参数: 时间任务, 延迟时间, 周期 如果延迟时间或周期值为父会抛出异常
        starvationTimer.scheduleAtFixedRate(new TimerTask() {
        override def run() {
        // 如果没有发送任务
        if (!hasLaunchedTask) {
        logWarning("Initial job has not accepted any resources; " +
        "check your cluster UI to ensure that workers are registered " +
        "and have sufficient resources")
        } else {
        // 如果发送了任务, 就取消
        this.cancel()
        }
        }
        // 默认的饥饿超时临界值: 15s
        }, STARVATION_TIMEOUT_MS, STARVATION_TIMEOUT_MS)
        }
        hasReceivedTask = true
        }
        // 调用 CoarseGrainedSchedulerBackend对象中的方法
        backend.reviveOffers()
        }
      • 如果重试 3 次 依然失败, 那么这个 task 所在的 stage 就失败了

      • 如果 stage 失败了则由 DAGScheduler 来负责重试, 重新发送 TaskSet 到 TaskScheduler, Stage 默认重试 4 次。

      • 如果重试 4 次 以后依然失败, 那么 该 job 就失败了。

      • 一个 job 失败了, Application 就失败了。

    • TaskScheduler 不仅能重试失败的 task, 还会重试 straggling(直译是挣扎的, 这边可以意译为缓慢的) task(执行速度比其他task慢太多的task)

      /**
      * TaskScheduler 启动
      */
      override def start() {
      //StandaloneSchedulerBackend 启动
      backend.start() if (!isLocal && conf.getBoolean("spark.speculation", false)) {
      logInfo("Starting speculative execution thread")
      // 启动定期执行推测任务线程
      speculationScheduler.scheduleWithFixedDelay(new Runnable {
      override def run(): Unit = Utils.tryOrStopSparkContext(sc) {
      // 检查所有活跃的jon中是否有可推测的任务
      checkSpeculatableTasks()
      }
      }, SPECULATION_INTERVAL_MS, SPECULATION_INTERVAL_MS, TimeUnit.MILLISECONDS)
      }
      }
      // Check for speculatable tasks in all our active jobs.
      // 检查是否有可推测的任务
      def checkSpeculatableTasks() {
      // 是否应该重新激活
      var shouldRevive = false
      // 加同步锁
      synchronized {
      // 检查是否有可推测的任务(传入执行推测所需的最小时间)
      shouldRevive = rootPool.checkSpeculatableTasks(MIN_TIME_TO_SPECULATION)
      }
      // 如果需要重新激活
      if (shouldRevive) {
      // 就尝试运行推测任务
      backend.reviveOffers()
      }
      }
    • Spark 推测执行机制:

      • 如果有运行缓慢的 task, 那么 TaskScheduler 会启动一个新的 task 来与该运行缓慢的 task 执行相同的处理逻辑。

      • 两个 task 哪个先执行完, 就以哪个 task 的执行结果为准。

      • 在 Spark 中推测执行默认是关闭的。

      • 推测执行可以通过 spark.speculation 属性来配置

          /**
        * Return a speculative task for a given executor if any are available
        * 如果有卡壳的进程,就向已知的executor进程返回一个推测任务
        * The task should not have an attempt running on this host, in case
        * the host is slow.
        * 该任务不应有任何尝试任务在该主机上运行, 以防止该主机是有延迟的
        * In addition, the task should meet the given locality constraint.
        * 此外, 该任务需要满足已知的本地约束
        */
        // Labeled as protected to allow tests to override providing speculative tasks if necessary
        // 标注为 protected 以允许测试 来重写 提供的推测任务(如果需要的话)
        protected def dequeueSpeculativeTask(execId: String, host: String, locality: TaskLocality.Value)
        : Option[(Int, TaskLocality.Value)] =
        {
        // 从推测式执行任务列表中移除已经成功完成的task
        speculatableTasks.retain(index => !successful(index)) // Remove finished tasks from set // 1.判断 task 是否可以在该executor对应的Host上执行, 判断条件为:
        // 2.没有taskAttempt在该Host上运行
        // 3. 该 executor 没有在 task 的黑名单中(task 在该executor上失败过, 并且仍在‘黑暗’时间中)
        def canRunOnHost(index: Int): Boolean = {
        !hasAttemptOnHost(index, host) &&
        !isTaskBlacklistedOnExecOrNode(index, execId, host)
        }
        // 判断推测执行任务集合是否为空
        if (!speculatableTasks.isEmpty) {
        // Check for process-local tasks;
        // 检查 本地进程任务
        // note that tasks can be process-local on multiple nodes when we replicate cached blocks, as in Spark Streaming
        // 需要注意的是: 当我们备份缓存块时, 任务可以以本地进程 或者 多节点的形式运行 (就像spark流那样)
        for (index <- speculatableTasks if canRunOnHost(index)) {
        val prefs = tasks(index).preferredLocations
        val executors = prefs.flatMap(_ match {
        case e: ExecutorCacheTaskLocation => Some(e.executorId)
        case _ => None
        });
        // 如果 executor 进程包含该任务Id
        if (executors.contains(execId)) {
        // 就不推测该任务
        speculatableTasks -= index
        // 返回某个本地进程
        return Some((index, TaskLocality.PROCESS_LOCAL))
        }
        } // Check for node-local tasks 检查本地节点的任务
        if (TaskLocality.isAllowed(locality, TaskLocality.NODE_LOCAL)) {
        for (index <- speculatableTasks if canRunOnHost(index)) {
        val locations = tasks(index).preferredLocations.map(_.host)
        if (locations.contains(host)) {
        speculatableTasks -= index
        return Some((index, TaskLocality.NODE_LOCAL))
        }
        }
        } // Check for no-preference tasks 检查非优先级的任务
        if (TaskLocality.isAllowed(locality, TaskLocality.NO_PREF)) {
        // 遍历 speculatableTasks, 如果有任务能够在主机上运行
        for (index <- speculatableTasks if canRunOnHost(index)) {
        // 获取该task的优先级位置
        val locations = tasks(index).preferredLocations
        if (locations.size == 0) {
        speculatableTasks -= index
        return Some((index, TaskLocality.PROCESS_LOCAL))
        }
        }
        } // Check for rack-local tasks 监察本地构建的任务
        if (TaskLocality.isAllowed(locality, TaskLocality.RACK_LOCAL)) {
        for (rack <- sched.getRackForHost(host)) {
        for (index <- speculatableTasks if canRunOnHost(index)) {
        val racks = tasks(index).preferredLocations.map(_.host).flatMap(sched.getRackForHost)
        if (racks.contains(rack)) {
        speculatableTasks -= index
        return Some((index, TaskLocality.RACK_LOCAL))
        }
        }
        }
        } // Check for non-local tasks 检查非本地性的任务
        if (TaskLocality.isAllowed(locality, TaskLocality.ANY)) {
        for (index <- speculatableTasks if canRunOnHost(index)) {
        speculatableTasks -= index
        return Some((index, TaskLocality.ANY))
        }
        }
        } None
        }
        • 需要注意的是:

          • 对于 ETL(Extract Transformation Load) 类型的需要导入数据库的业务需要关闭推测执行机制, 否则会有重复的数据导入数据库。
          • 如果遇到数据倾斜的情况, 开启推测执行则有可能导致一直会有 task 重新启动处理相同的逻辑, 任务可能一直处于处理不完的状态。
  • 粗粒度资源申请 和 细粒度资源申请

    • 粗粒度资源申请(Spark)

      • 在 Application 执行之前, 将所有的资源申请完毕, 当资源申请成功后, 才会进行任务的调度, 当所有的 task 执行完成后才会释放这部分资源

      • 优点

        • 在 Application 执行之前, 所有的资源都申请完毕, 每一个 task 直接使用资源就可以了, 不需要 task 在执行前自己去申请资源。
        • task 执行快了 => stage 执行就快了 => job 执行就快了 => application 执行就快了
      • 缺点

        • 直到最后一个 task 执行完成才会释放资源, 集群的资源无法充分利用。(俗称: 占着M坑不拉S)
    • 细粒度资源申请(MR)

      • Application 执行之前不需要先去申请资源, 而是直接执行, 让 job 中的每一个 task 在执行前自己去申请资源, task执行完成就释放资源。

      • 优点

        • 集群的资源可以充分利用
      • 缺点

        • task自己去申请资源,task启动变慢,Application的运行就响应的变慢了。

Spark 资源调度 与 任务调度的更多相关文章

  1. 【Spark篇】---Spark资源调度和任务调度

    一.前述 Spark的资源调度是个很重要的模块,只要搞懂原理,才能具体明白Spark是怎么执行的,所以尤其重要. 自愿申请的话,本文分粗粒度和细粒度模式分别介绍. 二.具体 Spark资源调度流程图: ...

  2. 【Spark-core学习之六】 Spark资源调度和任务调度

    环境 虚拟机:VMware 10 Linux版本:CentOS-6.5-x86_64 客户端:Xshell4 FTP:Xftp4 jdk1.8 scala-2.10.4(依赖jdk1.8) spark ...

  3. Spark 资源调度及任务调度

    1.  资源分配 通过SparkSubmit进行提交应用后,首先会创建Client将应用程序(字节码文件.class)包装成Driver,并将其注册到Master.Master收到Client的注册请 ...

  4. Spark资源调度及任务调度

    1.  资源分配 通过SparkSubmit进行提交应用后,首先会创建Client将应用程序(字节码文件.class)包装成Driver,并将其注册到Master.Master收到Client的注册请 ...

  5. Spark资源调度和任务调度

    一.资源调度&任务调度 1.启动集群后,Worker节点会周期性的[心跳]向Master节点汇报资源情况,Master掌握集群资源情况. 2.当Spark提交一个Application后,根据 ...

  6. Spark Core_资源调度与任务调度详述

    转载请标明出处http://www.cnblogs.com/haozhengfei/p/0593214ae0a5395d1411395169eaabfa.html Spark Core_资源调度与任务 ...

  7. Spark Core 资源调度与任务调度(standalone client 流程描述)

    Spark Core 资源调度与任务调度(standalone client 流程描述) Spark集群启动:      集群启动后,Worker会向Master汇报资源情况(实际上将Worker的资 ...

  8. spark 图文详解:资源调度和任务调度

    讲说spark的资源调度和任务调度,基本的spark术语,这里不再多说,懂的人都懂了... 按照数字顺序阅读,逐渐深入理解:以下所有截图均为个人上传,不知道为什么总是显示别人的QQ,好尴尬,无所谓啦, ...

  9. [Spark内核] 第31课:Spark资源调度分配内幕天机彻底解密:Driver在Cluster模式下的启动、两种不同的资源调度方式源码彻底解析、资源调度内幕总结

    本課主題 Master 资源调度的源码鉴赏 [引言部份:你希望读者看完这篇博客后有那些启发.学到什么样的知识点] 更新中...... 资源调度管理 任务调度与资源是通过 DAGScheduler.Ta ...

随机推荐

  1. SparkSQL 疫情Demo练习

    在家闲着没事干, 写个简单的疫情数据处理Demo, 顺便回顾下SparkSQL. 模拟数据(以下数据皆为虚构, 如有雷同不胜荣幸) 市民信息(civic_info.csv) id_no,name,se ...

  2. kibana 删除document或者type

    // 删除某个document DELETE /索引名称/type名称/document编号(id) // 删除整个type PUT 索引名称/type名称/_delete_by_query?conf ...

  3. vue.js使用更简单的方法制作带删除功能的tooolist

    今天复习了下vue.js,先做了个基本版的todolist,做完后自己想加个删除本项的按钮.一开始做没啥头绪了,试了试无果,查了一把后发现网上的那些方法真的是麻烦,自己动手丰衣足食,自己最后换了思路试 ...

  4. Day9 - I - 不要62 HDU - 2089

    杭州人称那些傻乎乎粘嗒嗒的人为62(音:laoer).杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌照,不再含有不吉利的数字了,这样一来,就可以消除个别的士司机和乘客的心理障碍, ...

  5. IOS pin约束问题 存在间隙

    今天在为自己的view添加约束 对比以前添加的约束时,发现有有两层淡红色线框一条实线和一条虚线,而以前一个demo中添加的则只有一个蓝色实线框. 今天添加的约束如图1所示: 图1 而以前添加约束如图2 ...

  6. python中单下划线的变量

    1._xxx 不能用于’from module import *’ 以单下划线开头的表示的是protected类型的变量.即保护类型只能允许其本身与子类进行访问.2.__xxx 双下划线的表示的是私有 ...

  7. 小程序行内元素且有border的情况下,根据文字宽度自动调节元素宽度

    比如认识成都: <view style="display: flex;align-items: center;"> <text class="cityV ...

  8. CAS实现单点登录(SSO)经典完整教程

    转自 http://blog.csdn.net/small_love/article/details/6664831 一.简介 1.cas是有耶鲁大学研发的单点登录服务器 2.本教材所用环境 Tomc ...

  9. 无线渗透--wifiphisher之wifi钓鱼获取wifi密码

    本来是想试验一下暴力破解的,但是由于字典太大,跑的时间也比较长,于是使用了钓鱼的方法. 先说一下wifiphisher钓鱼获取wifi密码的原理: wifiphisher对于你在攻击中选定的wifi会 ...

  10. Jquery实现功能---购物车

    //需求,勾选选项时,总价格要跟着变,点击添加数量,总价格也要跟着变,全部要动态变化 //代码如下 <!DOCTYPE html> <html> <head> &l ...