版权声明:本文为原创文章,未经允许不得转载。
复习内容:
Spark中Task的提交源码解读 http://www.cnblogs.com/yourarebest/p/5423906.html

SchedulerBackend是一个trait,它配合TaskSchedulerImpl共同完成Task调度、执行、资源的分配等。它的子类如下所示,不同的子类对应的不同Spark不同的资源分配调度。详见图1。

图1 SchedulerBackend子类继承图

Spark中不同(集群)模式进行资源的分配是通过调用backend.reviveOffers()方法来给Task分配资源的,其调度子类与其负责的运行模式如下所示:
LocalBackend
(1)本地单线程运行模式,master形如local
(2)本地多线程运行模式,匹配local[N]和Local[*],
(3)匹配local[*, M]和local[N, M]
SparkDeploySchedulerBackend
(4)匹配Spark Standalone运行模式
(5)匹配local-cluster运行模式即伪分布模式
YarnClusterSchedulerBackend
(6)"yarn-standalone"或"yarn-cluster"运行模式,
(7)yarn-client运行模式
CoarseMesosSchedulerBackend(粗粒度)和MesosSchedulerBackend(细粒度)
(8)匹配Mesos运行模式,mesos有粗粒度和细粒度两种调度模式。
补充:细粒度模式目前仅支持Mesos。
粗粒度调度模式中,每个Executor在获得系统资源后,就长期拥有,直到应用程序退出才释放资源。优点:减少了资源调度的时间开销,缺点:所分配的资源被某个应用长期占有,造成资源的浪费。
细粒度调度模式中,资源是根据任务的需求动态调度的,任务完成后就还给Mesos,所以不存在资源浪费的问题,但调度延迟较大。

1.LocalBackend

调用远程的一个引用申请资源,该远程引用已在start方法中赋值

override def reviveOffers() {
localEndpoint.send(ReviveOffers)
}

LocalBackend收到远程的ReviveOffers消息在receive方法中进行消息的匹配,进行资源的分配,如下所示:

override def receive: PartialFunction[Any, Unit] = {
case ReviveOffers =>
reviveOffers()详见(1)
case StatusUpdate(taskId, state, serializedData) =>
scheduler.statusUpdate(taskId, state, serializedData)
if (TaskState.isFinished(state)) {
freeCores += scheduler.CPUS_PER_TASK
reviveOffers()
}
case KillTask(taskId, interruptThread) =>
executor.killTask(taskId, interruptThread)
}

(1)方法 reviveOffers()如下所示:

def reviveOffers() {
val offers = Seq(new WorkerOffer(localExecutorId, localExecutorHostname, freeCores))
for (task <- scheduler.resourceOffers(offers).flatten) {
freeCores -= scheduler.CPUS_PER_TASK
//在executor上创建Task
executor.launchTask(executorBackend, taskId = task.taskId, attemptNumber = task.attemptNumber,
task.name, task.serializedTask)
}
}

2.SparkDeploySchedulerBackend

由图1 SchedulerBackend子类继承我们知道SparkDeploySchedulerBackend是类CoarseGrainedSchedulerBackend的子类,属于粗粒度调度模式,类CoarseGrainedSchedulerBackend的子类的调度都是通过它的reviveOffers方法来完成的,因为都属于粗粒度调度模式。
远程引用申请资源

override def reviveOffers() {
driverEndpoint.send(ReviveOffers)
}

同样在receive方法匹配ReviveOffers 消息,调用方法 makeOffers()

