4.master资源调度源码分析(Driver调度和Application调度(两种))
来源:互联网 发布:ps4串流pc软件 编辑:程序博客网 时间:2024/06/03 16:43
master资源调度中 , 分为Driver调度和Application调度
1.Driver调度 :
首先会对在master上注册过的worker进行随机打乱 , 利用Random.shuffle 方法就可以实现 , 同时将状态为ALIVE的worker放进一个hashmap 中 ,
driver调度机制 , driver是程序员编写的程序 , 只有在yarn-cluster集群模式下才会被yarn调用 , 其余两种模式都是本地启动driver , 而不会注册并调度driver
若是yarn-cluster模式下则会遍历在yarn中所有缓存的driver , 同时遍历注册过的所有的worker , 若是worker的内存和cpu数都大于等于driver的内存和cpu数 , 则启动该driver , 并将该driver从缓存队列中清除 , 这也就说明了yarn-cluster模式下driver是运行在一个worker中的 !
源码如下:
/**
* Schedule the currently available resources among waiting apps. This method will be called
* every time a new app joins or resource availability changes.
*
* master的资源调度算法 , 分为平均资源调度算法和非平均资源调度算法
*/
private def schedule() {
// 首先检查master状态是否为ALIVE
if (state != RecoveryState.ALIVE) { return }
// First schedule drivers, they take strict precedence over applications
// Randomization helps balance drivers
//1.Driver调度 : 首先对传入的worker集合进行随机的打乱 , worker必须是ALIVE状态 , shuffledAliveWorkers为一数组,保存了所有活着的worker
val shuffledAliveWorkers = Random.shuffle(workers.toSeq.filter(_.state == WorkerState.ALIVE))
// 记录下状态为ALIVE的worker数量
val numWorkersAlive = shuffledAliveWorkers.size
// 初始化取worker的索引值 , 初始为0 , 最大值为numWorkersAlive
var curPos = 0
// 开始遍历所有等待的Driver , 其实只有在yarn-cluster模式下才会有driver的注册和调度
// 而standalone和client模式都会在上传jar的worker机子上启动driver,没有注册和调度
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
// 表示被拜访过的worker数量 , 该值肯定会小于numWorkersAlive
var numWorkersVisited = 0
// 对于每一个等待队列中的driver , 都需要遍历所有的worker , 检查worker的硬件资源是否符合driver的需求
while (numWorkersVisited < numWorkersAlive && !launched) {
// 获取一个状态为ALIVE的worker , curPos会每循环一次+1
val worker = shuffledAliveWorkers(curPos)
// 当前循环中被拜访的worker数量+1
numWorkersVisited += 1
//若是worker的内存大于等于driver所需要的内存 并且 worker可用的cpu core的数量大于等于dirver所需要的core
if (worker.memoryFree >= driver.desc.mem && worker.coresFree >= driver.desc.cores) {
// 将driver发布到资源符合的那台worker节点上进行启动
launchDriver(worker, driver)
// 该driver从等待队列中移除
waitingDrivers -= driver
// 当前遍历的driver通过 , 退出这次循环 , 进行下一个等待队列的driver
launched = true
}
// 将worker的遍历序号+1
curPos = (curPos + 1) % numWorkersAlive
}
}
上面的源码中只有lauchDriver方法需要看一下 , 如下 :
/**
* 将driver发布到对应的那台worker节点上去
*/
def launchDriver(worker: WorkerInfo, driver: DriverInfo) {
logInfo("Launching driver " + driver.id + " on worker " + worker.id)
// 在worker节点中增加driver
worker.addDriver(driver)
// 给driver的worker赋值
driver.worker = Some(worker)
// 获取worker的actor代理对象 , 发送发布driver的消息
worker.actor ! LaunchDriver(driver.id, driver.desc)
// 更改driver的状态为RUNNING
driver.state = DriverState.RUNNING
}
2.Application资源调度:
有两种 , spreadOutApps(平均分配)调度算法和非spreadOutApps调度算法
spreadOutApps(平均分配)调度算法:
1.首先遍历缓存watingApps中需要调度的Application , 凡是注册过的Application都会存储在这个缓存中 , 并且过滤出还需要调度的core的Application , 然后从workers中遍历出状态为ALIVE且还有剩余cpu的worker , 并对其进行倒序排序;
2.创建一个空数组 , 用来存储分配给每个worker的cpu数;
3.计算出到底要给这个Application分配出多少个core , 原理是取app所需要的cpu数和所有worker中可用cpu数的最小值;
4.遍历3中计算出来的值 , 为每一个worker分配cpu 当worker分配到一个cpu时该worker的cpu数加1 , 同时可用的cpu数减1, 直到分配完cpu为止 , 代码如下:
//2.Application调度 : 默认的情况下采用平均资源调度算法 , spreadOutApps变量可以从SparkConf中设置,master会获取spreadOutApps = conf.getBoolean("spark.deploy.spreadOut", true)
if (spreadOutApps) {
// Try to spread out each app among all the nodes, until it has all its cores
// 遍历所有的缓存队列的Application并且这个Application的还需要分配core
for (app <- waitingApps if app.coresLeft > 0) {
// 过滤出状态ALIVE并且能被Application使用的worker , 最后按照cpu core的数量进行倒序排序
val usableWorkers = workers.toArray.filter(_.state == WorkerState.ALIVE)
.filter(canUse(app, _)).sortBy(_.coresFree).reverse
// numUsable记录 可用的worker数量
val numUsable = usableWorkers.length
// 创建一个空数组 , 用于记录每个worker被分配出去的core
val assigned = new Array[Int](numUsable) // Number of cores to give on each node
// 从当前Application需要的core的数量和所有worker可用的core数量取最小值 , 表示Application可以分配到的core数量
var toAssign = math.min(app.coresLeft, usableWorkers.map(_.coresFree).sum)
// 表示usableWorkers数组中的索引
var pos = 0
// 开始对当前Application需要每个worker节点分配多少core进行循环计算
while (toAssign > 0) {
// 如果当前的worker节点可用的core数量大于当前worker节点已经被分配出去的core的数量 ,那么继续分配1个core给当前的Application
if (usableWorkers(pos).coresFree - assigned(pos) > 0) {
// 当前Application需要被分配的core减1 , 直到为0 , 当为0的时候表示一个Application已经获取了所有的core
toAssign -= 1
// 将当前pos索引表示的worker节点被分配出去的core数量+1
assigned(pos) += 1
}
// 分配下一个worker节点的core
pos = (pos + 1) % numUsable
}
也就说 , 通过上面的算法 , 是将cpu数平均的分配到worker上了 , 可能就不是我们在用submit-shell提交App时指定的cpu数了 ;
5.在可用worker分配到cpu数时 , 那么就在worker启动executor , 并将executor添加到Applicaion的缓存结构中 , 代码如下:
// Now that we've decided how many cores to give on each node, let's actually give them
// 从第一个worker开始遍历 , assigned数组此时已经记录好了当前Application在每个worker上所需要的cpu core数量
for (pos <- 0 until numUsable) {
if (assigned(pos) > 0) {
// 根据worker和当前worker已经分配的core数量创建一个executor
val exec = app.addExecutor(usableWorkers(pos), assigned(pos))
// 在当前executor上发表executor , 也就是创建executor
launchExecutor(usableWorkers(pos), exec)
// 将Application的状态更改为RUNNING
app.state = ApplicationState.RUNNING
}
}
}
非spreadOutApps调度算法如下:
该算符与spreadOutApps截然相反 , 是尽可能少的为这个Application启动worker , 也就说如果一个worker只有10个core可用 , 而Application需要20个core , 那么会将这个worker上的所有的core用完 , 代码如下:
//非平均Application资源调度算法
} else {
// Pack each app into as few nodes as possible until we've assigned all its cores
// 遍历每状态为ALIVE的worker , 并且检查worker的可用core数量是否大于0
for (worker <- workers if worker.coresFree > 0 && worker.state == WorkerState.ALIVE) {
// 遍历Application等待队列中的每一个Application并且判断其已经被分配的core数量是否大于0
for (app <- waitingApps if app.coresLeft > 0) {
// 判断一个Application是否可以在一个worker上运行 , 主要是判断Application所需要的每个节点的内存是否小于Worker节点剩余的内存 , 还要判断worker节点是否已经对这个Application启用了executor
// 只有在worker节点内存容量通过并且没有该Application所对应的executor才能继续往下走
if (canUse(app, worker)) {
// 获取当前worker剩余的cpu core数量与当前Application需要的core数量的最小值
val coresToUse = math.min(worker.coresFree, app.coresLeft)
// 若最小值大于0
if (coresToUse > 0) {
// 在该worker节点上创建一个Application所对应的executor
val exec = app.addExecutor(worker, coresToUse)
// 在该worker节点上发布executor
launchExecutor(worker, exec)
// 更改当前Application的状态为RUNNING
app.state = ApplicationState.RUNNING
}
}
}
}
}
以上就是所有关于master资源调度的源码分析 , 其实源码比较难理解的就是Application的平均资源调度 , 但是道理却比较简单 !
阅读全文
0 0
- 4.master资源调度源码分析(Driver调度和Application调度(两种))
- Master原理剖析与源码分析:资源调度机制源码分析(schedule(),两种资源调度算法)
- Spark资源调度机制源码分析--基于spreadOutApps及非spreadOutApps两种资源调度算法
- day31:Driver在Cluster模式下的启动、两种不同的资源调度方式源码彻底解析、资源调度
- Spark资源调度分配内幕解密:Driver在Cluster模式下的启动、两种不同的资源调度方式源码彻底解析、资源调度内幕总结
- Spark源码分析之Master资源调度算法原理
- spark源码学习(二)---Master源码分析(3)-master对driver、executor的调度
- Driver在Cluster模式下的启动、两种不同的资源调度方式源码彻底解析、资源调度内幕总结
- Zeppelin源码分析-调度和资源分析(1)
- Zeppelin源码分析-调度和资源分析(2)
- 第31课: Spark资源调度分配内幕天机彻底解密:Driver在Cluster模式下的启动、两种不同的资源调度方式源码彻底解析、资源调度内幕总结
- kubernetes1.8 源码分析之资源调度
- Hadoop YARN 内存和CPU两种资源的调度
- Hadoop调度源码分析 任务调度阶段
- Spark1.6.3 Driver端 task调度源码分析
- 资源调度
- Spark系列(七)Master中的资源调度
- Spark Executor Driver资源调度小结
- ROS教程(二):ROS文件系统介绍
- 代码实例
- Spring-boot入门
- 安卓中应用开发
- 动态规划训练12 [G
- 4.master资源调度源码分析(Driver调度和Application调度(两种))
- CodeM资格赛C 优惠券 题解
- Spring相关笔记
- 微信退款开发过程
- H5的 WebSocket
- Leetcode 516. Longest Palindromic Subsequence
- 翻译 – 高性能网站需避免的7个错误
- 百亿级日志处理稳定性保证的一些技巧
- 安卓第一行代码总结(一)