spark源码分析—spark core(一):集群启动

spark集群通过spark的start-all.sh脚本进行启动,所以首先我们看一下该脚本的内容,该脚本内容很简单,它会通过调用相同目录下的start-master.sh脚本启动spark Master服务,调用start-slaves.sh脚本启动spark Worker服务。大家注意start-master.sh脚本只会在调用该脚本的机器启动Mater服务,如果Master是HA的话,需要到另外的配置为Master的机器调用start-master.sh脚本启动。所以start-all.sh脚本只能启动非HA的spark集群的所有Master和Worker服务,这和Hadoop的脚本有些区别,不知道社区是故意为之还是因为其他的理由。
接下来我们分析一下spark集群启动相关的脚本的内容。

start-all.sh脚本片段
if [ -z "${SPARK_HOME}" ]; then
  export SPARK_HOME="$(cd "`dirname "$0"`"/..; pwd)"
fi

# 加载spark的配置的环境变量,主要加载SPARK_HOME、
# SPARK_CONF_DIR和一下python相关的环境变量
. "${SPARK_HOME}/sbin/spark-config.sh"

# 调用start-master.sh脚本启动spark的master服务,
# 且Master服务在该脚本被调用的机器上启动
"${SPARK_HOME}/sbin"/start-master.sh

# 调用start-slaves.sh脚本启动所用的Worker服务,
# 该脚本会调用slaves.sh脚本,通过ssh发送Worker服务启动命令
"${SPARK_HOME}/sbin"/start-slaves.sh

接下来分别对Master和Worker服务的启动流程进行分析

1、Master启动

我们从start-master.sh脚本开始一步一步分析。

spark-master.sh脚本片段
# 启动Master服务需要调用的Scala主类
CLASS="org.apache.spark.deploy.master.Master"
# start-master.sh脚本会调用spark-daemon.sh脚本,spark-daemon.sh脚本,
# spark-daemon.sh脚本又会调用spark-class.sh脚本,并将参数传到该脚本,
# spark-class.sh脚本会调用Spark的org.apache.spark.launcher.Main类对不同的CLASS、不同的OS生成合适的启动服务进程的java命令
# Usage: spark-daemon.sh [--config <conf-dir>] (start|stop|submit|status) <spark-command> <spark-instance-number> <args...>
"${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

spark-daemon.sh脚本和spark-class.sh脚本就不再分析,感兴趣的可以看一下。接下来看一下org.apache.spark.deploy.master.Master类的main方法,看一下Master的启动流程

def main(argStrings: Array[String]) {
    // 注册Master进程的Logger对象,用于处理该进程的“TERM”,“HUP”,“INT”事件,并打印事件到日志
    Utils.initDaemon(log)
    // 创建SparkConf对象,该对象会加载Spark的所有配置参数,并保存在ConcurrentHashMap对象中
    val conf = new SparkConf
    // 创建MasterArguments对象,该对象会对Master的参数进行解析
    val args = new MasterArguments(argStrings, conf)
   // 创建一个RPC环境,并启动Master的RPC端点服务且注册到该RPC环境中
    val (rpcEnv, _, _) = startRpcEnvAndEndpoint(args.host, args.port, args.webUiPort, conf)
   // 阻塞等待RPC环境结束
    rpcEnv.awaitTermination()
  }

接下来,看一下startRpcEnvAndEndpoint()方法,该方法会创建RPC环境和Master端点服务的注册流程,RpcEnv底层就是一个Netty实现的Socket服务,通过不同的rpc endpoint注册信息进行路由,对于不同的Socket请求调用不同的处理逻辑。Spark从2.+开始集群的RPC抛弃了Akka,通过Netty实现了一套简化版的Akka RPC框架。后边会分析具体的实现,在这里只要知道它是一个RPC服务即可。

