资源调度机制源码分析(schedule方法,两种调度算法)

sparkContext初始化后会注册Application,然后会调用schedule方法,如何为Application在worker上启动Executor,Executor启动后,DAGScheduler和TaskScheduler才能分配task给Executor来进行计算。所以schedule是把整个流程窜起来的重点。

private def schedule(): Unit = {
  //standby master是不会进行Application等资源调度的
  if (state != RecoveryState.ALIVE) {
    return
  }
  // Drivers take strict precedence over executors
  //第一行重要代码,Random.shuffle的原理,将集合的元素随机大量
    取出workers中所有之前注册上来的workers,进行过滤,必须是状态ALIVE的worker
    对状态为ALIVE的worker,调用Random的shuffle方法进行随机打乱
  val shuffledAliveWorkers = Random.shuffle(workers.toSeq.filter(_.state == WorkerState.ALIVE))
  val numWorkersAlive = shuffledAliveWorkers.size
  var curPos = 0
  //首先调度Driver,什么情况下会注册Driver并且导致Driver被调度,其实只有用yarn-cluster模式提交
    才会。因为standalone和yarn-client模式,都是在本地直接启动Driver,而不会来注册Driver,更不可能调度Driver
    遍历waitingDrivers ArrayBuffer
  for (driver <- waitingDrivers.toList) { // iterate over a copy of waitingDrivers
    // We assign workers to each waiting driver in a round-robin fashion. For each driver, we
    // start from the last worker that was assigned a driver, and continue onwards until we have
    // explored all alive workers.
    var launched = false
    var numWorkersVisited = 0
    //只要有活着的worker没有遍历到,并且driver还没有被启动,也就是launched为false
    while (numWorkersVisited < numWorkersAlive && !launched) {
      val worker = shuffledAliveWorkers(curPos)
      numWorkersVisited += 1
    //如果当前这个worker的空闲内存和cpu数量大于等于Driver需要的
      if (worker.memoryFree >= driver.desc.mem && worker.coresFree >= driver.desc.cores) {
        //启动Driver
        launchDriver(worker, driver)
        //并且将driver从ArrayBuffer中移除
        waitingDrivers -= driver
        launched = true
      }
      //将指针指向下一个worker
      curPos = (curPos + 1) % numWorkersAlive
    }
  }
  startExecutorsOnWorkers()
}
private def launchDriver(worker: WorkerInfo, driver: DriverInfo) {
  logInfo("Launching driver " + driver.id + " on worker " + worker.id)
  //将driver加入worker内存的缓存结构
    将worker内使用的内存和cpu数量,都加上driver需要的内存和cpu数量
  worker.addDriver(driver)
  //同时把worker也加入到driver的缓存结构中
  driver.worker = Some(worker)
  //然后调用worker的RpcEndpoint,给它发送LaunchDriver消息,让worker来启动Driver
  worker.endpoint.send(LaunchDriver(driver.id, driver.desc))
  //将driver的状态设置为Running
  driver.state = DriverState.RUNNING
}

 Application的调度机制(核心之核心 )
 两种算法:一种是spreadOutApps(默认),另一种是非spreadOutApps

 通过spreadOutApps(默认)算法,其实 会将每个application,要启动的executor都平均分配 到每个worker上
 比如有20cpu core,有10个worker,那么实际会遍历两遍,每次循环,每个worker分配一个core
 最后每个worker分配了两个core

非spreadOutApps算法与上面的正好相反,每个application,都尽可能少的分配到worker上去, 比如总共有10个worker,每个有10个core application总共要分配20个core,那么只会分配到两个worker上,每个worker都占满了这10个core那么其它的application只能分配另外的worker上去了。 所以我们在spark-submit中配置了要10个executor,每个execuotr需要2个core 那么共需要20个core,但这种算法中,其实只会启动两个executor,每个executor有10个core

