此源码版本为2.1.0,若有不准确的地方,欢迎各位指点!
spark的启动是通过${SPARK_HOME}/sbin目录下的start-all.sh来启动的,它调用了spark-config.sh,start-master.sh,start-slaves.sh三个脚本。
spark-config.sh中加载的就是一些配置信息和环境变量。
start-master.sh和start-slaves.sh是启动master和worker进程的脚本。
master通过start-master.sh这个脚本来启动。
CLASS="org.apache.spark.deploy.master.Master"
......
"${SPARK_HOME}/sbin"/spark-daemon.sh start $CLASS 1 \
--ip $SPARK_MASTER_IP --port $SPARK_MASTER_PORT --webui-port $SPARK_MASTER_WEBUI_PORT \
$ORIGINAL_ARGS
在这里通过class指定的路径调用了master这个类,传入如ip,port等参数,这里首先会运行master的伴生类的main方法
- 初始化日志信息
- 读取配置文件
- 创建RPC通信系统
/**
* RpcEnv包含了如下三大核心:
* 1.RpcEndpoint 消息循环体,负责接收并处理消息。Spark中的Master、Worker都是RpcEndpoint 。
* 2.RpcEndpointRef :RpcEndpoint的引用,如果需要和RpcEndpoint通信,
* 就必须获取它的RpcEndpointRef,通过RpcEndpointRef发送消息。
* 3.Dispatcher:消息调度器,负责RPC消息路由到适当的RpcEndpoint。
*/
private[deploy] object Master extends Logging {
val SYSTEM_NAME = "sparkMaster"
val ENDPOINT_NAME = "Master"
def main(argStrings: Array[String]) {
// 初始化log对象
Utils.initDaemon(log)
val conf = new SparkConf
// MasterArguments类在new一个对象的时候会先读取spark-env.sh中的配置信息,然后再读取
// spark-defaults.conf中的数据,然后在获取通过shell传入的参数,如果有重复的值,那么后面的会覆盖前面的值。
// 所以spark中配置信息获取的优先性为:传入参数 > spark-defaults.conf > spark-env.sh
// 构建Master的参数
val args = new MasterArguments(argStrings, conf)
// 创建RpcEnv和Endpoint,spark在akka或netty的基础上封装了一个RPC系统,它负责spark的各个进程之间的通信
// 指定的主机名必须是start-master.sh脚本运行的本地机器名称
val (rpcEnv, _, _) = startRpcEnvAndEndpoint(args.host, args.port, args.webUiPort, conf)
// 等待终止
rpcEnv.awaitTermination()
}
main方法调用了startRpcEnvAndEndpoint方法来开启RPC,这里查看一下此方法的源代码
- 创建Spark安全管理类 - SecurityManager
- 创建RPC的server端 - RpcEnv
- 创建RPC的client端 - Endpoint
- 与master这个client端进行通信
/**
* Start the Master and return a three tuple of:
* (1) The Master RpcEnv
* (2) The web UI bound port
* (3) The REST server bound port, if any
*/
def startRpcEnvAndEndpoint(
host: String,
port: Int,
webUiPort: Int,
conf: SparkConf): (RpcEnv, Int, Option[Int]) = {
// spark中负责安全的类,集群中节点的交流,比如akka,netty的通信还有shuffle阶段
// 使用的BlockTransferService的授权都是由这个类来管理的。
val securityMgr = new SecurityManager(conf)
// 首先创建RpcEnv,也就是server端
val rpcEnv = RpcEnv.create(SYSTEM_NAME, host, port, conf, securityMgr)
// 在RpcEnv中为当前的Master对象创建一个Endpoint,之后就可以直接根据这个Endpoint和Master通信了。
val masterEndpoint = rpcEnv.setupEndpoint(ENDPOINT_NAME,
new Master(rpcEnv, rpcEnv.address, webUiPort, securityMgr, conf))
// 就是向masterEndpoint发送一个BoundPortsRequest,这里会返回一个BoundPortsResponse的信息
val portsResponse = masterEndpoint.askWithRetry[BoundPortsResponse](BoundPortsRequest)
(rpcEnv, portsResponse.webUIPort, portsResponse.restPort)
}
master继承了ThreadSafeRpcEndpoint,所以它本身就是一个Endpoint了,在RPC Endpoint创建的时候,会调用这个Endpoint的OnStart方法。
- 创建Spark的WebUI界面
- 定时检查并删除死掉的worker进程
- 创建StandaloneRestServer
- 向测量系统注册,并启动
- 创建standalone模式下的恢复机制
override def onStart(): Unit = {
logInfo("Starting Spark master at " + masterUrl)
logInfo(s"Running Spark version ${org.apache.spark.SPARK_VERSION}")
// 创建MasterWebUI界面,也就是显示Master信息的页面
webUi = new MasterWebUI(this, webUiPort)
// 绑定web接口后面的Http server
webUi.bind()
// 通过访问这个url可以获取master的信息
masterWebUiUrl = "http://" + masterPublicAddress + ":" + webUi.boundPort
if (reverseProxy) {
masterWebUiUrl = conf.get("spark.ui.reverseProxyUrl", masterWebUiUrl)
logInfo(s"Spark Master is acting as a reverse proxy. Master, Workers and " +
s"Applications UIs are available at $masterWebUiUrl")
}
// 按固定时间间隔检查并remove掉死掉的workers
checkForWorkerTimeOutTask = forwardMessageThread.scheduleAtFixedRate(new Runnable {
override def run(): Unit = Utils.tryLogNonFatalError {
self.send(CheckForWorkerTimeOut)
}
}, 0, WORKER_TIMEOUT_MS, TimeUnit.MILLISECONDS)
// 创建restServer,用于接收提交的application
if (restServerEnabled) {
val port = conf.getInt("spark.master.rest.port", 6066)
restServer = Some(new StandaloneRestServer(address.host, port, conf, self, masterUrl))
}
// 启动restServer
restServerBoundPort = restServer.map(_.start())
// 向master的测量系统注册源
masterMetricsSystem.registerSource(masterSource)
// 启动master的测量系统
masterMetricsSystem.start()
// 启动application的测量系统
applicationMetricsSystem.start()
// Attach the master and app metrics servlet handler to the web ui after the metrics systems are
// started.
// 通过和attachHandler的绑定,可以把源信息展现在桌面上
masterMetricsSystem.getServletHandlers.foreach(webUi.attachHandler)
applicationMetricsSystem.getServletHandlers.foreach(webUi.attachHandler)
// 之后就是standalone模式下恢复机制,(主要介绍下ZooKeeper的方式)
val serializer = new JavaSerializer(conf)
val (persistenceEngine_, leaderElectionAgent_) = RECOVERY_MODE match {
case "ZOOKEEPER" =>
logInfo("Persisting recovery state to ZooKeeper")
//创建持久化引擎,用于持久化一些数据信息,如application,driverdeng
//创建实现leader选举的类,用于在为leader的master出错后,选举别的master为leader
val zkFactory =
new ZooKeeperRecoveryModeFactory(conf, serializer)
(zkFactory.createPersistenceEngine(), zkFactory.createLeaderElectionAgent(this))
case "FILESYSTEM" =>
val fsFactory =
new FileSystemRecoveryModeFactory(conf, serializer)
(fsFactory.createPersistenceEngine(), fsFactory.createLeaderElectionAgent(this))
case "CUSTOM" =>
val clazz = Utils.classForName(conf.get("spark.deploy.recoveryMode.factory"))
val factory = clazz.getConstructor(classOf[SparkConf], classOf[Serializer])
.newInstance(conf, serializer)
.asInstanceOf[StandaloneRecoveryModeFactory]
(factory.createPersistenceEngine(), factory.createLeaderElectionAgent(this))
case _ =>
(new BlackHolePersistenceEngine(), new MonarchyLeaderAgent(this))
}
persistenceEngine = persistenceEngine_
leaderElectionAgent = leaderElectionAgent_
}
到此master的启动已经完成,等待其它进程向其发送消息。