Spark源码分析 -- SchedulableBuilder

SchedulableBuilder就是对Scheduleable tree的封装, 
在Pool层面(中间节点), 完成对TaskSet的调度(FIFO, FAIR) 
在TaskSetManager 层面(叶子节点), 完成对TaskSet中task的调度(locality)以及track(retry)

TaskSetManager

用于封装TaskSet, 主要提供对单个TaskSet内部的tasks的track和schedule 
所以主要的接口, 
resourceOffer, 对于一个resource offer, 如何schedule一个task来执行 
statusUpdate, 对于task状态的track

/**
 * Tracks and schedules the tasks within a single TaskSet. This class keeps track of the status of
 * each task and is responsible for retries on failure and locality. The main interfaces to it
 * are resourceOffer, which asks the TaskSet whether it wants to run a task on one node, and
 * statusUpdate, which tells it that one of its tasks changed state (e.g. finished).
 *
 * THREADING: This class is designed to only be called from code with a lock on the TaskScheduler
 * (e.g. its event handlers). It should not be called from other threads.
 */
private[spark] trait TaskSetManager extends Schedulable {
  def schedulableQueue = null  
  def schedulingMode = SchedulingMode.NONE
  def taskSet: TaskSet
  def resourceOffer(
      execId: String,
      host: String,
      availableCpus: Int,
      maxLocality: TaskLocality.TaskLocality)
    : Option[TaskDescription]
  def statusUpdate(tid: Long, state: TaskState, serializedData: ByteBuffer)
  def error(message: String)
}

 

ClusterTaskSetManager

ClusterScheduler上对于TaskSetManager的实现

1 addPendingTask 
locality, 在schedule时候需要考虑, 应该优先执行尽可能近的task 
所有未被执行的tasks, 都是pending task, 并且是安装不同locality粒度存储在hashmap中的 
pendingTasksForExecutor, hashmap, 每个executor被指定的task 
pendingTasksForHost,  hashmap, 每个instance被指定的task 
pendingTasksForRack, hashmap, 每个机架被指定的task 
pendingTasksWithNoPrefs, ArrayBuffer, 没有locality preferences的tasks, 随便在那边执行 
allPendingTasks, ArrayBuffer, 所有的pending task 
speculatableTasks, 重复的task, 熟悉hadoop的应该容易理解 
可以继续看下addPendingTask, 如何把task加到各个list上去

