一、本地化调度
### --- 本地化调度
~~~ DAGScheduler切割Job,划分Stage。
~~~ 调用submitStage来提交一个Stage对应的tasks,submitStage会调用submitMissingTasks,
~~~ submitMissingTasks 确定每个需要计算的 task 的 preferred Locations
~~~ 通过调用 getPreferrdeLocations 得到分区的优先位置,
~~~ 一个partition对应一个task,此分区的优先位置就是task的优先位置
~~~ 从调度队列中拿到 TaskSetManager 后,
~~~ 那么接下来的工作就是 TaskSetManager 按照一定的规则一个个取出task 给 TaskScheduler,
~~~ TaskScheduler 再交给 SchedulerBackend 发送到 Executor 上执行
### --- 根据每个 task 的优先位置,确定 task 的 Locality 级别,Locality一共有五种,优先级由高到低顺序
PROCESS_LOCAL data is in the same JVM as the running code. This is the best locality possible
NODE_LOCAL data is on the same node. Examples might be in HDFS on the same node, or in
another executor on the same node. This is a little slower than PROCESS_LOCAL because the data has to travel between processes
NO_PREF data is accessed equally quickly from anywhere and has no locality preference
RACK_LOCAL data is on the same rack of servers. Data is on a different server on the same rack so needs to be sent over the network, typically through a single switch
ANY data is elsewhere on the network and not in the same rack
### --- 本地化调度
~~~ 在调度执行时,Spark总是会尽量让每个 Task 以最高的本地性级别来启动;
~~~ 当某个 Task 以某个本地性级别启动,但是该本地性级别对应的所有节点都没有空闲资源而启动失败,
~~~ 此时并不会马上降低本地性级别启动,而是在某个时间长度内再次以X本地性级别来启动该task,
~~~ 若超过限时时间则降级启动,去尝试下一个本地性级别,依次类推;
~~~ 通过调整每个类别的最大容忍延迟时间,
~~~ 在等待阶段对应的 Executor 可能就会有相应的资源去执行此 Task,
~~~ 这样有可能在一定程度上提到运行性能;
二、返回结果
### --- 返回结果
~~~ 对于Executor的计算结果,会根据结果的大小使用不同的处理策略:
![](https://i-blog.csdnimg.cn/blog_migrate/769aa8596f12df56012b2285289ee7fe.png)
### --- 计算结果在(0,200KB-128MB)区间内:通过Netty直接发送给Driver终端
~~~ Netty的预留空间reservedSizeBytes,200KB
~~~ spark.rpc.message.maxSize,默认值是128MB
~~~ 计算结果在[128MB, 1GB]区间内:将结果以 taskId 为编号存入到 BlockManager 中,
~~~ 然后通过 Netty 把编号发送给 Driver;阈值可通过 Netty 框架传输参数设置
~~~ spark.driver.maxResultSize,默认值 1G
~~~ 计算结果在(1GB,∞)区间内:直接丢弃,可通过spark.driver.maxResultSize配置
三、失败重试与黑名单机制
### --- 失败重试与黑名单机制
~~~ Task被提交到Executor启动执行后,
~~~ Executor会将执行状态上报给SchedulerBackend(DriverEndpoint);
~~~ SchedulerBackend 则告诉 TaskScheduler,TaskScheduler 找到该 Task 对应的 TaskSetManager,
~~~ 并通知到该TaskSetManager,这样 TaskSetManager 就知道 Task 的失败与成功状态;
~~~ 即 SchedulerBackend(DriverEndPoint) => TaskScheduler => TaskSetManager
~~~ 对于失败的Task,会记录它失败的次数,如果失败次数还没有超过最大重试次数,
~~~ 那么就把它放回待调度的Task池子中,否则整个Application失败;
~~~ 在记录 Task 失败次数过程中,会记录它上一次失败所在的Executor Id和Host。
~~~ 下次再调度这个Task时,会使用黑名
~~~ 单机制,避免它被调度到上一次失败的节点上,起到一定的容错作用;