一.Spark心跳概述

前面两节中介绍了Spark RPC的基本知识,以及深入剖析了Spark RPC中一些源码的实现流程。

具体可以看这里:

这一节我们来看看一个Spark RPC中的运用实例--Spark的心跳机制。当然这次主要还是从代码的角度来看。

我们首先要知道Spark的心跳有什么用。心跳是分布式技术的基础,我们知道在Spark中,是有一个Master和众多的Worker,那么Master怎么知道每个Worker的情况呢,这就需要借助心跳机制了。心跳除了传输信息,另一个主要的作用就是Worker告诉Master它还活着,当心跳停止时,方便Master进行一些容错操作,比如数据转移备份等等。

与之前讲Spark RPC一样,我们同样分成两部分来分析Spark的心跳机制,分为服务端(Spark Context)和客户端(Executor)。

二. Spark心跳服务端heartbeatReceiver解析

我们可以发现,SparkContext中有关于心跳的类以及RpcEndpoint注册代码。

class SparkContext(config: SparkConf) extends Logging {
......
private var _heartbeatReceiver: RpcEndpointRef = _
......
//向 RpcEnv 注册 Endpoint。
_heartbeatReceiver = env.rpcEnv.setupEndpoint(HeartbeatReceiver.ENDPOINT_NAME, new HeartbeatReceiver(this))
......
val (sched, ts) = SparkContext.createTaskScheduler(this, master, deployMode)
_schedulerBackend = sched
_taskScheduler = ts
_dagScheduler = new DAGScheduler(this)
_heartbeatReceiver.ask[Boolean](TaskSchedulerIsSet)
......
}

这里rpcEnv已经在上下文中创建好,通过setupEndpoint向rpcEnv注册一个心跳的Endpoint。还记得上一节中HelloworldServer的例子吗,在setupEndpoint方法中,会去调用Dispatcher创建这个Endpoint(这里就是HeartbeatReceiver)对应的Inbox和EndpointRef,然后在Inbox监听是否有新消息,有新消息则处理它。注册完会返回一个EndpointRef(注意这里有Refer,即是客户端,用来发送消息的)。

所以这一句

_heartbeatReceiver = env.rpcEnv.setupEndpoint(HeartbeatReceiver.ENDPOINT_NAME, new HeartbeatReceiver(this))

就已经完成了心跳服务端监听的功能。

那么这条代码的作用呢?

_heartbeatReceiver.ask[Boolean](TaskSchedulerIsSet)

这里我们要看上面那句val (sched, ts) = SparkContext.createTaskScheduler(this, master, deployMode),它会根据master url创建SchedulerBackend和TaskScheduler。这两个类都是和资源调度有关的,所以需要借助心跳机制来传送消息。其中TaskScheduler负责任务调度资源分配,SchedulerBackend负责与Master、Worker通信收集Worker上分配给该应用使用的资源情况。

这里主要是告诉HeartbeatReceiver(心跳)的监听端,告诉它TaskScheduler这个东西已经设置好啦。HeartbeatReceiver就会回应你说好的,我知道的,并持有这个TaskScheduler。

到这里服务端heartbeatReceiver就差不多完了,我们可以发现,HeartbeatReceiver除了向RpcEnv注册并监听消息之外,还会去持有一些资源调度相关的类,比如TaskSchedulerIsSet。

三. Spark心跳客户端发送心跳解析

发送心跳发送在Worker,每个Worker都会有一个Executor,所以我们可以发现在Executor中发送心跳的代码。

private[spark] class Executor(
executorId: String,
executorHostname: String,
env: SparkEnv,
userClassPath: Seq[URL] = Nil,
isLocal: Boolean = false)
extends Logging {
......
// must be initialized before running startDriverHeartbeat()
//创建心跳的 EndpointRef
private val heartbeatReceiverRef = RpcUtils.makeDriverRef(HeartbeatReceiver.ENDPOINT_NAME, conf, env.rpcEnv)
......
startDriverHeartbeater()
......
/**
* Schedules a task to report heartbeat and partial metrics for active tasks to driver.
* 用一个 task 来报告活跃任务的信息以及发送心跳。
*/
private def startDriverHeartbeater(): Unit = {
val intervalMs = conf.getTimeAsMs("spark.executor.heartbeatInterval", "10s") // Wait a random interval so the heartbeats don't end up in sync
val initialDelay = intervalMs + (math.random * intervalMs).asInstanceOf[Int] val heartbeatTask = new Runnable() {
override def run(): Unit = Utils.logUncaughtExceptions(reportHeartBeat())
}
//heartbeater是一个单线程线程池,scheduleAtFixedRate 是定时执行任务用的,和 schedule 类似,只是一些策略不同。
heartbeater.scheduleAtFixedRate(heartbeatTask, initialDelay, intervalMs, TimeUnit.MILLISECONDS)
}
......
}

