Hive on Spark源码分析(二)—— SparkSession与HiveSparkClient

Hive on Spark源码分析(一)—— SparkTask
Hive on Spark源码分析(二)—— SparkSession与HiveSparkClient
Hive on Spark源码分析(三)—— SparkClilent与SparkClientImpl(上)
Hive on Spark源码分析(四)—— SparkClilent与SparkClientImpl(下)
Hive on Spark源码分析(五)—— RemoteDriver
Hive on Spark源码分析(六)—— RemoteSparkJobMonitor与JobHandle 

在Hive on Spark源码分析(一)—— SparkTask中已经介绍到,SparkSession是HOS任务提交的起点。在HOS的代码实现中,SparkSession是一个接口,具体的实现类是SparkSessionImpl,因此我们直接进入到SparkSessionImpl内部进行分析。

首先是初始化:

 
   
  1. //通过makeSessionId,随机生成一个id就可以初始化一个sparkSession
  2. public SparkSessionImpl() {
  3. sessionId = makeSessionId();
  4. }
 
    
  1. public static String makeSessionId() {
  2. return UUID.randomUUID().toString();
  3. }

初始化后需要打开session。其实所谓的打开一个session,其实就是实例化一个hiveSparkClient对象。创建过程基于简单工厂模式,如果获取到的spark.master参数的值为"local",则创建LocalHiveSparkClient实例
,否则创建RemoteHiveSparkClient实例:
 
     
  1. @Override
  2. public void open(HiveConf conf) throws HiveException {
  3. this.conf = conf;
  4. isOpen = true;
  5. try {
  6. hiveSparkClient = HiveSparkClientFactory.createHiveSparkClient(conf);
  7. } catch (Throwable e) {
  8. throw new HiveException("Failed to create spark client.", e);
  9. }
  10. }

实例化 hiveSparkClient对象后,则可以通过 hiveSparkClient提交session需要提交的任务,具体实现在submit方法中。首先判断session是否已经打开,如果已经成功打开则调用 hiveSparkClient的execute方法进行任务提交:
 
      
  1. @Override
  2. public SparkJobRef submit(DriverContext driverContext, SparkWork sparkWork) throws Exception {
  3. Preconditions.checkState(isOpen, "Session is not open. Can\'t submit jobs.");
  4. return hiveSparkClient.execute(driverContext, sparkWork);
  5. }

由于H iveSparkClient是一个接口,它有两个实现类,分别是 LocalHiveSparkClient和 RemoteHiveSparkClient。 前面我们已经提到,其实例根据提交参数的不同,会分别创建不同的子类实例 。因为local模式通常仅用于实验环境,因此这里我们主要分析一下 RemoteHiveSparkClient的execute方法是如何提交SparkSession的任务的。

RemoteHiveSparkClient的execute方法首先判断是否提交模式为yarn-cluster或yarn-client,并且remoteClient(SparkClient实例)还没准备好,是的话需要创建一个SparkClient。否则直接调用submit方法将任务提交。
 
    
  1. @Override
  2. public SparkJobRef execute(final DriverContext driverContext, final SparkWork sparkWork) throws Exception {
  3. if (hiveConf.get("spark.master").startsWith("yarn-") && !remoteClient.isActive()) {
  4. // Re-create the remote client if not active any more
  5. close();
  6. createRemoteClient();
  7. }
  8. try {
  9. return submit(driverContext, sparkWork);
  10. } catch (Throwable cause) {
  11. throw new Exception("Failed to submit Spark work, please retry later", cause);
  12. }
  13. }

