Spark源码解析(二)集群启动脚本分析

Spark执行过程【源码分析+图示】——启动脚本(standalone模式) 文章目录前言1. 执行start-master.sh脚本1.1 源码分析1.2 图示2. 执行start-slaves.sh脚本2.1 源码分析2.1.1 Worker的main方法2.1.2 Worker执行onStart方法(内部发送消息去Master注册)2.1.3 Master收到RegisterWorker消息2.1.4 Worker注册成功收到RegisteredWorker消息2.1... 阅读详情

通过学习Spark源码为了更深入的了解Spark。主要按照以下流程进行Spark的源码分析,包含了Spark集群的启动以及任务提交的执行流程:

  1. Spark RPC分析
  2. start-all.sh
  3. Master启动分析
  4. Work启动分析
  5. spark-submit.sh脚本分析
  6. SparkSubmit分析
  7. SparkContext初始化

2.start-all.sh

源码分析,我这里使用的Spark版本是Spark2.4.7。使用scala版本是Scala2.11。

1.start-all.sh  内部执行start-master.sh和start-slaves.sh。

if [ -z "${SPARK_HOME}" ]; then
  export SPARK_HOME="$(cd "`dirname "$0"`"/..; pwd)"
fi

# Load the Spark configuration
. "${SPARK_HOME}/sbin/spark-config.sh"

# Start Master
"${SPARK_HOME}/sbin"/start-master.sh

# Start Workers
"${SPARK_HOME}/sbin"/start-slaves.sh

2.start-master.sh和start-slaves.sh都有调用spark-daemon.sh

start-master.sh

"${SPARK_HOME}/sbin"/spark-daemon.sh start $CLASS 1 \
  --host $SPARK_MASTER_HOST --port $SPARK_MASTER_PORT --webui-port $SPARK_MASTER_WEBUI_PORT \
  $ORIGINAL_ARGS


start-slaves.sh

"${SPARK_HOME}/sbin"/spark-daemon.sh start $CLASS $WORKER_NUM \
   --webui-port "$WEBUI_PORT" $PORT_FLAG $PORT_NUM $MASTER "$@"

3.spark-daemon.sh内部调用spark-class最终转到执行

  • Master启动:spark-class org.apache.spark.deploy.master.Master
  • Worker启动:spark-class org.apache.spark.deploy.worker.Worker

最终会跳到对应的main方法。

3.Master启动分析

    def main(argStrings: Array[String]) {
        Thread.setDefaultUncaughtExceptionHandler(new SparkUncaughtExceptionHandler(exitOnUncaughtException = false))
        
        // TODO 注释:初始化日志输出
        Utils.initDaemon(log)
        
        // TODO 注释:创建conf对象其实是读取Spark默认配置的参数
        val conf = new SparkConf
        
        /**
          *
          * 1.解析main方法传入的参数
          * 2.解析配置文件,加载集群启动时需要解析的配置文件。
          */
        val args = new MasterArguments(argStrings, conf)
        
        // TODO 注释:初始化RPC服务和终端
        /**
、
         *   注释:
         *   1、RpcEnv
         *   2、Endpoint
         */
        val (rpcEnv, _, _) = startRpcEnvAndEndpoint(args.host, args.port, args.webUiPort, conf)
        
        // TODO 注释: 等待结束
        rpcEnv.awaitTermination()
    }

分析startRpcEnvAndEndpoint方法。这里初始化RpcEnv和启动MsterEndpoint。

  def startRpcEnvAndEndpoint(host: String, port: Int, webUiPort: Int, conf: SparkConf): (RpcEnv, Int, Option[Int]) = {
        
        // TODO 注释:初始化securityManager
        val securityMgr = new SecurityManager(conf)
        
        // TODO 注释:初始化RPCEnv
        val rpcEnv = RpcEnv.create(SYSTEM_NAME, host, port, conf, securityMgr)
        
        // TODO 注释:向RPC注册master终端,涉及到Master的实例化
        /**
         *   注释: rpcEnv.setupEndpoint 会直接初始化和启动 EndPoint
         *   参数解析
         *   1、ENDPOINT_NAME endpoint的名字
         *   2、new Master()  endpoint 实例对象
         *   由于这个 endpoint 就是 Master实例:所以代码跳转到两个地方:
         *   1、master的构造法方法
         *   2、master的 onStart()
         *      做了三件事:
         *      1、启动 master web ui
         *      2、启动定时任务检查 worker 状态
         *      3、如果启用了 HA,则 master 参与选举
         */
        val masterEndpoint = rpcEnv.setupEndpoint(ENDPOINT_NAME, new Master(rpcEnv, rpcEnv.address, webUiPort, securityMgr, conf))
        
        // TODO 注释:rest的绑定端口
        val portsResponse = masterEndpoint.askSync[BoundPortsResponse](BoundPortsRequest)
        
        // TODO 注释:返回结果
        (rpcEnv, portsResponse.webUIPort, portsResponse.restPort)
    }

