文章目录
核心
所谓的内核,其实就是Spark的内部核心原理。
1.Spark应用提交
(1) Spark向Yarn提交
(1) Spark向Yarn提交
当使用bin/java执行java程序时,会产生JVM,java的进程
(2) ApplicationMaster, Driver, Executor
ApplicationMaster是一个进程
Driver是一个线程, 但是我们一般会讲SparkContext称之为Driver
Executor是一个计算对象, 但有时我们将ExecutorBackend后台通信对象也称 之为Executor
2.Spark内部组件及通信
(1) 通信原理 - IO - RPC
基本的网络通信:Socket, ServerSocket
通信框架:AKKA(旧), Netty(新)(AIO)
三种IO方式:BIO(阻塞式), NIO(非阻塞式), AIO(异步)
Linux, windows
在Linux系统上,AIO的底层实现仍使用EPOLL,与NIO相同,因此在性能上没有明显的优势;Windows的AIO底层实现良好,但是Netty开发人员并没有把Windows作为主要使用平台考虑。微软的windows系统提供了一种异步IO技术:IOCP(I/O CompletionPort,I/O完成端口);Linux下由于没有这种异步IO技术,所以使用的是epoll(一种多路复用IO技术的实现)对异步IO进行模拟。所以在Linux上不建议使用AIO
(2) 组件内部
Driver - CoarseGrainedSchedulerBackend - DriverEndpoint
Executor - CoarseGrainedExecutorBackend
(3) 组件之间
通信终端共通类:Endpoint
通信终端:RpcEndpoint(receive)
通信终端引用:RpcEndpointRef(send,ask)
源码解析
SparkSubmit
SparkSubmit
-- main
-- new SparkSubmit
// 执行提交操作
-- doSubmit
// 解析参数
-- parseArguments
// --master yarn => master
// --deploy-mode cluster => deployMode
// --class SparkPI(WordCount) => 【mainClass】
-- parse
-- submit
-- doRunMain
-- runMain
// 准备提交环境
// (childArgs, childClasspath, sparkConf, childMainClass)
// 【cluster】 : childMainClass = org.apache.spark.deploy.yarn.YarnClusterApplication
// 【client 】 : childMainClass = mainClass
-- prepareSubmitEnvironment(args)
-- Thread.currentThread.setContextClassLoader(loader)
// 通过类名加载这个类
// Class.forName("xxxxx")
-- mainClass = Utils.classForName(childMainClass)
// 反射创建类的对象并进行类型转换
-- app = mainClass.newInstance().asInstanceOf[SparkApplication]
-- app.start
YarnClusterApplication
-- start
// 封装参数
// --class
-- new ClientArguments
-- new Client
// 创建Yarn客户端对象
-- yarnClient = YarnClient.createYarnClient
// ResourceManager
-- rmClient
-- run
// 提交应用
-- submitApplication
// 配置JVM的启动参数
// 封装指令 :【cluster】 command = bin/java org.apache.spark.deploy.yarn.ApplicationMaster
// 封装指令 :【client 】 command = bin/java org.apache.spark.deploy.yarn.ExecutorLauncher
-- createContainerLaunchContext
-- createApplicationSubmissionContext
// 向Yarn提交应用
-- yarnClient.submitApplication(appContext)
ApplicationMaster
ApplicationMaster
-- main
// 封装参数
// --class
-- ApplicationMasterArguments
-- new ApplicationMaster
-- run
// 【cluster】
-- runDriver
// 启动用户的应用
-- userClassThread = startUserApplication()
// 反射加载类,获取类的main方法
// --class SparkPI(WordCount)
-- userClassLoader.loadClass(args.userClass).getMethod("main", classOf[Array[String]])
-- userThread = new Thread
-- thread.setName(【Driver】)
// 在执行main方法
-- mainMethod.invoke
// 线程阻塞,等待对象(SparkContext)的返回
-- ThreadUtils.awaitResult
// 注册AM
-- registerAM
-- createAllocator
-- client.createAllocator
// 分配资源
-- allocator.allocateResources()
// 获取可用的资源列表
-- allocateResponse.getAllocatedContainers()
// 处理可用的资源
-- handleAllocatedContainers
// 匹配后的资源可以运行
-- runAllocatedContainers
-- container => ExecutorRunnable
-- nmClient
// 启动容器
-- startContainer
-- run
// 封装指令
// command = bin/java org.apache.spark.executor.CoarseGrainedExecutorBackend
-- prepareCommand
// 让NM启动容器, 启动Executor
-- nmClient.startContainer(container.get, ctx)
// 【client】
-- runExecutorLauncher
移动数据不如移动计算
涉及到本地化级别
CoarseGrainedExecutorBackend
CoarseGrainedExecutorBackend
-- main
-- run
通信
RPC:通信,RMI,EJB
Endpoint:终端
backend:后台
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JRLJ8lRQ-1595292951976)(/Users/vanas/Library/Application Support/typora-user-images/截屏2020-07-10下午8.39.38.png)]
3.Spark作业的提交(调度)
(1) Application
Yarn中会有Application
SparkConf中配置setAppName(“xxxxx”)
SparkContext
(2) 逻辑代码 => RDD
RDD的创建
RDD的转换
RDD的行动
(3) Job => 行动算子(N)
触发作业的执行:sc.runJob
new ActiveJob
(4) Stage的划分 => 转换算子 => 依赖关系
窄依赖,宽依赖
分区的数量
默认不变
Shuffle的算子一般都会有改变分区数量的参数
默认分区的数量
Local[*]
(5) Task的切分(Partition)
任务和阶段的关系
阶段数量 = ResultStage + N * ShuffleDep
阶段类型:ResultStage, ShuffleMapStage
任务和分区的关系
任务的数量 : 当前阶段的分区的数量
任务的总数量
任务的总数量 : 所有阶段的任务总和
任务的类型
ShuffleMapState => ShuffleMapTask
ResultStage => ResultTask
4.任务的执行
(1) 内容
阶段ID
RDD元数据,依赖
分区
首选位置
(2) 序列化(累加器,KRYO)
默认序列化:Java序列化
Kryo序列化:shuffle的场合下,如果类型为值类型或字符串类型
(3) 调度
Driver
TaskSet - Stage
TaskSetManager - Scheduler
调度模式:FIFO(默认), FAIR
任务调度池:Pool(FIFO)
Executor
本地化级别(调度)
降级处理:默认等待3s
(4) 计算
任务的编码
向Executor发送Task
Executor接收Task,并进行解码
计算对象执行Task
线程池执行task.run方法, 调用具体Task对象的runTask方法。
(5) Shuffle
Shuffle map(Write)
Shuffle reduce(Read)
管理器:SortShuffleManager
ShuffleWriter
BypassMergeSortShuffleWriter
UnsafeShuffleWriter
SortShuffleWriter
写磁盘文件时,首先进行内存中的排序,如果内存(5M)不够,会溢写磁盘,生成临时文件,最终将所有的临时文件合并成数据文件和索引文件。
预聚和的原理:在排序时,构造了一种类似于hashtable的结构,所以想同的key就聚合在一起。
排序规则:首先会按照分区进行排序,然后按照key.hashCode进行排序
ShuffleHandle
BypassMergeSortShuffleHandle: 没有预聚和功能的算子 & reduce的分区数量 <=阈值(200)
SerializedShuffleHandle :Spark的内存优化后的解决方案,对象序列化后不需要反序列化。!(支持序列化重定位 & 支持预聚和 & 分区数量 大于阈值)
BaseShuffleHandle :默认的shuffleHandle
(6) 内存(cache)
(7) 累加器