SparkClient的创建同样是使用工厂模式,但是SparkClient的实现其实只有SparkClientImpl。并且如果开启了动态资源分配,会对executor进行数目进行配置:
 
     
  1. private void createRemoteClient() throws Exception {
  2. remoteClient = SparkClientFactory.createClient(conf, hiveConf);
  3. if (HiveConf.getBoolVar(hiveConf, ConfVars.HIVE_PREWARM_ENABLED) &&
  4. hiveConf.get("spark.master").startsWith("yarn-")) {
  5. int minExecutors = getExecutorsToWarm();
  6. if (minExecutors <= 0) {
  7. return;
  8. }
  9. LOG.info("Prewarm Spark executors. The minimum number of executors to warm is " + minExecutors);
  10. // Spend at most MAX_PREWARM_TIME to wait for executors to come up.
  11. int curExecutors = 0;
  12. long ts = System.currentTimeMillis();
  13. do {
  14. try {
  15. curExecutors = getExecutorCount(MAX_PREWARM_TIME, TimeUnit.MILLISECONDS);
  16. } catch (TimeoutException e) {
  17. // let\'s don\'t fail on future timeout since we have a timeout for pre-warm
  18. LOG.warn("Timed out getting executor count.", e);
  19. }
  20. if (curExecutors >= minExecutors) {
  21. LOG.info("Finished prewarming Spark executors. The current number of executors is " + curExecutors);
  22. return;
  23. }
  24. Thread.sleep(500); // sleep half a second
  25. } while (System.currentTimeMillis() - ts < MAX_PREWARM_TIME);
  26. LOG.info("Timeout (" + MAX_PREWARM_TIME / 1000 + "s) occurred while prewarming executors. " +
  27. "The current number of executors is " + curExecutors);
  28. }
  29. }

下面来看一下submit方法的实现:
 
     
  1. private SparkJobRef submit(final DriverContext driverContext, final SparkWork sparkWork) throws Exception {
  2. final Context ctx = driverContext.getCtx();
  3. final HiveConf hiveConf = (HiveConf) ctx.getConf();
  4. //添加各种jar包和配置文件
  5. refreshLocalResources(sparkWork, hiveConf);
  6. final JobConf jobConf = new JobConf(hiveConf);
  7. // Create temporary scratch dir
  8. final Path emptyScratchDir = ctx.getMRTmpPath();
  9. FileSystem fs = emptyScratchDir.getFileSystem(jobConf);
  10. fs.mkdirs(emptyScratchDir);
  11. //序列化配置、临时目录路径和任务
  12. byte[] jobConfBytes = KryoSerializer.serializeJobConf(jobConf);
  13. byte[] scratchDirBytes = KryoSerializer.serialize(emptyScratchDir);
  14. byte[] sparkWorkBytes = KryoSerializer.serialize(sparkWork);
  15. //创建JobStatusJob,包含环境配置,临时目录和具体的提交的任务
  16. JobStatusJob job = new JobStatusJob(jobConfBytes, scratchDirBytes, sparkWorkBytes);
  17. //这一步调用SparkClientImpl向RemoteDriver提交job,job包含原来提交过来的SparkWork,还包含了jobConf和临时目录(emptyScratchDir)
  18. //过程: SparkClientImpl.submit => ClientProtocol.submit =>
  19. JobHandle<Serializable> jobHandle = remoteClient.submit(job);
  20. //这里sparkClientTimtout是hive-site.xml中配置的hive.spark.job.monitor.timeout
  21. //remoteClient是SparkClientImpl
  22. //jobHanle是submit后返回的JobHandleImpl,是跟踪任务的关键
  23. RemoteSparkJobStatus sparkJobStatus = new RemoteSparkJobStatus(remoteClient, jobHandle, sparkClientTimtout);
  24. return new RemoteSparkJobRef(hiveConf, jobHandle, sparkJobStatus);
  25. }

RemoteHiveSparkClient中还有一些其他简单方法这里就不做具体分析了。另外这个类里面还有一个非常重要的call方法,是最终生成执行计划,得到RDD并最终执行的方法,后面会在RemoteDriver类的分析中进行具体分析。

现在任务的提交又传递给了remoteClient,是一个SparkClient类型的实例,因此下一篇我们将会具体来分析SparkClient的实现类,SparkClientImpl。


你可能感兴趣的:(Hive,on,Spark)