def startRpcEnvAndEndpoint(
      host: String,
      port: Int,
      webUiPort: Int,
      conf: SparkConf): (RpcEnv, Int, Option[Int]) = {
    // 创建SecurityManager对象,负责Spark不同组件之间的的安全通行
    val securityMgr = new SecurityManager(conf)
    // 创建RPCEnv,用于RPC服务,spark从2.+开始实现了自己的基于Netty的类Akka的RPC框架,不再使用Akka
    val rpcEnv = RpcEnv.create(SYSTEM_NAME, host, port, conf, securityMgr)
    // 创建、注册Master服务的RPC端点并启动
    val masterEndpoint = rpcEnv.setupEndpoint(ENDPOINT_NAME,
      new Master(rpcEnv, rpcEnv.address, webUiPort, securityMgr, conf))
   // 向Master发送同步RPC调用,获取Master服务的端口号
    val portsResponse = masterEndpoint.askSync[BoundPortsResponse](BoundPortsRequest)
    (rpcEnv, portsResponse.webUIPort, portsResponse.restPort)
  }

貌似到执行完startRpcEnvAndEndpoint()方法就结束了,但是事实上不是这样的。

前面说了,Spark RPC服务和Akka类似,所以在Master endpoint启动后RPC框架会立即调用该endpoint的onStart()方法,因此Master的启动还没有结束,接下来继续看一下Master类的onStart()方法有什么东东吧

override def onStart(): Unit = {
    // 打印Master启动日志
    logInfo("Starting Spark master at " + masterUrl)
    logInfo(s"Running Spark version ${org.apache.spark.SPARK_VERSION}")
    // 创建MasterWebUI服务,并绑定端口启动,其实底层就是一个基于Jetty的web服务,
    // 而这个就是我们在浏览器输入spark_master_url:spark_master_port访问的web服务
    webUi = new MasterWebUI(this, webUiPort)
    webUi.bind()
    masterWebUiUrl = "http://" + masterPublicAddress + ":" + webUi.boundPort
   // 如果使用反向代理,masterWebUI地址使用代理地址
    if (reverseProxy) {
      masterWebUiUrl = conf.get("spark.ui.reverseProxyUrl", masterWebUiUrl)
      lo
Apache Spark个分布式计算框架,它支持内存中的数据处理,大大提高了大数据批处理的速度,并且还能够用于实时流式处理、机器学习等场景。 ### 源码结构 Spark 的源码非常庞大而复杂,但可以大致分为以下几个模块: 1. **Core Module** 这是整个项目的基石,包含了任务调度系统(Scheduler)、依赖管理机制以及底层的RDD操作逻辑。理解这部分有助于掌握如何构建高效的任务分发及执行流程。 2. **SQL Module (Catalyst Optimizer)** Catalyst 是 Spark SQL 中的个查询优化引擎。通过规则匹配的方式对输入表达式树进行转换,以此达到提高性能的目的。例如,自动选择最优物理计划或者简化某些特定模式下的表达式求值过程。 3. **Streaming Module** 实现了微批次架构来模拟连续的数据流入流出特性。开发者可以通过简单的API快速搭建起强大的实时应用;同时内部也包含了许多针对时间窗口运算的设计考量点。 4. **MLlib / GraphX Modules** MLlib 提供了系列常用的机器学习算法库及其相关的工具函数;GraphX 则专注于图模型方面的研究工作,如PageRank 算法实现等。 5. **Other Components** 包含Shuffle Manager, Storage Layer, Metrics System等功能组件,在实际运行过程中起到辅助作用。 ### 关键设计原则 - **弹性分布式数据集(RDD)** RDD是种只读的分区记录集合,它可以并行地存储在集群节点上并且允许用户施加各种变换(transformation),最终得到新的RDD实例。这种不可变性保证了容错性的简单性和效率最大化。 - **宽窄依赖关系(Wide/Narrow Dependencies)** 根据算子之间的连接方式将DAG划分成Stage阶段时所考虑的因素之。“窄”表示每个parent partition仅对应少量child partitions,“宽”的情况反之亦然。这直接影响到shuffle文件大小和网络传输成本等问题。 - **延迟加载(Lazy Evaluation)** 当创建个新RDD之后并不会立即开始计算,而是等到触发Action才会真正启动作业。这策略不仅节省资源而且便于做更精细粒度的错误恢复措施。 为了深入探究其原理建议从官方文档入手逐步探索各个部分的核心功能接口定义,再结合社区贡献者的博客文章加深认识。此外,GitHub 上面也有很多基于不同版本解读整套体系的文章可供参考。 --
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值