sparkLauncher记录(spark-2.2.0)

sparkLauncher 代码记录

1.概述
    sparkLauncher 是一个在代码里提交spark任务的类
    这个类底层使用的依然是spark-submit脚本进行提交,通过ProcessBuilder 来设置相关环境参数调用
    主要的方法有下面几个
    launch                   提交一个任务,任务的提交输出结果如何由用户自己处理
    createBuilder          launch和startApplication方法中调用生成ProcessBuilder执行shell脚本对象的方法
    startApplication      提交一个任务,并根据监听任务状态的改变来执行用户指定的listener

2.launch方法

   //这个方法是在设置完sparkLauncher的其它参数方法之后调用这个方法执行任务代码
   //通过createBuilder 创建一个Process 的对象然后执行

    public Process launch() throws IOException {
        Process childProc = createBuilder().start();     //在这里调用
        if (redirectToLog) {
          String loggerName = builder.getEffectiveConfig().get(CHILD_PROCESS_LOGGER_NAME);
          new OutputRedirector(childProc.getInputStream(), loggerName, REDIRECTOR_FACTORY);
        }
        return childProc;
    }

3.createBuilder方法
  //这个方法是构造执行命令,生成ProcessBuilder的函数
  //通过对象设置的javahome,sparkhome 找到对应的spark-submit脚本然后构建参数,生成cmd 命令,最后放到processBuilder中生成执行子进程
  
  private ProcessBuilder createBuilder() {
    List<String> cmd = new ArrayList<>();
    String script = isWindows() ? "spark-submit.cmd" : "spark-submit";   //找到对应的脚本
    cmd.add(join(File.separator, builder.getSparkHome(), "bin", script));   //设置脚本绝对路径
    cmd.addAll(builder.buildSparkSubmitArgs());  //脚本的设置参数

    //下面的就是做一些目录检查,系统检查,标准输出错误输出等设置之类的。

    // Since the child process is a batch script, let's quote things so that special characters are
    // preserved, otherwise the batch interpreter will mess up the arguments. Batch scripts are
    // weird.
    if (isWindows()) {
      List<String> winCmd = new ArrayList<>();
      for (String arg : cmd) {
        winCmd.add(quoteForBatchScript(arg));
      }
      cmd = winCmd;
    }
    
    //这里创建的ProcessBuilder对象,并设置环境变量(sparkConf通过环境变量获取spark相关配置,所以把参数设置到环境变量中可以让sparkConf获取到)
    ProcessBuilder pb = new ProcessBuilder(cmd.toArray(new String[cmd.size()]));
    for (Map.Entry<String, String> e : builder.childEnv.entrySet()) {
      pb.environment().put(e.getKey(), e.getValue());
    }

    if (workingDir != null) {
      pb.directory(workingDir);
    }

    // Only one of redirectError and redirectError(...) can be specified.
    // Similarly, if redirectToLog is specified, no other redirections should be specified.
    checkState(!redirectErrorStream || errorStream == null,
      "Cannot specify both redirectError() and redirectError(...) ");
    checkState(!redirectToLog ||
      (!redirectErrorStream && errorStream == null && outputStream == null),
      "Cannot used redirectToLog() in conjunction with other redirection methods.");

    if (redirectErrorStream || redirectToLog) {
      pb.redirectErrorStream(true);
    }
    if (errorStream != null) {
      pb.redirectError(errorStream);
    }
    if (outputStream != null) {
      pb.redirectOutput(outputStream);
    }

    return pb;
  }

3.startApplication方法
  //这个方法也是用来执行任务的。只是比launch 多了一个状态监控的功能,用户可以通过提供sparkAppHandle.Listener来实现任务状态变化时做一些动作

  public SparkAppHandle startApplication(SparkAppHandle.Listener... listeners) throws IOException {
    ChildProcAppHandle handle = LauncherServer.newAppHandle();//这里启动一个LauncherServer的服务来接收app状态变化的数据,更详细的将会在其它部分记录
    for (SparkAppHandle.Listener l : listeners) {
      handle.addListener(l); //这里把用户提供的监听放到执行函数里执行,具体如何实现将在其它部分记录
    }

    //这里就是获取spark-submit标准输出数据的log文件名称,如果没有设置,就通过下面代码进行生成和获取,根据类名和包名自动生成
    String loggerName = builder.getEffectiveConfig().get(CHILD_PROCESS_LOGGER_NAME);
    ProcessBuilder pb = createBuilder();
    // Only setup stderr + stdout to logger redirection if user has not otherwise configured output
    // redirection.
    if (loggerName == null) {
      String appName = builder.getEffectiveConfig().get(CHILD_PROCESS_LOGGER_NAME);
      if (appName == null) {
        if (builder.appName != null) {
          appName = builder.appName;
        } else if (builder.mainClass != null) {
          int dot = builder.mainClass.lastIndexOf(".");
          if (dot >= 0 && dot < builder.mainClass.length() - 1) {
            appName = builder.mainClass.substring(dot + 1, builder.mainClass.length());
          } else {
            appName = builder.mainClass;
          }
        } else if (builder.appResource != null) {
          appName = new File(builder.appResource).getName();
        } else {
          appName = String.valueOf(COUNTER.incrementAndGet());
        }
      }
      String loggerPrefix = getClass().getPackage().getName();  //获取包名
      loggerName = String.format("%s.app.%s", loggerPrefix, appName);  //根据包名和appname生成log文件名
      pb.redirectErrorStream(true);
    }

    //这里把LauncherServer的端口号通告到环境变量里,LauncherBackend就是通过环境变量获取LauncherServer的端口号进行通信的。
   //LauncherBackend 的具体详情将会在其他部分进行记录
    pb.environment().put(LauncherProtocol.ENV_LAUNCHER_PORT,
      String.valueOf(LauncherServer.getServerInstance().getPort()));
    pb.environment().put(LauncherProtocol.ENV_LAUNCHER_SECRET, handle.getSecret());
    try {
      handle.setChildProc(pb.start(), loggerName);  //启动spark-submit 提交任务,并把标准log输出到之前设置的loggername里。
    } catch (IOException ioe) {
      handle.kill();
      throw ioe;
    }

    return handle;
  }
posted @ 2018-01-30 19:08  vv.past  阅读(2095)  评论(0编辑  收藏  举报