addPendingTask(index: Int, readding: Boolean = false
两个参数, 
index, task的index, 用于从taskset中取得task 
readding, 表示是否新的task, 因为当executor失败的时候, 也需要把task重新再加到各个list中, list中有重复的task是没有关系的, 因为选取task的时候会自动忽略已经run的task

 

2 resourceOffer 
解决如何在taskset内部schedule一个task, 主要需要考虑的是locality, 直接看注释 
其中比较意思的是, 对currentLocalityIndex的维护 
初始时为0, PROCESS_LOCAL, 只能选择PendingTasksForExecutor 
每次调用resourceOffer, 都会计算和前一次task launch之间的时间间隔, 如果超时(各个locality的超时时间不同), currentLocalityIndex会加1, 即不断的放宽 
而代表前一次的lastLaunchTime, 只有在resourceOffer中成功的findTask时会被更新, 所以逻辑就是优先选择更local的task, 但当findTask总失败时, 说明需要放宽 
但是放宽后, 当有比较local的task被选中时, 这个currentLocalityIndex还会缩小, 因为每次都会把tasklocality赋值给currentLocality

 

3 statusUpdate 
应对statusUpdate, 主要是通过在clusterScheduler中注册的listener通知DAGScheduler 
当然对于失败的task, 还要再加到pending list里面去

/**
 * Schedules the tasks within a single TaskSet in the ClusterScheduler. This class keeps track of
 * the status of each task, retries tasks if they fail (up to a limited number of times), and
 * handles locality-aware scheduling for this TaskSet via delay scheduling. The main interfaces
 * to it are resourceOffer, which asks the TaskSet whether it wants to run a task on one node,
 * and statusUpdate, which tells it that one of its tasks changed state (e.g. finished).
 *
 * THREADING: This class is designed to only be called from code with a lock on the
 * ClusterScheduler (e.g. its event handlers). It should not be called from other threads.
 */
private[spark] class ClusterTaskSetManager(
    sched: ClusterScheduler,
    val taskSet: TaskSet,
    clock: Clock = SystemClock)
  extends TaskSetManager
  with Logging
{
  val tasks = taskSet.tasks
  val numTasks = tasks.length

  // Set of pending tasks for each executor. These collections are actually
  // treated as stacks, in which new tasks are added to the end of the
  // ArrayBuffer and removed from the end. This makes it faster to detect
  // tasks that repeatedly fail because whenever a task failed, it is put
  // back at the head of the stack. They are also only cleaned up lazily;
  // when a task is launched, it remains in all the pending lists except
  // the one that it was launched from, but gets removed from them later.
  private val pendingTasksForExecutor = new HashMap[String, ArrayBuffer[Int]]
  // Set of pending tasks for each host. Similar to pendingTasksForExecutor,
  // but at host level.
  private val pendingTasksForHost = new HashMap[String, ArrayBuffer[Int]]
  // Set of pending tasks for each rack -- similar to the above.
  private val pendingTasksForRack = new HashMap[String, ArrayBuffer[Int]]
  // Set containing pending tasks with no locality preferences.
  val pendingTasksWithNoPrefs = new ArrayBuffer[Int]
  // Set containing all pending tasks (also used as a stack, as above).
  val allPendingTasks = new ArrayBuffer[Int]
  // Tasks that can be speculated. Since these will be a small fraction of total
  // tasks, we'll just hold them in a HashSet.
  val speculatableTasks = new HashSet[Int]
 
  // Figure out which locality levels we have in our TaskSet, so we can do delay scheduling
  val myLocalityLevels = computeValidLocalityLevels() // 当前TaskSet里面的task locality有哪些
  val localityWaits = myLocalityLevels.map(getLocalityWait) // 每个locality level默认的等待时间(从配置读)

  // Delay scheduling variables: we keep track of our current locality level and the time we
  // last launched a task at that level, and move up a level when localityWaits[curLevel] expires.
  // We then move down if we manage to launch a "more local" task.
  var currentLocalityIndex = 0    // 当前myLocalityLevels中的index, 从0开始, 从最小的开始schedule
  var lastLaunchTime = clock.getTime()  // 记录最后launch task的时间, 用于后面会算超时, 如果发生超时, currentLocalityIndex+1 
 
  /**
   * Add a task to all the pending-task lists that it should be on. If readding is set, we are
   * re-adding the task so only include it in each list if it's not already there.
   */
  private def addPendingTask(index: Int, readding: Boolean = false) {
    // Utility method that adds `index` to a list only if readding=false or it's not already there
    def addTo(list: ArrayBuffer[Int]) {
      if (!readding || !list.contains(index)) { // 新的的task或在该list里面没有
        list += index
      }
    }
    var hadAliveLocations = false
    for (loc <- tasks(index).preferredLocations) {
      for (execId <- loc.executorId) {
        if (sched.isExecutorAlive(execId)) {
          addTo(pendingTasksForExecutor.getOrElseUpdate(execId, new ArrayBuffer)) // 首先加到相应的executor列表中
          hadAliveLocations = true
        }
      }
      if (sched.hasExecutorsAliveOnHost(loc.host)) {
        addTo(pendingTasksForHost.getOrElseUpdate(loc.host, new ArrayBuffer)) // 加到host的列表中 
        for (rack <- sched.getRackForHost(loc.host)) {
          addTo(pendingTasksForRack.getOrElseUpdate(rack, new ArrayBuffer)) // 加到Rack的列表中
        }
        hadAliveLocations = true
      }
    }

    if (!hadAliveLocations) { // 如果上面的选择都失败了, 或本来就没有preferred locations, 那就加到pendingTasksWithNoPrefs中
      // Even though the task might've had preferred locations, all of those hosts or executors
      // are dead; put it in the no-prefs list so we can schedule it elsewhere right away.
      addTo(pendingTasksWithNoPrefs)
    }

    if (!readding) { // 对于新的task, 需要加到allPendingTasks中
      allPendingTasks += index  // No point scanning this whole list to find the old task there
    }
  }
 
  /**
   * Dequeue a pending task for a given node and return its index and locality level.
   * Only search for tasks matching the given locality constraint.
   */
  private def findTask(execId: String, host: String, locality: TaskLocality.Value)
    : Option[(Int, TaskLocality.Value)] =
  {
    // 先从Executor
    for (index <- findTaskFromList(getPendingTasksForExecutor(execId))) { // findTaskFromList, Dequeue a pending task from the given list and return its index.
      return Some((index, TaskLocality.PROCESS_LOCAL))
    }
    // Node, 需要先check locality
    if (TaskLocality.isAllowed(locality, TaskLocality.NODE_LOCAL)) { // locality >= TaskLocality.NODE_LOCAL 
      for (index <- findTaskFromList(getPendingTasksForHost(host))) {
        return Some((index, TaskLocality.NODE_LOCAL))
      }
    }
    // Rack, 需要先check locality 
    if (TaskLocality.isAllowed(locality, TaskLocality.RACK_LOCAL)) {
      for {
        rack <- sched.getRackForHost(host)
        index <- findTaskFromList(getPendingTasksForRack(rack))
      } {
        return Some((index, TaskLocality.RACK_LOCAL))
      }
    }
    // Look for no-pref tasks after rack-local tasks since they can run anywhere.
    for (index <- findTaskFromList(pendingTasksWithNoPrefs)) {
      return Some((index, TaskLocality.PROCESS_LOCAL))
    }
    if (TaskLocality.isAllowed(locality, TaskLocality.ANY)) {
      for (index <- findTaskFromList(allPendingTasks)) {
        return Some((index, TaskLocality.ANY))
      }
    }
    // Finally, if all else has failed, find a speculative task
    return findSpeculativeTask(execId, host, locality)
  }

 

  /**
   * Respond to an offer of a single executor from the scheduler by finding a task
   */
  override def resourceOffer(
      execId: String,
      host: String,
      availableCpus: Int,
      maxLocality: TaskLocality.TaskLocality)
    : Option[TaskDescription] =
  {
    if (tasksFinished < numTasks && availableCpus >= CPUS_PER_TASK) { // 前提是task没有执行完和有足够的available cores(>1)
      val curTime = clock.getTime()

      var allowedLocality = getAllowedLocalityLevel(curTime) // 取到当前allowed LocalityLevel
      if (allowedLocality > maxLocality) {  //  不能超出作为参数传入的maxLocality, 调用者限定
        allowedLocality = maxLocality   // We're not allowed to search for farther-away tasks
      }

      findTask(execId, host, allowedLocality) match { // 调用findTask, 并对返回值进行case, findTask逻辑很简单就是依次从不同的locality中取task
        case Some((index, taskLocality)) => {
          // Found a task; do some bookkeeping and return a task description
          val task = tasks(index)
          val taskId = sched.newTaskId()
          // Figure out whether this should count as a preferred launch
          logInfo("Starting task %s:%d as TID %s on executor %s: %s (%s)".format(
            taskSet.id, index, taskId, execId, host, taskLocality))
          // Do various bookkeeping
          copiesRunning(index) += 1
          val info = new TaskInfo(taskId, index, curTime, execId, host, taskLocality)
          taskInfos(taskId) = info
          taskAttempts(index) = info :: taskAttempts(index)
          // Update our locality level for delay scheduling
          currentLocalityIndex = getLocalityIndex(taskLocality) // 用当前Task的locality来更新currentLocalityIndex, 这里index有可能会减少, 因为taskLocality <= currentLocality 
          lastLaunchTime = curTime      // 更新lastLaunchTime 
          // Serialize and return the task
          val startTime = clock.getTime()
          // We rely on the DAGScheduler to catch non-serializable closures and RDDs, so in here
          // we assume the task can be serialized without exceptions.
          val serializedTask = Task.serializeWithDependencies(
            task, sched.sc.addedFiles, sched.sc.addedJars, ser)
          val timeTaken = clock.getTime() - startTime
          increaseRunningTasks(1)
          logInfo("Serialized task %s:%d as %d bytes in %d ms".format(
            taskSet.id, index, serializedTask.limit, timeTaken))
          val taskName = "task %s:%d".format(taskSet.id, index)
          if (taskAttempts(index).size == 1)
            taskStarted(task,info)
          return Some(new TaskDescription(taskId, execId, taskName, index, serializedTask)) // 最终返回schedule得到的那个task
        }
        case _ =>
      }
    }
    return None
  }
 
  /**
   * Get the level we can launch tasks according to delay scheduling, based on current wait time.
   */
  private def getAllowedLocalityLevel(curTime: Long): TaskLocality.TaskLocality = {
    while (curTime - lastLaunchTime >= localityWaits(currentLocalityIndex) &&  // 发生超时
        currentLocalityIndex < myLocalityLevels.length - 1)
    {
      // Jump to the next locality level, and remove our waiting time for the current one since
      // we don't want to count it again on the next one
      lastLaunchTime += localityWaits(currentLocalityIndex)
      currentLocalityIndex += 1   // currentLocalityIndex 加 1
    }
    myLocalityLevels(currentLocalityIndex)
  }
 
  /**
   * Find the index in myLocalityLevels for a given locality. This is also designed to work with
   * localities that are not in myLocalityLevels (in case we somehow get those) by returning the
   * next-biggest level we have. Uses the fact that the last value in myLocalityLevels is ANY.
   */
  def getLocalityIndex(locality: TaskLocality.TaskLocality): Int = { // 查询locality在myLocalityLevels中的index
    var index = 0
    while (locality > myLocalityLevels(index)) {
      index += 1
    }
    index
  }
  
  /**
   * Compute the locality levels used in this TaskSet. Assumes that all tasks have already been
   * added to queues using addPendingTask.
   */
  // 仅仅从各个pending list中看看当前的taskset中的task有哪些preference locality, 从小到大 
  private def computeValidLocalityLevels(): Array[TaskLocality.TaskLocality] = {
    import TaskLocality.{PROCESS_LOCAL, NODE_LOCAL, RACK_LOCAL, ANY}
    val levels = new ArrayBuffer[TaskLocality.TaskLocality]
    if (!pendingTasksForExecutor.isEmpty && getLocalityWait(PROCESS_LOCAL) != 0) {
      levels += PROCESS_LOCAL
    }
    if (!pendingTasksForHost.isEmpty && getLocalityWait(NODE_LOCAL) != 0) {
      levels += NODE_LOCAL
    }
    if (!pendingTasksForRack.isEmpty && getLocalityWait(RACK_LOCAL) != 0) {
      levels += RACK_LOCAL
    }
    levels += ANY
    logDebug("Valid locality levels for " + taskSet + ": " + levels.mkString(", "))
    levels.toArray
  }
  
  /** Called by cluster scheduler when one of our tasks changes state */
  override def statusUpdate(tid: Long, state: TaskState, serializedData: ByteBuffer) {
    SparkEnv.set(env)
    state match {
      case TaskState.FINISHED =>
        taskFinished(tid, state, serializedData)
      case TaskState.LOST =>
        taskLost(tid, state, serializedData)
      case TaskState.FAILED =>
        taskLost(tid, state, serializedData)
      case TaskState.KILLED =>
        taskLost(tid, state, serializedData)
      case _ =>
    }
  }
  def taskStarted(task: Task[_], info: TaskInfo) {
    sched.listener.taskStarted(task, info) 
  }
}

 

Pool

一种对schedulableQueue的抽象, 什么是schedulable? 
注释说的, 包含Pools and TaskSetManagers, 这里设计有问题, 你会发现Pools和TaskSetManagers的核心接口完全不同, 虽然TaskSetManagers里面也实现了这些接口, 但都是meanless的 
简单理解成, 作者想要统一对待, 泛化Pools和TaskSetManagers, 所以这样做了

所以对于Pool, 可以理解为TaskSetManagers的容器, 当然由于Pool本身也是Schedulable, 所以容器里面也可以放Pool 
核心接口getSortedTaskSetQueue, 通过配置不同的SchedulingAlgorithm来调度TaskSetManagers(或pool)

所以注意那些FIFO或FAIR都是用来调度TaskSet的, 所以Spark调度的基础是stage

/**
 * An interface for schedulable entities.
 * there are two type of Schedulable entities(Pools and TaskSetManagers)
 */
private[spark] trait Schedulable {
  var parent: Schedulable
  // child queues
  def schedulableQueue: ArrayBuffer[Schedulable]
  def schedulingMode: SchedulingMode
  def weight: Int
  def minShare: Int
  def runningTasks: Int
  def priority: Int
  def stageId: Int
  def name: String

  def increaseRunningTasks(taskNum: Int): Unit
  def decreaseRunningTasks(taskNum: Int): Unit
  def addSchedulable(schedulable: Schedulable): Unit
  def removeSchedulable(schedulable: Schedulable): Unit
  def getSchedulableByName(name: String): Schedulable
  def executorLost(executorId: String, host: String): Unit
  def checkSpeculatableTasks(): Boolean
  def getSortedTaskSetQueue(): ArrayBuffer[TaskSetManager]
  def hasPendingTasks(): Boolean
}

 

package org.apache.spark.scheduler.cluster
//An Schedulable entity that represent collection of Pools or TaskSetManagers
private[spark] class Pool(
    val poolName: String,
    val schedulingMode: SchedulingMode,
    initMinShare: Int,
    initWeight: Int)
  extends Schedulable
  with Logging {

  var schedulableQueue = new ArrayBuffer[Schedulable] // 用于buffer Schedulable, TaskSetManager
  var schedulableNameToSchedulable = new HashMap[String, Schedulable]

  var priority = 0
  var stageId = 0
  var name = poolName
  var parent:Schedulable = null

  var taskSetSchedulingAlgorithm: SchedulingAlgorithm = { // SchedulingAlgorithm其实就是定义comparator,后面好将TaskSet排序
    schedulingMode match {
      case SchedulingMode.FAIR => 
        new FairSchedulingAlgorithm() // Fair
      case SchedulingMode.FIFO =>
        new FIFOSchedulingAlgorithm() // FIFO
    }
  }

  override def addSchedulable(schedulable: Schedulable) { // 增加一个TaskSetManager
    schedulableQueue += schedulable
    schedulableNameToSchedulable(schedulable.name) = schedulable
    schedulable.parent= this
  }

  override def removeSchedulable(schedulable: Schedulable) { // 删除一个TaskSetManager 
    schedulableQueue -= schedulable
    schedulableNameToSchedulable -= schedulable.name
  }

  override def getSortedTaskSetQueue(): ArrayBuffer[TaskSetManager] = { // 返回排过序的TaskSetManager列表
    var sortedTaskSetQueue = new ArrayBuffer[TaskSetManager]
    val sortedSchedulableQueue = schedulableQueue.sortWith(taskSetSchedulingAlgorithm.comparator) // sortWith 
    for (schedulable <- sortedSchedulableQueue) {
      sortedTaskSetQueue ++= schedulable.getSortedTaskSetQueue() // 这里的schedulable有可能也是pool, 所以需要递归调用
    }
    return sortedTaskSetQueue
  }
}

 

SchedulableBuilder

上面说了Pool里面可以是TaskSetManagers也可以是pool, 这样是不是可以形成tree 
SchedulableBuilder就是对Schedulable Tree的封装, 通过TaskSetManagers(叶节点)和pools(中间节点), 来生成Schedulable Tree 
这里只列出最简单的FIFO, 看不出tree的感觉 
对于FIFO很简单, 直接使用一个Pool就可以, 把所有的TaskSet使用addSchedulable加进去, 然后排序读出来即可

这里没有列出Fair的实现, 比较复杂, 后面再分析吧

/**
 * An interface to build Schedulable tree
 * buildPools: build the tree nodes(pools)
 * addTaskSetManager: build the leaf nodes(TaskSetManagers)
 */
private[spark] trait SchedulableBuilder {
  def buildPools()
  def addTaskSetManager(manager: Schedulable, properties: Properties)
}

private[spark] class FIFOSchedulableBuilder(val rootPool: Pool)
  extends SchedulableBuilder with Logging {

  override def buildPools() {
    // nothing
  }

  override def addTaskSetManager(manager: Schedulable, properties: Properties) {
    rootPool.addSchedulable(manager)
  }
}

本文章摘自博客园,原文发布日期:2014-01-06
莫比乌斯反演 本文从算法竞赛和数论角度出发,系统介绍了狄利克雷卷积与莫比乌斯反演的核心概念。首先定义了数论函数及其基本性质,重点阐述了狄利克雷卷积的代数结构(交换环)和关键恒等式(如μ*1=ε)。随后通过卷积视角简化了莫比乌斯反演的证明过程。最后将理论提升至抽象代数层面,揭示这些数论工具本质上是偏序集关联代数在整除关系下的特例,其中莫比乌斯函数对应Zeta函数的逆元。全文展现了数学概念从具体到抽象的升华过程,为理解这些工具提供了更深刻的代数视角。 阅读详情

相关推荐

第二章 社会老年学的概念和理论框架

老年歧视刻板印象:把年老等同于 3D:依赖 dependent、抑郁 depressed、痴呆 demented。硬件:户外空间建筑、交通、住房;软件:社会参与、敬老社会包容、公民参与就业、信息通讯、社区支持保健服务。保持心态、运动、均衡膳食,老年人可以维持健康头脑与身体;:老年人、老年艺术家通过艺术创作活动,丰富晚年,实现老有所乐、老有所教、老有所成、老有所为。家人照料、志愿、有偿劳动、学习分享都属于老有所为;老有所为:老年人参与社会,选择可以对社会做贡献的老年生活。相较于健康老化、积极老化、成功老化,

欢迎来到鸾姝淡月博客,专为程序员和编程爱好者打造的技术博客。我们分享编程语言趋势、开发工具、编程哲学、职业发展等,助你掌握新技术,优化技能,激发编程热情。 37

深入理解Spark 2.1 Core (三):任务调度器的原理与源码分析

上一篇博文《深入理解Spark 2.1 Core (二):DAG调度器的实现与源码分析 》讲到了DAGScheduler.submitMissingTasks中最终调用了taskScheduler.submitTasks来提交任务。这篇我们就从taskScheduler.submitTasks开始讲,深入理解TaskScheduler的运行过程。提交Task调用栈如下: TaskSchedulerI

4603

第33篇 · Vol.1·M1:LLM 基础与 Transformer

这个模块是整个 LLM 面试的地基:Attention 缩放、RoPE 位置编码、RMSNorm/Pre-Norm、Decoder-only 架构、KV Cache、FlashAttention、Scaling Law 与 Chinchilla 法则。算法岗必考,应用岗和工程岗也常被抽查前几题,面试官靠这几题判断你懂不懂底层。题目以 ⭐ 高频为主,几乎每场必问,要练到肌肉记忆的程度。对应学习篇 S1《大模型基础原理》,见本专栏第 3-5 篇。(核心知识点,具体到术语、数字、因果关系,不写废话)+

weixin_41870061的博客 66

Spark运行流程源码走读

SparkContext是整个spark程序的入口,在写WordCount程序时会new SparkContext(sparkConf)构建一个SparkContext实例。在SparkContext.scala中会执行一些必要的任务,最重要的如下(在396行的try块中的521行):  // Create and start the scheduler val (sched, ts) = Sp

ZERO 1604

Spark架构原理-TaskScheduler原理剖析

原文地址:https://blog.csdn.net/zhanglh046/article/details/78486051 TaskScheduler是一个接口,DAGScheduler在提交TaskSet给底层调度器的时候是面向接口TaskScheduler。TaskSchduler的核心任务是提交Taskset到集群运算并汇报结果。其执行过程如下图所示: 为TaskSet创建和维护一...

上海一九四三 1286

spark2.3源码分析之submitTasks的流程

TaskSchedulerImpl 概述 不同类型的集群对应于不同的SchedulerBackend:YarnSchedulerBackend、StandaloneSchedulerBackend、LocalSchedulerBackend等。TaskSchedulerImpl为不同的SchedulerBackend处理相同的逻辑,例如决定任务之间的调度顺序等。 client端必须先调用Ta...

lzf的博客 1517

Spark1.3从创建到提交:3)任务调度初始化源码分析