override def receive: PartialFunction[Any, Unit] = {
case ReviveOffers =>
makeOffers()详见(1)

(1)makeOffers()方法如下所示

private def makeOffers() {
//过滤掉正在kill的executor
val activeExecutors = executorDataMap.filterKeys(!executorsPendingToRemove.contains(_))
val workOffers = activeExecutors.map { case (id, executorData) =>
new WorkerOffer(id, executorData.executorHost, executorData.freeCores)
}.toSeq
//创建tasks
launchTasks(scheduler.resourceOffers(workOffers))详见(2)(3)
}

(2)scheduler.resourceOffers(workOffers)方法如下所示,TaskScheduleImpl调用提供slaves上的资源。我们通过按照激活的task set的优先级。我们以循环的方式将tasks均匀分配到节点上

def resourceOffers(offers: Seq[WorkerOffer]): Seq[Seq[TaskDescription]] = synchronized {
var newExecAvail = false
//对于每一个Executor进行如下操作,主要进行slave的hostname与executor的映射
for (o <- offers) {
executorIdToHost(o.executorId) = o.host
activeExecutorIds += o.executorId
if (!executorsByHost.contains(o.host)) {
executorsByHost(o.host) = new HashSetString
executorAdded(o.executorId, o.host)
newExecAvail = true
}
for (rack <- getRackForHost(o.host)) {
hostsByRack.getOrElseUpdate(rack, new HashSetString) += o.host
}
}
//随机shuffle操作避免将tasks分配到同样的一批workers上
val shuffledOffers = Random.shuffle(offers)
// Build a list of tasks to assign to each worker.
val tasks = shuffledOffers.map(o => new ArrayBufferTaskDescription)
val availableCpus = shuffledOffers.map(o => o.cores).toArray
//根据调度策略获取TaskSetManage的调度顺序
val sortedTaskSets = rootPool.getSortedTaskSetQueue
for (taskSet <- sortedTaskSets) {
logDebug("parentName: %s, name: %s, runningTasks: %s".format(
taskSet.parent.name, taskSet.name, taskSet.runningTasks))
if (newExecAvail) {
taskSet.executorAdded()
}
}
//根据调度策略依次得到TaskSet,
//在节点上尝试5种Locality,PROCESS_LOCAL, NODE_LOCAL, NO_PREF, RACK_LOCAL, ANY
//以最快的速度执行task
var launchedTask = false
for (taskSet <- sortedTaskSets; maxLocality <- taskSet.myLocalityLevels) {
do {
launchedTask = resourceOfferSingleTaskSet(
taskSet, maxLocality, shuffledOffers, availableCpus, tasks)
} while (launchedTask)
}
if (tasks.size > 0) {
hasLaunchedTask = true
}
return tasks
}

(3)launchTasks方法如下所示

private def launchTasks(tasks: Seq[Seq[TaskDescription]]) {
for (task <- tasks.flatten) {
//序列化task
val serializedTask = ser.serialize(task)
if (serializedTask.limit >= akkaFrameSize - AkkaUtils.reservedSizeBytes) {
scheduler.taskIdToTaskSetManager.get(task.taskId).foreach { taskSetMgr =>
try {
var msg = "Serialized task %s:%d was %d bytes, which exceeds max allowed: " +
"spark.akka.frameSize (%d bytes) - reserved (%d bytes). Consider increasing " +
"spark.akka.frameSize or using broadcast variables for large values."
msg = msg.format(task.taskId, task.index, serializedTask.limit, akkaFrameSize,
AkkaUtils.reservedSizeBytes)
taskSetMgr.abort(msg)
} catch {
case e: Exception => logError("Exception in error callback", e)
}
}
}
else {
val executorData = executorDataMap(task.executorId)
executorData.freeCores -= scheduler.CPUS_PER_TASK
//类CoarseGrainedExecutorBackend在Executor上反序列化task并完成task的创建
executorData.executorEndpoint.send(LaunchTask(new SerializableBuffer(serializedTask)))
}
}
}

3.MesosSchedulerBackend

Mesos细粒度调度模式时通过Mesos中的类MesosSchedulerDriver来完成调度,有兴趣的读者可以看一下。这里就不介绍了

【原】Spark不同运行模式下资源分配源码解读的更多相关文章

  1. Spark的 运行模式详解

    Spark的运行模式是多种多样的,那么在这篇博客中谈一下Spark的运行模式 一:Spark On Local 此种模式下,我们只需要在安装Spark时不进行hadoop和Yarn的环境配置,只要将S ...

  2. Unity非运行模式下实现动画播放/回退工具

    实现效果 核心功能 支持选定模型(带Animator)在非运行模式下,播放/暂停/停止动作. 支持动作单帧前进,单帧回退(帧时间默认0.05f,可以代码设置). 支持滚动条拖拽,将动作调整到指定时间. ...

  3. 【原】Spark中Job的提交源码解读

    版权声明:本文为原创文章,未经允许不得转载. Spark程序程序job的运行是通过actions算子触发的,每一个action算子其实是一个runJob方法的运行,详见文章 SparkContex源码 ...

  4. spark on yarn模式下配置spark-sql访问hive元数据

    spark on yarn模式下配置spark-sql访问hive元数据 目的:在spark on yarn模式下,执行spark-sql访问hive的元数据.并对比一下spark-sql 和hive ...

  5. spark 在yarn模式下提交作业

    1.spark在yarn模式下提交作业需要启动hdfs集群和yarn,具体操作参照:hadoop 完全分布式集群搭建 2.spark需要配置yarn和hadoop的参数目录 将spark/conf/目 ...

  6. 【原】SparkContex源码解读(二)

    版权声明:本文为原创文章,未经允许不得转载. 继续前一篇的内容.前一篇内容为: SparkContex源码解读(一)http://www.cnblogs.com/yourarebest/p/53266 ...

  7. Spark jdbc postgresql数据库连接和写入操作源码解读

    概述:Spark postgresql jdbc 数据库连接和写入操作源码解读,详细记录了SparkSQL对数据库的操作,通过java程序,在本地开发和运行.整体为,Spark建立数据库连接,读取数据 ...

  8. AFNetworking 3.0 源码解读 总结(干货)(下)

    承接上一篇AFNetworking 3.0 源码解读 总结(干货)(上) 21.网络服务类型NSURLRequestNetworkServiceType 示例代码: typedef NS_ENUM(N ...

  9. Spark Streaming揭秘 Day23 启动关闭源码图解

    Spark Streaming揭秘 Day23 启动关闭源码图解 今天主要分析一下SparkStreaming的启动和关闭过程. 从Demo程序出发,主要聚焦在两段代码: 启动代码: 关闭代码: 启动 ...

随机推荐

  1. H5发展简介

    HTML4.01 超文本标记语言,1999年12月24日由W3C组织发布. XHTML 扩展的超文本标记语言(eXtensible Hyper Text Markup Language),和HTML4 ...

  2. Oracle Database does not provide any supplemental logging, which means that by default LogMiner is not usable

    写在前面,在研究Oracle logmnr 的时候看到 http://www.askmaclean.com/archives/dbms_logmnr-unsupported-sqlredo.html ...

  3. [旧博客]Python 第一次

    决定学习Python,发现其需要安装的东西很小,只需要在官方网站下载和15MB左右的安装包,Python for windows.安装完毕就双击IDE快捷方式就打开IDE了. 你很发现IDE好像很面熟 ...

  4. Java基础中的一些注意点

    1.在Java编程语言中,标识符是赋予变量.类或方法的名称.标识符可从一个字母.下划线(_)或美元符号($)开始,随后也可跟数字.标识符是大小写区别对待的并且未规定最大长度. 2.Java技术源程序采 ...

  5. 一步步学习ASP.NET MVC3 (13)——HTML辅助方法

    请注明转载地址:http://www.cnblogs.com/arhat 今天老魏是在十分郁闷,我的一个U盘丢了,心疼里面的资料啊,全部是老魏辛辛苦苦积攒的Linux资料,太心疼,到现在心情还不是很爽 ...

  6. CODEVS 1004四子连棋

    [题目描述 Description] 在一个4*4的棋盘上摆放了14颗棋子,其中有7颗白色棋子,7颗黑色棋子,有两个空白地带,任何一颗黑白棋子都可以向上下左右四个方向移动到相邻的空格,这叫行棋一步,黑 ...

  7. iOS实现地图半翻页效果--老代码备用参考

    // Curl the image up or down CATransition *animation = [CATransition animation]; [animation setDurat ...

  8. Java从入门到精通(一)

    Java编程可以分为三个方向 ① Java se (j2se) 桌面开发 ② Java ee (j2ee) WEB开发 ③ Java me (j2me) 手机开发 java se 是基础中的基础 Ja ...

  9. 解读分库分表中间件Sharding-JDBC

    [编者按]数据库分库分表从互联网时代开启至今,一直是热门话题.在NoSQL横行的今天,关系型数据库凭借其稳定.查询灵活.兼容等特性,仍被大多数公司作为首选数据库.因此,合理采用分库分表技术应对海量数据 ...

  10. 《linux程序设计》笔记 第一章 入门

    linux程序存放位置linux主要有一下几个存放程序的目录: /bin    系统启动程序目录 /usr/bin 用户使用的标准程序 /usr/local/bin   用于存放软件安装目录 /usr ...