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;
}