对于代码的各个不做详细介绍。这里查看master的onStart方法中的定时任务启动

/**
 *   scheduleAtFixedRate 启动定时任务
 *   注释:启用定时任务,心跳机制,向自己发送CheckForWorkerTimeOut消息,用于检测Worker是否超时
 *   跟踪代码可知,最终会调用 timeOutDeadWorkers(),用于检测超时的Worker,并移除。
  *  浏览器中会展示子节点信息。
 */
checkForWorkerTimeOutTask = forwardMessageThread.scheduleAtFixedRate(new Runnable {
    override def run(): Unit = Utils.tryLogNonFatalError {
        /**
         *   注释: CheckForWorkerTimeOut  检查 worker 状态的
         *   1、ref   发给别人
         *   2、self   发给自己
         */
        self.send(CheckForWorkerTimeOut)
    }
    // 每隔 WORKER_TIMEOUT_MS 毫秒执行上面的这个run方法
}, 0, WORKER_TIMEOUT_MS, TimeUnit.MILLISECONDS)

下面代码是master选举的HA机制

/**
 *   注释: HA 的事儿  多个master之间的选举
 *   spark-env.sh
 *   RECOVERY_MODE = spark.deploy.recoveryMode = ZOOKEEPER
 */
val (persistenceEngine_, leaderElectionAgent_) = RECOVERY_MODE match {
	/**
	 *   注释:ZOOKEEPER 是自動管理 Master
	 */
	case "ZOOKEEPER" => logInfo("Persisting recovery state to ZooKeeper")
		val zkFactory = new ZooKeeperRecoveryModeFactory(conf, serializer)
		
		// TODO 注释:zkFactory.createLeaderElectionAgent(this) 选举
		(zkFactory.createPersistenceEngine(), zkFactory.createLeaderElectionAgent(this))
	
	/**
	 *   注释:FILESYSTEM 的方式在 Master 出现突障后需要手动启动机器,
	 *   机器启动后会立即成为 Active 级别的 Master 来对外提供服务(接受应用程序提交的请求、接受新的 Job 运行的请求)
	 */
	case "FILESYSTEM" => val fsFactory = new FileSystemRecoveryModeFactory(conf, serializer)
		(fsFactory.createPersistenceEngine(), fsFactory.createLeaderElectionAgent(this))
	
	/**
	 *   注释:CUSTOM 的方式允许用户自定义 Master HA 的实现,这对于高级用户特别有用;
	 */
	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))
	
	/**
	 *   注释:这是默应情况,当我们下载安装了 Spark 集群中就是采用这种方式,该方式不会持久化集群的数据,
	 *   Driver, Application, Worker and Executor. Master 启动起立即管理集群;
	 */
	case _ => (new BlackHolePersistenceEngine(), new MonarchyLeaderAgent(this))
}

4.Work启动分析

worker中的main方法

def main(argStrings: Array[String]) {
	Thread.setDefaultUncaughtExceptionHandler(new SparkUncaughtExceptionHandler(exitOnUncaughtException = false))
	Utils.initDaemon(log)
	val conf = new SparkConf
	val args = new WorkerArguments(argStrings, conf)
	
	/**
	 *   注释:
	 *   1、RpcEnv
	 *   2、Endpoint
	 */
	val rpcEnv = startRpcEnvAndEndpoint(args.host, args.port, args.webUiPort, args.cores, args.memory, args.masters, args.workDir, conf = conf)

	val externalShuffleServiceEnabled = conf.get(config.SHUFFLE_SERVICE_ENABLED)
	val sparkWorkerInstances = scala.sys.env.getOrElse("SPARK_WORKER_INSTANCES", "1").toInt
	require(externalShuffleServiceEnabled == false || sparkWorkerInstances <= 1,
		"Starting multiple workers on one host is failed because we may launch no more than one " + "external shuffle service on each host, please set spark.shuffle.service.enabled to " + "false or set SPARK_WORKER_INSTANCES to 1 to resolve the conflict.")
	
	/**
	 *   注释:
	 */
	rpcEnv.awaitTermination()
}