可以看到,在Executor中会创建心跳的EndpointRef,变量名为heartbeatReceiverRef。

然后我们主要看startDriverHeartbeater()这个方法,它是关键。

我们可以看到最后部分代码

    val heartbeatTask = new Runnable() {
override def run(): Unit = Utils.logUncaughtExceptions(reportHeartBeat())
}
heartbeater.scheduleAtFixedRate(heartbeatTask, initialDelay, intervalMs, TimeUnit.MILLISECONDS)

heartbeatTask是一个Runaable,即一个线程任务。scheduleAtFixedRate则是java concurrent包中用来执行定时任务的一个类,这里的意思是每隔10s跑一次heartbeatTask中的线程任务,超时时间30s。

为什么到这里还是没看到heartbeatReceiverRef呢,说好的发送心跳呢?别急,其实在heartbeatTask线程任务中又调用了另一个方法,我们到里面去一探究竟。

private[spark] class Executor(
executorId: String,
executorHostname: String,
env: SparkEnv,
userClassPath: Seq[URL] = Nil,
isLocal: Boolean = false)
extends Logging {
......
private def reportHeartBeat(): Unit = {
// list of (task id, accumUpdates) to send back to the driver
val accumUpdates = new ArrayBuffer[(Long, Seq[AccumulatorV2[_, _]])]()
val curGCTime = computeTotalGcTime() for (taskRunner <- runningTasks.values().asScala) {
if (taskRunner.task != null) {
taskRunner.task.metrics.mergeShuffleReadMetrics()
taskRunner.task.metrics.setJvmGCTime(curGCTime - taskRunner.startGCTime)
accumUpdates += ((taskRunner.taskId, taskRunner.task.metrics.accumulators()))
}
} val message = Heartbeat(executorId, accumUpdates.toArray, env.blockManager.blockManagerId)
try {
//终于看到 heartbeatReceiverRef 的身影了
val response = heartbeatReceiverRef.askWithRetry[HeartbeatResponse](
message, RpcTimeout(conf, "spark.executor.heartbeatInterval", "10s"))
if (response.reregisterBlockManager) {
logInfo("Told to re-register on heartbeat")
env.blockManager.reregister()
}
heartbeatFailures = 0
} catch {
case NonFatal(e) =>
logWarning("Issue communicating with driver in heartbeater", e)
heartbeatFailures += 1
if (heartbeatFailures >= HEARTBEAT_MAX_FAILURES) {
logError(s"Exit as unable to send heartbeats to driver " +
s"more than $HEARTBEAT_MAX_FAILURES times")
System.exit(ExecutorExitCode.HEARTBEAT_FAILURE)
}
}
}
...... }

可以看到,这里heartbeatReceiverRef和我们上一节的例子,HelloworldClient类似,核心也是调用了askWithRetry()方法,这个方法是通过同步的方式发送Rpc消息。而这个方法里其他代码其实就是获取task的信息啊,或者是一些容错处理。核心就是调用askWithRetry()方法来发送消息。

看到这你就明白了吧。Executor初始化便会用一个定时任务不断发送心跳,同时当有task的时候,会获取task的信息一并发送。这就是心跳的大概内容了。

总的来说Spark心跳的代码也是比较杂的,不过这些也都是为了让设计更加高耦合,低内聚,让这些代码更加方便得复用。不过通过层层剖析,我们还是发现其实它底层就是我们之前说到的Spark RPC框架的内容!!

OK,Spark RPC三部曲完毕。如果你能看到这里那不容易呀,给自己点个赞吧!!


推荐阅读 :

从分治算法到 MapReduce

大数据存储的进化史 --从 RAID 到 Hadoop Hdfs

一个故事告诉你什么才是好的程序员