上一节在SparkContext中也提及到了,在该类中创建了一个任务调度器,下面我们具体来分析这个方法 private[spark] var (schedulerBackend, taskScheduler) = SparkContext.createTaskScheduler(this, master) createTaskScheduler的代码如下: private def cr

Javis486的专栏 881

Spark源码分析 -- TaskScheduler

Spark在设计上将DAGScheduler和TaskScheduler完全解耦合, 所以在资源管理和task调度上可以有更多的方案 现在支持,LocalSheduler,ClusterScheduler,MesosScheduler, YarnClusterScheduler 先分析ClusterScheduler, 即standalone的Sp...

weixin_34240657的博客 127

Spark源码分析10-Schedualer

Spark很重要的一部分是Task的schedual,以下是具体的流程图。  SchedulableBuilder分为两种,分别是FairSchedulableBuilder和FIFOSchedulableBuilder。主要是pool的getSortedTaskSetQueue方法中调用不同的taskSetSchedulingAlgorithm去排序schedulableQueue o...

fannk的博客 231

Spark Core源码分析: Spark任务执行模型

DAGScheduler 面向stage的调度层,为job生成以stage组成的DAG,提交TaskSet给TaskScheduler执行。 每一个Stage内,都是独立的tasks,他们共同执行同一个compute function,享有相同的shuffledependencies。DAG在切分stage的时候是依照出现shuffle为界限的。