Worer实例创建方法startRpcEnvAndEndpoint。和master创建方法很类似

def startRpcEnvAndEndpoint(host: String, port: Int, webUiPort: Int, cores: Int, memory: Int, masterUrls: Array[String], workDir: String,
        workerNumber: Option[Int] = None, conf: SparkConf = new SparkConf): RpcEnv = {
        
        // The LocalSparkCluster runs multiple local sparkWorkerX RPC Environments
        val systemName = SYSTEM_NAME + workerNumber.map(_.toString).getOrElse("")
        val securityMgr = new SecurityManager(conf)
        val rpcEnv = RpcEnv.create(systemName, host, port, conf, securityMgr)
        val masterAddresses = masterUrls.map(RpcAddress.fromSparkURL(_))
        
        /**
         *   注释: 创建 Worker 实例
         *   这句代码会做重要的两件事
         *   1、Worker 的构造方法
         *   2、Worker 的 onStart 方法
         */
        rpcEnv.setupEndpoint(ENDPOINT_NAME, new Worker(rpcEnv, webUiPort, cores, memory, masterAddresses, ENDPOINT_NAME, workDir, conf, securityMgr))
        rpcEnv
}

这里主要查看Worker中的onStart方法

/**
 *   注释: 执行生命周期方法
 *   1、创建工作目录 createWorkDir()
 *   2、startExternalShuffleService() 启动 Shuffle 和 RPC 服务
 *   3、启动 worker 的webui
 *   4、worker 启动了之后要向 Master汇报registerWithMaster()
 */
override def onStart() {
	assert(!registered)
	logInfo("Starting Spark worker %s:%d with %d cores, %s RAM".format(host, port, cores, Utils.megabytesToString(memory)))
	logInfo(s"Running Spark version ${org.apache.spark.SPARK_VERSION}")
	logInfo("Spark home: " + sparkHome)
	
	// TODO_MA 注释:创建工作目录
	createWorkDir()
	
	/**
	 *   注释: 创建 RPC 服务
	 *   ExternalShuffleService是一个单独的进程服务,默认不开启
	 *   用于帮助Executor处理shuffle,降低Executor的压力
	 */
	startExternalShuffleService()
	
	/**
	 *   注释: 启动 web 服务
	 */
	webUi = new WorkerWebUI(this, workDir, webUiPort)
	webUi.bind()
	workerWebUiUrl = s"http://$publicAddress:${webUi.boundPort}"
	
	/**
	 *   注释: 向 Master 注册
	 */
	registerWithMaster()
	
	/**
	 *   注释:启动metricsSystem,用于度量各种指标
	 */
	metricsSystem.registerSource(workerSource)
	metricsSystem.start() // Attach the worker metrics servlet handler to the web ui after the metrics system is started.
	metricsSystem.getServletHandlers.foreach(webUi.attachHandler)
}

可以根据startExternalShuffleService()方法查看创建Worker RPC服务端。核心代码如下:

def start() {
	require(server == null, "Shuffle server already started")
	val authEnabled = securityManager.isAuthenticationEnabled()
	logInfo(s"Starting shuffle service on port $port (auth enabled = $authEnabled)")
	val bootstraps: Seq[TransportServerBootstrap] = if (authEnabled) {
		Seq(new AuthServerBootstrap(transportConf, securityManager))
	} else {
		Nil
	}
	
	/**
	 *   注释: 创建 RPC 服务端
	 *   TransportServer server = transportContext.createServer(port, bootstraps.asJava)
	 */
	server = transportContext.createServer(port, bootstraps.asJava)
	
	shuffleServiceSource.registerMetricSet(server.getAllMetrics)
	shuffleServiceSource.registerMetricSet(blockHandler.getAllMetrics)
	masterMetricsSystem.registerSource(shuffleServiceSource)
	masterMetricsSystem.start()
}

