Redis存储Offset(重点)
- 获取redis连接
- 从redis获取Offset(有或者无)
- 更新Offset到redis
在使用Redis连接的时候,注意一点,Connection连接无法被序列化,也就是说在Driver创建的连接,无法发送到Executor内部使用,需要在Executor内部创建单独的连接操作,才可以使用数据的累加或者计算操作,而且Driver负责维护Offset,此连接和Executor内部的连接不冲突,就算在Executor内部关闭连接,也不会影响Driver的连接,因为在Executor内部关闭的连接是Executor的Jedis连接,和Driver无关。(在Driver的代码只会执行一次)
Spark On Yarn
原理:
cluster模式
- 客户端(Spark集群的某台服务器)提交任务到集群(RM)
- RM接收任务后,将任务发送到对应的NM
- NM接收到任务,首先启动一个APP Master(Driver),再去创建对应的Container
- Container内部封装对应的Executor资源,Executor启动后,反向注册到Driver(APP Master)
- APP Master接收到Executor反向注册的信息,监控集群内的Task运行 状态
- APP Master将监控信息返回给RM,供给客户端显示(log)
- 任务运行完成,各个NM会释放对应的资源,注销当前的任务
其实用Spark On Yarn 的模式,说白了就是Yarn的原理,在这里面没有Master节点,也没有Worker节点,有的就是RM,NM,调度操作都是有RM来执行,任务运行都是NM来执行。任务的监控由内部创建的APP Master完成
client模式
- 由客户端(不是集群内的服务器)提交任务到集群(创建APP Master)
- RM接收任务,会将任务分配到应用的NM上面
- NM会启动对应的Container,内部封装Executor的资源
- Executor启动后,反向注册到单独提交任务的那台服务器上的Driver
- Driver实时监控当前集群的运行状态(Task运行状态)
- 任务执行完成,log任务会在Driver显示,同时RM会收集log到服务器
两者提交方式都可以正常运行任务,但是注意,Cluster模式提交任务后,Driver在内部创建,并且实时监控,也就是说,只要集群正常运行,Driver就正常工作,最后输出结果,但是Client的模式,如果提交任务的这台服务器挂了,那么任务失败
Spark源码分析(了解)
DAG Scheduler:有向无环图,根据宽窄依赖划分Stage,将所有Task提交至Task Scheduler上
- 首先DAG触发条件是Action(例如:foreach)点入runjob方法
- 进入DAG的runjob方法,内部开始提交当前任务,找到submitJob方法提交
- submitJob内部主要是将Job赋值Id,创建阻塞线程,让每个任务 按循序执行
- 通过Event事件队列,进行匹配,匹配当前提交任务的事件
- 找到当执行方(首先创建ResultStage)
- 通过getOrCreateShuffleMapStage,划分所有的shuffle依赖
- 倒推完成后,得到finalStage,然后将finalStage传入getMissingParentStages方法进行划分Stage
- 内部通过Visit方法,进行递归 调用,循环所有的依赖,如果遇到宽依赖就保存,如果是 窄依赖就push回去继续循环
- 当所有Stage划分完成后,将finalStage提交,通过submitStage方法提交
- 接下里划分通过 TasksubmitMissingTasks
- 内部通过Stage划分Task,Task的数量等于Partition数量,内部会划分两种Task
- 划分完成Task提交所有Tasks到TaskSet中
- 通过taskScheduler.submitTasks 提交至TaskSchedule上面,进行本地化的划分
- 这里调用的是 实现taskScheduler特质的TaskSchedulerImpl
- 找到submitTasks的重写方法,在TaskSchedulerImpl实现Task提交
- 每个Task在执行之前都会监控通过TaskSetManager
- 执行Task是FIFO的调度模式(先进先出)
- 开始分配所有Task到每个节点内部,通过resourceOffers方法
- 找到taskSet.executorAdded() 划分本地化级别
- 本地化级别通过枚举方式实现computeValidLocalityLevels()
- 五种本地化级别 PROCESS_LOCAL, NODE_LOCAL, NO_PREF, RACK_LOCAL, ANY 分别是,节点本地化,进程本地化,机架本地化,无本地化,没有
- TaskSetManager内部实现本地化级别划分(最优位置)
- Task会执行Run方法,然后每个Task会通过执行句柄发送到Executor内部执行
- Executor内部会将Task反序列化,根据核数运行Task
Task Scheduler:接收反向注册信息,发送Task(本地级别)
Shuffle(重点)
HashShuffle(了解)
未优化:首先通过ShuffleMapTask写入数据,然后每个ShuffleMapTask创建reduce个数的缓冲区,每个缓冲区都会溢写文件到本地磁盘,生成shuffleBlockFile本地文件,每个缓冲区都会创建一个shuffleBlockFile文件,然后在创建文件后,会创建对应的索引文件,将所有的本地文件元数据信息发送到Driver端,Driver会保存着元数据信息,然后reduce拉取本地文件的时候,会根据driver内的元数据信息拉取数据。
优化后:开启合并小文件机制(consolidation 机制),这样减少小文件输出,复用文件和缓存区,减少资源浪费
spark1.6以前这种shuffle还是不错的,减少磁盘IO,和文件的创建,性能还可以,但是2.0以后,因为底层内存模型发生改变,造成这种shuffle操作,不能完全的合理利用内存,所以被弃用,换成SortShuffle机制
SortShuffle 有两种机制(掌握)
普通机制:内部有两种数据结构,Map和Array,Map会做局部的聚合操作,提前进行分区内的合并,而Array直接写入内存,然后排序到每个缓冲区,当缓冲区满了以后,会溢写到磁盘文件,溢写后的小文件会被合并(这点跟MR比较像),然后合并的时候,都会记录当前的索引信息,当reduce拉取数据的时候,那么会根据索引信息找到位置去拉取merge文件内的数据
触发条件是大于200个Task
ByPass机制:其实前身就是HashShuffle机制,通过溢写文件,然后在去合并当前文件,而hash的是复用之前的本地文件,触发的条件是Task小于200个,溢写后的小文件会被合并(这点跟MR比较像),然后合并的时候,都会记录当前的索引信息,当reduce拉取数据的时候,那么会根据索引信息找到位置去拉取merge文件内的数据
钨丝Shuffle(TungstenShuffle了解)
其实内部实现的是SortShuffle普通机制,只不过对内存和缓存进行的合理利用,但是很多算子还是不支持钨丝shuffle这种shuffle操作。