张包峰的博客 4869

Spark源码分析 – 汇总索引

http://jerryshao.me/categories.html#architecture-ref http://blog.csdn.net/pelick/article/details/17222873 如果想了解Spark的设计, 第一个足够 如果想梳理Spark的源码整体结构, 第二个也可以  ALL Spark源码分析SparkContext Spark源码分...

weixin_33796205的博客 246

大数据 之 Snappy

【代码】大数据 之 Snappy。

zhixingheyi_tian的博客 165

IoT DC3 时序存储选型:四款数据库可插拔

—按设备取最近趋势、跨设备时段聚合,还是分析时多序列对齐。不同答案对应不同的数据库。定义端口,四个适配器各自对接一款时序数据库,由配置项。

Pnoker 291

美团2026年Q2财报:收入1046亿元,同比增长14.4%

本季度,美团继续加大生态投入力度,推动行业可持续发展。进一步完善骑手福利保障体系,7月1日起,美团骑手“新职伤”保险已覆盖全国,保费由平台全额缴纳,实现每单必保、每人必保。依托美团在本地生活领域的长期积累,CatPaw提供企业级Agent开发与托管能力,帮助商家把AI落地高价值经营场景,切实提升经营效率,已在餐饮、美业、宠物医院等多个真实场景中完成验证。美团CEO王兴表示:“我们会坚定加大生态与科技投入,推动AI融入真实业务场景,以提升用户、商户体验和公司经营效率,帮大家吃得更好,生活更好。