Worker创建好后向master注册registerWithMaster()方法。核心代码如下:

/**
 * 有可能master有多个
 */
private def tryRegisterAllMasters(): Array[JFuture[_]] = {
	masterRpcAddresses.map { masterAddress =>
		registerMasterThreadPool.submit(new Runnable {
			override def run(): Unit = {
				try {
					logInfo("Connecting to master " + masterAddress + "...")
					
					/**
					 *   注释: 一定是获取 Master 的 引用对象
					 *   要给 master发消息,就必须获取到 master 中的 endpoint 的 ref
					 */
					val masterEndpoint = rpcEnv.setupEndpointRef(masterAddress, Master.ENDPOINT_NAME)
					
					/**            
					 *   注释: 发送注册消息给 master
					 */
					sendRegisterMessageToMaster(masterEndpoint)
				} catch {
					case ie: InterruptedException => // Cancelled
					case NonFatal(e) => logWarning(s"Failed to connect to master $masterAddress", e)
				}
			}
		})
	}
}

这里包含了获取master的引用对象和发送消息给master。这里继续深入查看发送消息代码如下:

private def sendRegisterMessageToMaster(masterEndpoint: RpcEndpointRef): Unit = {
	
	/**
	 *   注释: 发送消息
	 *   RegisterWorker 就是 Worker 的注册消息
	 *   所以
	 *   Master 的 receive 方法会接受到  RegisterWorker 注册消息
	 *   Worker 的 receive 方法也会收到 Master 返回回来的注册成功消息
	 */
	masterEndpoint.send(RegisterWorker(workerId, host, port, self, cores, memory, workerWebUiUrl, masterEndpoint.address))
}

当Worker向master发送RegisterWorker后,可以在master的receive方法中看到RegisterWorker接收执行代码:

/**
 *   注释: 处理 Worker 注册
 */
case RegisterWorker(id, workerHost, workerPort, workerRef, cores, memory, workerWebUiUrl, masterAddress) => logInfo(
	"Registering worker %s:%d with %d cores, %s RAM".format(workerHost, workerPort, cores, Utils.megabytesToString(memory)))
	if (state == RecoveryState.STANDBY) {
		workerRef.send(MasterInStandby)
	} else if (idToWorker.contains(id)) {
		workerRef.send(RegisterWorkerFailed("Duplicate worker ID"))
	} else {
		// TODO 注释:过来注册的 worker的信息的封装
		val worker = new WorkerInfo(id, workerHost, workerPort, cores, memory, workerRef, workerWebUiUrl)
		
		// TODO 注释:将 worker 信息注册到 Master 的 workers set 中
		if (registerWorker(worker)) {
			// TODO 注释:注册成功
			persistenceEngine.addWorker(worker)
			
			// TODO 注释:返回注册成功消息
			/**
			 *   注释:
			 *   1、workerRef 是 Wroker 的endpoint 的代理
			 *   2、给 worker 发回一个 RegisteredWorker 的消息
			  *  返回给worker
			 */
			workerRef.send(RegisteredWorker(self, masterWebUiUrl, masterAddress))
			
			// TODO 注释: 参加工作,加入调度
			schedule()
		} else {
			val workerAddress = worker.endpoint.address
			logWarning("Worker registration failed. Attempted to re-register worker at same " + "address: " + workerAddress)
			workerRef.send(RegisterWorkerFailed("Attempted to re-register worker at same address: " + workerAddress))
		}
	}

当master给work发送RegisteredWorker Class。注意这个Class是有实现RegisterWorkerResponse

  case class RegisteredWorker(
      master: RpcEndpointRef,
      masterWebUiUrl: String,
      masterAddress: RpcAddress) extends DeployMessage with RegisterWorkerResponse

那么在slave的receive中用RegisterWorkerResponse接收数据。继而去执行handleRegisterResponse方法

override def receive: PartialFunction[Any, Unit] = synchronized {
	case msg: RegisterWorkerResponse => handleRegisterResponse(msg)
}

在handleRegisterResponse方法中,返回注册成功消息,会给自己发送心跳消息SendHeartbeat