Spark RPC框架源码分析(三)Spark心跳机制分析的更多相关文章

  1. Spark RPC框架源码分析(一)简述

    Spark RPC系列: Spark RPC框架源码分析(一)运行时序 Spark RPC框架源码分析(二)运行时序 Spark RPC框架源码分析(三)运行时序 一. Spark rpc框架概述 S ...

  2. Spark RPC框架源码分析(二)RPC运行时序

    前情提要: Spark RPC框架源码分析(一)简述 一. Spark RPC概述 上一篇我们已经说明了Spark RPC框架的一个简单例子,Spark RPC相关的两个编程模型,Actor模型和Re ...

  3. 框架源码系列三:手写Spring AOP(AOP分析、AOP概念学习、切面实现、织入实现)

    一.AOP分析 问题1:AOP是什么? Aspect Oriented Programming 面向切面编程,在不改变类的代码的情况下,对类方法进行功能增强. 问题2:我们需要做什么? 在我们的框架中 ...

  4. Java集合框架源码(三)——arrayList

    1. ArrayList概述: ArrayList是List接口的可变数组的实现.实现了所有可选列表操作,并允许包括 null 在内的所有元素.除了实现 List 接口外,此类还提供一些方法来操作内部 ...

  5. Spark Scheduler模块源码分析之TaskScheduler和SchedulerBackend

    本文是Scheduler模块源码分析的第二篇,第一篇Spark Scheduler模块源码分析之DAGScheduler主要分析了DAGScheduler.本文接下来结合Spark-1.6.0的源码继 ...

  6. 【原】Spark中Client源码分析(二)

    继续前一篇的内容.前一篇内容为: Spark中Client源码分析(一)http://www.cnblogs.com/yourarebest/p/5313006.html DriverClient中的 ...

  7. 【原】Spark中Master源码分析(一)

    Master作为集群的Manager,对于集群的健壮运行发挥着十分重要的作用.下面,我们一起了解一下Master是听从Client(Leader)的号召,如何管理好Worker的吧. 1.家当(静态属 ...

  8. Spark Scheduler模块源码分析之DAGScheduler

    本文主要结合Spark-1.6.0的源码,对Spark中任务调度模块的执行过程进行分析.Spark Application在遇到Action操作时才会真正的提交任务并进行计算.这时Spark会根据Ac ...

  9. Apache Spark源码走读之6 -- 存储子系统分析

    欢迎转载,转载请注明出处,徽沪一郎. 楔子 Spark计算速度远胜于Hadoop的原因之一就在于中间结果是缓存在内存而不是直接写入到disk,本文尝试分析Spark中存储子系统的构成,并以数据写入和数 ...

随机推荐

  1. <<C语言--神奇的指针>>

    指针很简单 ------引子 学计算机语言,首先推荐C语言.无论是数据结构还是算法,站在C语言的角度,会让我们理解的更加清晰透彻. 但是,指针不太"友好",让很多人抓狂,头疼.不少 ...

  2. Eclipse4JavaEE安装SpringBoot

    第一步:下载SpringBoot SpringBoot官网下载链接 第二步:在Eclipse里进行安装 打开Eclipse,菜单栏Help ->Install New Software,进入下图 ...

  3. asp.net Core 中AuthorizationHandler 实现自定义授权

    前言 ASP.NET Core 中 继承的是AuthorizationHandler ,而ASP.NET Framework 中继承的是AuthorizeAttribute. 它们都是用过重写里面的方 ...

  4. XSS Challenges xss-quiz.int21h.jp

    概述: https://xss-quiz.int21h.jp/ Stage #1 payload: <script>alert(document.domain);</script&g ...

  5. 联发科Helio P90(mt6779),P70(mt6775),P60(MT6771),P35,P22(MT6762)芯片参数规格

    Helio P90(mt6779)是一款人工智能处理平台,集成了超级强大的AI专核APU 2.0,具有超强的AI性能和一系列基于人工智能的成像升级.该芯片将重新定义消费者对智能手机AI功能的体验.He ...

  6. 一目了然呀的VS2017 Live Test

    刚刚试用了一下VS2017中的单元测试,发现,这一次,覆盖测试会自动标记出来.不用像以前一样要他细检查了.这次会自动帮你全部标记出来. 新建单元测试,使用MS的单元测试方案(VSTS使用的时候方便.) ...

  7. x宝23大洋包邮的老式大朝华MP3播放器简单评测

    (纯兴趣测评,非广告) 最近逛X宝,看到了这个古董级MP3播放器居然还在售,于是脑抽+情怀泛滥买了一个. 然后呢,从遥远的深圳跨越好几千公里邮过来了这个玩意: 那节南孚5号电池是我自己的,是为了对比一 ...

  8. MySQL教程之concat以及group_concat的用法

    本文中使用的例子均在下面的数据库表tt2下执行: 一.concat()函数 1.功能:将多个字符串连接成一个字符串. 2.语法:concat(str1, str2,...) 返回结果为连接参数产生的字 ...

  9. 【Python实践-1】求一元二次方程的两个解

    知识点: import sys, sys模块包含了与Python解释器和它的环境有关的函数. “sys”是“system”的缩写.sys.exit() 中途退出程序, (注:0是正常退出,其他为不正常 ...

  10. 每日分享!介绍Css 盒模型!

    如何定义盒模型: 在CSS盒子模型理论中,页面中所有的元素都是看成一个盒子,并且还占据一定的空间. 一个页面是由很多这样的盒子组成的.这些盒子之间都会相会影响,因此我们掌握CSS盒模型相当重要.需要理 ...