TMT_XQ的博客 197

Kafka实战 自定义Offset消费 手动Offset管理

<think>我们根据内容生成摘要。内容主要讲Kafka消费者组的offset管理,包括earliest语义、重复消费、自动/手动提交offset、同步异步提交等。需要≤150字。</think>摘要:Kafka消费者组通过offset记录消费位置,实现宕机恢复后从指定位置继续消费。earliest仅当offset不存在时生效;已有有效offset则无效。自动提交offset可能因未提交导致重复消费,可缩短提交周期或改手动提交(同步阻塞、异步回调)降低风险。

小楼一夜听春雨,深巷明朝卖杏花 183

现代大数据技术栈核心知识总结

参考链接:https://golangguide.top大数据领域技术很多,但不需要单纯记技术名称。理解大数据技术最好的方式,是搞清楚每项技术解决什么问题。整体可以分成:数据存储、数据传输、数据计算、数据组织、数据查询分析几个部分。一个典型的大数据系统可以简单理解为: 可以简单记成:MapReduce 是 Hadoop 时代经典的分布式计算模型。核心思想: 例如统计海量日志中每个城市的访问次数,可以把数据分给多台机器处理,最后汇总结果。MapReduce 主要面向批处理: 由于任务调度、中间结果落盘、Shu

EmotionComputer 239

Django的手机数据分析与可视化

Django的手机数据分析与可视化系统 摘 要 本系统基于Django框架、HTML和MySQL数据库技术,构建了一个全面的手机数据分析与可视化平台。大屏内容丰富,包括品牌市场份额分布、手机价格区间分布、各维度评分分布、热门机型Top10、内存容量分布以及品牌评分与价格分析等多个模块。通过这些模块,用户可以直观地了解手机市场的整体趋势、各品牌的市场表现、手机价格分布情况、用户评分分布以及热门机型的详细信息。系统采用了先进的可视化技术,将复杂的数据以图表的形式呈现,使得数据更加易于理解和分析。 在管理功能方

weixin_41915110的博客 765

参考资料-基于Freescale单片机的电池管理系统设计.zip

参考资料-基于Freescale单片机的电池管理系统设计.zip

上一篇: 《Java遗传算法编程》—— 1.4 进化计算的优势
下一篇: 《部署IPv6网络(修订版)》一2.3 IPv6 Internet控制消息协议(ICMPv6)
weixin_34024034
博客等级 码龄11年 6727粉丝 137原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值