private def handleRegisterResponse(msg: RegisterWorkerResponse): Unit = synchronized {
	msg match {
		/**
		 *   注释: 处理注册成功
		 */
		case RegisteredWorker(masterRef, masterWebUiUrl, masterAddress) =>
			if (preferConfiguredMasterAddress) {
			logInfo("Successfully registered with master " + masterAddress.toSparkURL)
		} else {
			logInfo("Successfully registered with master " + masterRef.address.toSparkURL)
		}
			registered = true
			changeMaster(masterRef, masterWebUiUrl, masterAddress)
			
			/**
			 *   注释:在 Worker 启动之后,就开始向 Master 进行注册,注册之后,会收到一个 RegisterWorkerResponse 的信息
			 */
			forwordMessageScheduler.scheduleAtFixedRate(new Runnable {
				override def run(): Unit = Utils.tryLogNonFatalError {
					/**
					 *   注释: 发送心跳 给自己。
					 */
					self.send(SendHeartbeat)
				}
			}, 0, HEARTBEAT_MILLIS, TimeUnit.MILLISECONDS)
			if (CLEANUP_ENABLED) {
				logInfo(s"Worker cleanup enabled; old application directories will be deleted in: $workDir")
				forwordMessageScheduler.scheduleAtFixedRate(new Runnable {
					override def run(): Unit = Utils.tryLogNonFatalError {
						self.send(WorkDirCleanup)
					}
				}, CLEANUP_INTERVAL_MILLIS, CLEANUP_INTERVAL_MILLIS, TimeUnit.MILLISECONDS)
			}
			
			val execs = executors.values.map { e =>
				new ExecutorDescription(e.appId, e.execId, e.cores, e.state)
			}
			masterRef.send(WorkerLatestState(workerId, execs.toList, drivers.keys.toSeq))
		case RegisterWorkerFailed(message) => if (!registered) {
			logError("Worker registration failed: " + message)
			System.exit(1)
		}
		case MasterInStandby => // Ignore. Master not yet ready.
	}
}

当有给自己发送SendHeartbeat时候,会发现给master发送心跳Heartbeat

case SendHeartbeat => if (connected) {
	sendToMaster(Heartbeat(workerId, self))
}

在Master中会看到对应的处理,如下:

/**
 *   注释:心跳
 *   master启动之后,会等待 Worker 启动好了之后,过来汇报。
 *   这里就是处理 Worker 在注册成功只有,发送过来的心跳信息
 */
case Heartbeat(workerId, worker) => idToWorker.get(workerId) match {
	// TODO 注释: 每一个woker发送心跳来了之后,master 都会更新他们的最后一次心跳的时间
	case Some(workerInfo) => workerInfo.lastHeartbeat = System.currentTimeMillis()
	case None => if (workers.map(_.id).contains(workerId)) {
		logWarning(s"Got heartbeat from unregistered worker $workerId." + " Asking it to re-register.")
		worker.send(ReconnectWorker(masterUrl))
	} else {
		logWarning(s"Got heartbeat from unregistered worker $workerId." + " This worker was never registered, so ignoring the heartbeat.")
	}
}

当Worker向Master注册成功后,执行schedule()方法,如下。

/**
 *   注释: 在 master 中, 每一个 worker 上线之后,向 master 注册,注册成功 则证明他可以参加工作
 *   执行 schedule 调度方法,具体做两件事,根据需要启动 driver 或者 executor
 *   1、有可能:launchDriver(worker, driver)
 *   2、startExecutorsOnWorkers()
 */
private def schedule(): Unit = {
	if (state != RecoveryState.ALIVE) {
		return
	}
	
	// TODO 注释:计算存活的 Worker 有多少个
	// Drivers take strict precedence over executors
	val shuffledAliveWorkers = Random.shuffle(workers.toSeq.filter(_.state == WorkerState.ALIVE))
	val numWorkersAlive = shuffledAliveWorkers.size
	
	// TODO 注释:把等待队列中的待启动的 Driver 拿出来启动
	var curPos = 0
	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
		while (numWorkersVisited < numWorkersAlive && !launched) {
			val worker = shuffledAliveWorkers(curPos)
			numWorkersVisited += 1
			
			// TODO 注释: 如果满足,Driver 启动需要的 CPU 和 Memory 资源,则启动 Driver
			if (worker.memoryFree >= driver.desc.mem && worker.coresFree >= driver.desc.cores) {
				/**
				 *   注释: 启动 Driver
				 */
				launchDriver(worker, driver)
				waitingDrivers -= driver
				launched = true
			}
			curPos = (curPos + 1) % numWorkersAlive
		}
	}
	
	/**
	 *   注释:启动所有 Worker 节点上的 Executor
	 */
	startExecutorsOnWorkers()
}