//这个方法就是真正启动executor的方法,在执行这个方法之前,会调用一些其他的验证方法,得到一个结果集合
//assignedCores,这个集合计算出了每个一个work上能分配几个core。通过这个结果,就能知道启动几个executor
private def allocateWorkerResourceToExecutors(
        app: ApplicationInfo,
        assignedCores: Int,
        coresPerExecutor: Option[Int],
        worker: WorkerInfo): Unit = {
    // If the number of cores per executor is specified, we divide the cores assigned
    // to this worker evenly among the executors with no remainder.
    // Otherwise, we launch a single executor that grabs all the assignedCores on this worker.
    //在循环的当前worker里,要启动exec的个数 (该worker的总数core / 每个exec需要的core = exec个数,如果没配置每个exec所需core,则默认为1)
    val numExecutors = coresPerExecutor.map { assignedCores / _ }.getOrElse(1)
    //如果没配置每个exec所需core,直接在这个把分配给这个worker的所有core全部用来启动这个exec,否者按照配置的来
    val coresToAssign = coresPerExecutor.getOrElse(assignedCores)
    for (i <- 1 to numExecutors) {
        val exec = app.addExecutor(worker, coresToAssign)
        launchExecutor(worker, exec)
        app.state = ApplicationState.RUNNING
    }
}
/**
 总结:
 提交任务时指定每个exec分配2个core,启动3个executor
 那么在spark会用默认算法spreadOutApps,平均给每个worker分配资源的情况下
 先计算出总core数  2*3 = 6
 然后给某三个worker一个分配1个exec(assignedCores集合里存两个Int:2,2,2 代表三个worker分别分配2个core)
 然后公式 assignedCores(该worker启动exec所需core总数) / coresPerExecutor(配置的每个exec启动core个数) = (2/2=1)(该worker启动的exe个数)
 然后公式 coresPerExecutor.getOrElse(assignedCores) 到底要启动几个core(2)
 最后:得到了要启动2个core,得到了要启动exec的个数,就循环exec个数来分别启动2个core

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
车间调度问题是指如何合理安排生产车间中各个任务的执行顺序和时间,以最大化生产效率。遗传算法是一种模拟自然进化的优化算法,可以用来解决车间调度问题。 首先,需要定义问题的目标函数。在车间调度问题中,目标是最小化生产完成时间或最大化生产效率。 然后,需要建立遗传算法的基本模型。首先,定义染色体编码,即如何表示调度的解。一种常用的编码方式是使用整数数组,每个位置表示对应任务的执行顺序。然后,定义初始种群,可以随机生成一些解作为初始种群。接下来,定义适应度函数,评价每个解的好坏程度。适应度函数可以根据目标函数来定义,如计算生产完成时间或生产效率。 接下来,进行遗传算法的迭代过程。每一代都经历选择、交叉和变异三个操作。选择操作根据适应度函数对种群进行筛选,选择适应度高的个体用于下一代。交叉操作将选出的个体进行随机组合,生成新的个体。变异操作随机改变某个个体的基因,引入新的解探索。重复进行选择、交叉和变异,直到达到停止条件,如达到一定的迭代次数或找到满意的解。 最后,根据遗传算法得到的最优解,进行车间调度。根据编码方式将解转化为任务的执行顺序和时间,并进行生产调度。 以下是一个简单的车间调度遗传算法建模源码示例(使用Python语言): ```python import random def generate_initial_population(population_size, num_tasks): population = [] for _ in range(population_size): schedule = list(range(1, num_tasks+1)) random.shuffle(schedule) population.append(schedule) return population def fitness_function(schedule): # 计算适应度函数 # 例如,根据生产完成时间或生产效率计算适应度 fitness = ... return fitness def selection(population, num_parents): # 根据适应度函数选择优秀的个体作为父代 parents = sorted(population, key=lambda x: fitness_function(x), reverse=True)[:num_parents] return parents def crossover(parents, num_offsprings): offsprings = [] while len(offsprings) < num_offsprings: parent1, parent2 = random.sample(parents, 2) crossover_point = random.randint(1, len(parent1)-1) offspring = parent1[:crossover_point] + parent2[crossover_point:] offsprings.append(offspring) return offsprings def mutation(offsprings): for offspring in offsprings: if random.random() < mutation_rate: mutation_point1, mutation_point2 = random.sample(range(len(offspring)), 2) offspring[mutation_point1], offspring[mutation_point2] = offspring[mutation_point2], offspring[mutation_point1] return offsprings # 主函数 population_size = 100 num_parents = 20 num_offsprings = 80 mutation_rate = 0.1 num_generations = 100 num_tasks = 10 # 任务数量 population = generate_initial_population(population_size, num_tasks) for generation in range(num_generations): parents = selection(population, num_parents) offsprings = crossover(parents, num_offsprings) offsprings = mutation(offsprings) population = parents + offsprings # 根据最优解进行车间调度 best_solution = max(population, key=lambda x: fitness_function(x)) schedule = best_solution # 将最优解转化为任务的执行顺序和时间 ``` 这是一个基本的车间调度遗传算法建模的源码示例,可以根据具体的问题进行修改和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值