spark源码分析—spark core(一):集群启动 spark集群通过spark的start-all.sh脚本进行启动,所以首先我们看一下该脚本的内容,该脚本内容很简单,它会通过调用相同目录下的start-master.sh脚本启动spark Master服务,调用start-slaves.sh脚本启动spark Worker服务。大家注意start-master.sh脚本只会在调用该脚本的机器启动Mater服务,如果Master是HA的话,需要到... 阅读详情

相关推荐

一文带你解析Spark的源码:从Spark-Submit任务提交到资源分配Executor

随着互联网的告诉发展,每天会有大量的数据产生,传统的数据分析手段已经不能满足现在的需求。Apache Spark是专为大规模数据处理而设计的快速通用的计算引擎,现在形成一个高速发展应用广泛的生态系统。Spark由于其基于内存的计算模式,能够很好的应用于各个应用场景,包括离线数据分析、在线 数据分析和机器学习等。要想深入的了解Spark,学习其源码实现是必不可少的过程。 在本场 Chat 中,会讲到...

GitChat 4737

Spark内核源码分析(一)

这里主要是sparkSubmit提交,客户端到YarnCluster模式

luis_0514的博客 1054

spark源码分析Master与Worker启动流程篇

spark通信流程 概述 spark作为一套高效的分布式运算框架,但是想要更深入的学习它,就要通过分析spark的源码,不但可以更好的帮助理解spark的工作过程,还可以提高对集群的排错能力,本文主要关注的是Spark的Master的启动流程与Worker启动流程。 现在Spark最新版本为1.6,但是代码的逻辑不够清晰,不便于理解,这里以1.3为准Master启动我们启动一个Mast

那年的坏人的专栏 4207

Spark运行脚本 start-all.sh(standalone)

#!/usr/bin/env bash # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional informat...

leofionn的博客 1559

Spark On YARN启动流程源码分析

1、spark-submit入口介绍 一般的spark作业都是通过命令行spark-submit相关的指令来进行提交,使用--master yarn来指定提交到对应的yarn集群上,如下: ./bin/spark-submit --class org.apache.spark.examples.SparkPi \ --master yarn \ --deploy-mode cluster \ --driver-memory 4g \ --executor-memory

METON的博客 1590

Simulink Test Sequence自动化测试实战:从Excel到模型验证

本文详细介绍了如何利用Excel和MATLAB脚本实现Simulink Test Sequence的自动化测试。通过设计结构化Excel模板管理测试数据,并调用Simulink Test API批量创建与执行测试用例,该方法能显著提升复杂模型单元测试的效率,便于集成到CI/CD流程,实现从数据到验证的全自动化。

weixin_29261711的博客 144

1. Spark源码解析之启动脚本解析

从零开始解读Spark源码。前期记录详细点。 Spark启动方式主要有两种:start-all.sh一键启动,start-master.sh和start-slave.sh单独启动master和slave。 运行Spark的方式也是两种:spark-shell和spark-submit。 这里解析所有相关启动脚本。 Spark集群启动脚本 start-all.sh start-mas...

L, there! 1156

【元宵快乐】猜灯谜吃元宵 元气满满闹元宵~(附猜灯谜小游戏)

导语 元宵节,又称上元节、灯节,是春节之后的第一个重要节日。 相传,汉文帝(前179—前157年)为庆祝周勃于正月十五勘平诸吕之乱,每逢此夜,必出宫游玩,与民 同乐,在古代,夜同宵,正月又称元月,汉文帝就将正月十五定为元宵节。 随着社会和时代的变迁,元宵节的风俗习惯在不断变化,但至今仍是中国的传统节日。2008年, 元宵节选入第二批国家级非物质文化遗产。 对我而言,除了吃元宵、看花灯……还有一件最重要的事情…就是… 猜灯谜!猜灯谜!!猜灯谜!!!猜谜事小,展现聪明才智事大哈哈哈.jpg .

顾木子吖的博客 2098

centos7开机启动脚本、启动Hadoop、Spark

【代码】centos7开机启动脚本、启动Hadoop、Spark。

Xiaoweidumpb 435

第10章:位同步与Gardner环路(1)

第10章(1)内容如下:一、位同步与载波同步的区别1、采样时钟相位偏差的影响2、采样时钟频率偏差的影响二、Gardner环路算法与插值方法1、插值滤波器2、Gardner误差检查算法三、Gardner环路中环路滤波器与数字振荡器设计1、环路滤波器2、数控振荡器(NCO)四、Gardner环路的可运行MATLAB代码五、参考资料六、致谢 最近搞懂了Gardner环路的一些知识,于是更新第10章——位同步。 如果你稍不留意的话,这可能是你在网上能看到的介绍Gardner环路及其MATLAB仿真最详细的一篇文章了

weixin_44283855的博客 1万+

【AN7581】AN7581 SDK编译环境搭建操作说明

安装编译需要的工具包。

wgl307293845的博客 338

Spark源码系列(零)-如何开始分析Spark源码

如果想更深入的理解Spark,那么就需要深入去研究下源码实现。关于Spark源码下载可以到官方网站或者github进行下载,而阅读源码的最好方式就是调试,Spark源码调试有很多种办法,本文介绍一种我常用的调试方法。 在调试Spark代码之前,可以先找一台linux主机安装Spark,这里最简单的方式用local方式即可,只需要下载官网编译好的Spark安装包即可,当然也可以部署一套Standalone模式的SPark集群,或者使用Spark on Yarn模式(推荐这种方法,不过需要搭建一套Hadoop

Dreamershi的专栏 505

【GlobalMapper精品教程】074:从Lidar点云创建3D地形模型

本文基于地形点云数据,基于泊松方法、贪婪三角形测量方法和阿尔法形状创建3d地形模型。

智慧空间实验室 2169

Spark内核详解 (3) | Spark集群启动流程的简单分析

  大家好,我是不温卜火,是一名计算机学院大数据专业大二的学生,昵称来源于成语—不温不火,本意是希望自己性情温和。作为一名互联网行业的小白,博主写博客一方面是为了记录自己的学习过程,另一方面是总结自己所犯的错误希望能够帮助到很多和自己一样处于起步阶段的萌新。但由于水平有限,博客中难免会有一些错误出现,有纰漏之处恳请各位大佬不吝赐教!暂时只有csdn这一个平台,博客主页:https://buwenbuhuo.blog.csdn.net/   本片博文为大家带来的是Spark集群启动流程的简单分析。 目录.

不温卜火 4896

29、几何形态测量学在蝴蝶翅膀研究中的应用

本博客介绍了几何形态测量学在花斑木蛱蝶(Pararge aegeria)翅膀研究中的应用,探讨了环境因素对蝴蝶翅膀大小和形状的影响。通过选择合适的地标并采用Procrustes分析方法,研究揭示了不同景观类型下翅膀形状的差异及其潜在的适应性意义。文章还讨论了发育可塑性在飞行形态变异中的作用,并比较了几何形态测量学与其他传统方法的优势。最后提出了未来研究方向,包括多因素综合分析、发育机制探索及长期生态监测,为理解蝴蝶的适应性进化提供了重要线索。

ff678的博客 107

ArcGIS实验教程——实验二十五:大型商场选址经典案例

本实验基于ArcGIS平台进行商场选址分析,综合考虑道路、居民点、停车场和已有商场四个影响因素。通过缓冲区分析建立各要素的影响范围(道路50米、居民点100米、停车场100米、已有商场500米),采用叠加分析中的相交和擦除运算确定最优选址区域。实验建立了区位评价体系,通过属性赋值(1/-1)和联合运算生成综合评价结果,并实现分级可视化显示。注意事项包括距离参数设置对结果的影响以及正确选择空间分析工具。该流程为商业选址提供了科学的GIS解决方案。

智慧空间实验室 1万+

Spark源码解析(一):Spark执行流程和脚本

Spark执行流程 Spark带注释源码 对于整个Spark源码分析系列,我将带有注释的Spark源码和分析的文件放在我的GitHub上Spark源码剖析欢迎大家fork和star 过程描述: 1.通过Shell脚本启动Master,Master类继承Actor类,通过ActorySystem创建并启动。 2.通过Shell脚本启动Worker,Worker类继承Actor类,通过A...

Oeljeklaus的博客 6344

嵌入式面经篇八——进程&线程

记录一些招聘公司在招聘嵌入式软件岗位时的一些问题,此文为第八篇。进程上文:是指进程由用户态切换到内核态时需要保存用户态时CPU寄存器中的值,进程状态以及堆栈上的内容。即保存当前进程的状态,以便再次执行该进程时,能够恢复切换时的状态,继续执行。进程下文:是指切换到内核态后执行的程序,即进程运行在内核空间的部分。中断上文:硬件通过中断触发信号,导致内核调用中断处理程序,进入内核空间。这个过程中,硬件的一些变量和参数也要传递给内核,内核通过这些参数进行中断处理。

明天你好的博客 1854

Spark 集群启动的流程

1.简介 源码版本spark-2.4.0 Spark 作为目前大数据处理的主流架构,吸引了不少人去学习它,本人也不例外,对Spark抱有强烈的好奇心,因此对Spark在执行我们编写的程序时,到底是怎么运行的,是我一直想要搞明白的事情。所以从本篇博客开始,我就详细的介绍Spark执行程序的流程。一方面为了巩固自己所学的知识,另一方面也是为了抛转引玉,希望能有更多的人来介绍Spark. 本篇博客为本系...

fengshaungme的博客 3293

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

[spark] standalone集群模式Driver启动过程

本篇文章简单整理一下spark在standalone集训模式下启动Driver的流程,本篇文章只解析到Driver启动成功,启动后续任务执行在后面博客更新,个人比较喜欢从代码跟踪,文章代码粘贴只提取部分重要代码。。。。。。 一、脚本查看 spark-submit触发任务的提交,查看spark-submit脚本会看出最终执行任务的主类是: exec "${SPARK_HOME}"/...

u012989317的博客 1025

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

spark源码解析:2.1 start-master.sh脚本分析

1. start-master.sh脚本内容 # 判断是否配置了SPARK_HOME,如果没有设置则先通过$0获取当前脚本的文件名 # 再通过dirname获取sbin目录,再cd回上级目录,pwd获取绝对路径,以此设置SPARK_HOME if [ -z "${SPARK_HOME}" ]; then export SPARK_HOME="$(cd "`dirname "$0"`"/..; ...

hlp4207的博客 519

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

Spark源码分析(一):Master 启动

文章目录启动 master一、启动脚本: start-all.sh1.1 start-master.sh Master 脚本分析去到脚本: spark-daemon.sh1.2 start-slaves.sh Worker 脚本分析去到脚本:slave.sh 启动 master 一、启动脚本: start-all.sh #!/usr/bin/env bash # 如果配置了 SPARK_HOM...

Java_Road_Far的博客 950

Spark2.3.2源码解析: 3.启动worker节点启动流程源码分析

本文启动worker节点启动流程分析   启动命令: ${SPARK_HOME}/sbin/start-slave.sh spark://sysadmindeMacBook-Pro.local:7077   查看start-slave.sh   执行脚本: /workspace/spark-2.3.2/sbin/spark-daemon.sh start org.apache....

张伯毅的专栏 6368

spark源码(二)spark如何启动driver、application注册、executor构建命令拼装

driver概念解析、driver运行、application注册、executor构建命令拼装

Interest1_wyt的博客 1749

Spark-环境启动

从start-all.sh开始捋,一直捋到Master、Worker的启动并建立通信。

lu070828的博客 1954

Spark源码解析01-Master启动流程

1、前言 Master是spark中核心角色,涉及到集群通信以及资源调用申请,不仅要接收Driver,Worker的注册调用,还需要根据调度情况知道其他角色的状态,例如:Executor、Driver的状态等。 通过以上的推断,Master需要有个传输层(TransportServer)专门用来发送或者接受服务,如下图 由上图可见,如果使用传输的实例过多,势必会存在问题,如:这个实例消息A传给谁,实例消息B由哪个实例接收等,为此,我们可以在传输层上添加一个分发器(Dispatcher),如下图 引入分发

Linzx的学习笔记 1141
上一篇: Spark源码解析(一)Spark RPC分析
下一篇: Spark源码解析(三)任务提交流程分析
Yuan_CSDF
博客等级 码龄10年 169粉丝 130原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值