参考文档:https://blog.csdn.net/weixin_43161811/article/details/103152867
入口命令(参看第一篇文章):
java ClientFrontend run -c com.tclking.ai.PVUVDayMain -m yarn-cluster examples/batch/WordCount.jar
ClientFrontend.main-->cli.parseParameters(args) // Line 1083 --->run(args)
代码如下:
protected void run(String[] args) throws Exception { LOG.info("Running 'run' command."); //构建新的options即commandOptions: 如下代码会调用CliFrontendParser.getProgramSpecificOptions相关添加 option
// options.addOption(JAR_OPTION); new Option("j", "jarfile", true, "Flink program JAR file.");
// options.addOption(CLASS_OPTION); new Option("c", "class", true, "Class with the program entry point
// options.addOption(CLASSPATH_OPTION);new Option("C", "classpath", true, "Adds a URL to each user code classloader on all nodes in the cluster.
// options.addOption(PARALLELISM_OPTION);new Option("p", "parallelism", true,"The parallelism with which to run the program. Optional flag to override the default value
// options.addOption(ARGS_OPTION); new Option("a", "arguments", true,"Program arguments. Arguments can also be added without -a, simply as trailing parameters.")
// options.addOption(LOGGING_OPTION); new Option("q", "sysoutLogging", false, "If present, suppress logging output to standard out.");
// options.addOption(DETACHED_OPTION); new Option("d", "detached", false, "If present, runs the job in detached mode")
// options.addOption(SHUTDOWN_IF_ATTACHED_OPTION);new Option("sae", "shutdownOnAttachedExit", false"If the job is submitted in attached mode, perform a best-effort cluster shutdown "
// options.addOption(YARN_DETACHED_OPTION);
// options.addOption(PY_OPTION);
// options.addOption(PYFILES_OPTION);
// options.addOption(PYMODULE_OPTION);
// options.addOption(SAVEPOINT_PATH_OPTION)new Option("s", "fromSavepoint", true,"Path to a savepoint to restore the job from (for example hdfs:///flink/savepoint-1537).")
// options.addOption(SAVEPOINT_ALLOW_NON_RESTORED_OPTION);new Option("n", "allowNonRestoredState", false
final Options commandOptions = CliFrontendParser.getRunCommandOptions();
//合并commandOptions和customCommandLineOptions,
//customCommandLineOptions 在new CliFrontend时Line 131, new options,然后遍历customCommandLines即FlinkYarnSessionCli和DefaultCLI得到
// addOption(addressOption) ; //new Option("m", "jobmanager", true,"Address of the JobManager (master) to which to connect. ")
// addOption(zookeeperNamespaceOption); //new Option("z", "zookeeperNamespace", true,"Namespace to create the Zookeeper sub-paths for high availability mode");
// addOption(applicationId); //new Option(shortPrefix + "id", longPrefix + "applicationId", true, "Attach to running YARN session");
// 并添加FlinkYarnSessionCli的addRunOptions即FlinkYarnSessionCli构造时,所有的option,参看210行
//类似: new Option(shortPrefix + "nm", longPrefix + "name", true, "Set a custom name for the application on YARN");
final Options commandLineOptions = CliFrontendParser.mergeOptions(commandOptions, customCommandLineOptions);
//args即为 命令行提交的参数: run -c com.tclking.ai.PVUVDayMain -m yarn-cluster examples/batch/WordCount.jar
// 实际调用并返回: DefaultParser.parse(commandLineOptions, args, true)
final CommandLine commandLine = CliFrontendParser.parse(commandLineOptions, args, true);
//commandLine构造RunOptions
final RunOptions runOptions = new RunOptions(commandLine);
// evaluate help flag
if (runOptions.isPrintHelp()) {
CliFrontendParser.printHelpForRun(customCommandLines);
return;
}
if (!runOptions.isPython()) {
// Java program should be specified a JAR file
if (runOptions.getJarFilePath() == null) {
throw new CliArgsException("Java program should be specified a JAR file.");
}
}
final PackagedProgram program;
try {
LOG.info("Building program from JAR file");
program = buildProgram(runOptions);
}
catch (FileNotFoundException e) {
throw new CliArgsException("Could not build the program from JAR file.", e);
}
//
// 遍历customCommandLine:即FlinkYarnSessionCli和DefaultCLI,判断active(参数commandLine)
// 判断-m参数是否等于yanr-cluster,为true返回FlinkYarnSessionCli
final CustomCommandLine<?> customCommandLine = getActiveCustomCommandLine(commandLine);
try {
runProgram(customCommandLine, commandLine, runOptions, program);
} finally {
program.deleteExtractedLibraries();
}
}
// buildProgram主要构建PackagedProgram: jar 路径,classpath,入口类entryPointClass, 参数,
PackagedProgram buildProgram(ProgramOptions options) throws FileNotFoundException, ProgramInvocationException { String[] programArgs = options.getProgramArgs(); String jarFilePath = options.getJarFilePath(); List<URL> classpaths = options.getClasspaths(); // Get assembler class String entryPointClass = options.getEntryPointClassName(); File jarFile = null; if (options.isPython()) { // If the job is specified a jar file if (jarFilePath != null) { jarFile = getJarFile(jarFilePath); } // If the job is Python Shell job, the entry point class name is PythonGateWayServer. // Otherwise, the entry point class of python job is PythonDriver if (entryPointClass == null) { entryPointClass = "org.apache.flink.client.python.PythonDriver"; } } else { if (jarFilePath == null) { throw new IllegalArgumentException("Java program should be specified a JAR file."); } jarFile = getJarFile(jarFilePath); } PackagedProgram program = entryPointClass == null ? new PackagedProgram(jarFile, classpaths, programArgs) : new PackagedProgram(jarFile, classpaths, entryPointClass, programArgs); program.setSavepointRestoreSettings(options.getSavepointRestoreSettings()); return program; }
核心逻辑: runProgram(customCommandLine, commandLine, runOptions, program);
// customCommandLine为 FlinkYarnSessionCli实例
// commandLine为
private <T> void runProgram(
CustomCommandLine<T> customCommandLine,
CommandLine commandLine,
RunOptions runOptions,
PackagedProgram program) throws ProgramInvocationException, FlinkException {
// 根据用户命令行参数,创建ClusterDescriptor,ClusterDescriptor是一个集群属性的描述对象,
// 用于部署集群(例如 Yarn、Mesos),并且返回一个与集群通信的客户端
// customCommandLine为 FlinkYarnSessionCli实例。具体参看明细1
final ClusterDescriptor<T> clusterDescriptor = customCommandLine.createClusterDescriptor(commandLine);
try {
final T clusterId = customCommandLine.getClusterId(commandLine);
final ClusterClient<T> client;
// directly deploy the job if the cluster is started in job mode and detached
if (clusterId == null && runOptions.getDetachedMode()) {
int parallelism = runOptions.getParallelism() == -1 ? defaultParallelism : runOptions.getParallelism();
final JobGraph jobGraph = PackagedProgramUtils.createJobGraph(program, configuration, parallelism);
final ClusterSpecification clusterSpecification = customCommandLine.getClusterSpecification(commandLine);
client = clusterDescriptor.deployJobCluster(
clusterSpecification,
jobGraph,
runOptions.getDetachedMode());
logAndSysout("Job has been submitted with JobID " + jobGraph.getJobID());
try {
client.shutdown();
} catch (Exception e) {
LOG.info("Could not properly shut down the client.", e);
}
} else {
final Thread shutdownHook;
if (clusterId != null) {
client = clusterDescriptor.retrieve(clusterId);
shutdownHook = null;
} else {
// also in job mode we have to deploy a session cluster because the job
// might consist of multiple parts (e.g. when using collect)
final ClusterSpecification clusterSpecification = customCommandLine.getClusterSpecification(commandLine);
client = clusterDescriptor.deploySessionCluster(clusterSpecification);
// if not running in detached mode, add a shutdown hook to shut down cluster if client exits
// there's a race-condition here if cli is killed before shutdown hook is installed
if (!runOptions.getDetachedMode() && runOptions.isShutdownOnAttachedExit()) {
shutdownHook = ShutdownHookUtil.addShutdownHook(client::shutDownCluster, client.getClass().getSimpleName(), LOG);
} else {
shutdownHook = null;
}
}
try {
client.setPrintStatusDuringExecution(runOptions.getStdoutLogging());
client.setDetached(runOptions.getDetachedMode());
LOG.debug("{}", runOptions.getSavepointRestoreSettings());
int userParallelism = runOptions.getParallelism();
LOG.debug("User parallelism is set to {}", userParallelism);
if (ExecutionConfig.PARALLELISM_DEFAULT == userParallelism) {
userParallelism = defaultParallelism;
}
executeProgram(program, client, userParallelism);
} finally {
if (clusterId == null && !client.isDetached()) {
// terminate the cluster only if we have started it before and if it's not detached
try {
client.shutDownCluster();
} catch (final Exception e) {
LOG.info("Could not properly terminate the Flink cluster.", e);
}
if (shutdownHook != null) {
// we do not need the hook anymore as we have just tried to shutdown the cluster.
ShutdownHookUtil.removeShutdownHook(shutdownHook, client.getClass().getSimpleName(), LOG);
}
}
try {
client.shutdown();
} catch (Exception e) {
LOG.info("Could not properly shut down the client.", e);
}
}
}
} finally {
try {
clusterDescriptor.close();
} catch (Exception e) {
LOG.info("Could not properly close the cluster descriptor.", e);
}
}
}
明细1:customCommandLine.createClusterDescriptor(commandLine)
@Override
public AbstractYarnClusterDescriptor createClusterDescriptor(CommandLine commandLine) throws FlinkException {
// 基于原有在flink/conf下的配置文件对象,将命令行参数为准替换配置文件设置,如jm,tm设置
final Configuration effectiveConfiguration = applyCommandLineOptionsToConfiguration(commandLine);
// 下属方法调用getClusterDescriptor, 见明细代码2
return createDescriptor(
effectiveConfiguration,
yarnConfiguration,
configurationDirectory,
commandLine);
}
明细代码2:
private AbstractYarnClusterDescriptor getClusterDescriptor(
Configuration configuration,
YarnConfiguration yarnConfiguration,
String configurationDirectory) {
// 实例化 yarnClient = new YarnClientImpl()
final YarnClient yarnClient = YarnClient.createYarnClient();
// yarnConfiguration为直接new YarnConfigruation(),主要一些默认静态属性
// 将配置文件的yarn属性值赋值给对象属性
yarnClient.init(yarnConfiguration);
// 1 构造资源管理器客户端 ClientRMProxy为单例,返回值赋给yarnClient 属性rmClient
this.rmClient=ClientRMProxy.createRMProxy(this.getConfig(), ApplicationClientProtocol.class)
// 2 按RM高可用与否,返回对应的proxy,并创建RetryProxy。 参看明细3。
yarnClient.start();
// 构造YarnClusterDescriptor对象。参数yarnClient的rmClient属性为上述代理返回。
return new YarnClusterDescriptor(
configuration,
yarnConfiguration,
configurationDirectory,
yarnClient,
false);
}
明细3:ClientRMProxy根据是否高可用返回对应rmClient
protected static <T> T createRMProxy(Configuration configuration, Class<T> protocol, RMProxy instance) throws IOException {
YarnConfiguration conf = configuration instanceof YarnConfiguration ? (YarnConfiguration)configuration : new YarnConfiguration(configuration);
RetryPolicy retryPolicy = createRetryPolicy(conf);
if (HAUtil.isHAEnabled(conf)) {
//高可用情况
RMFailoverProxyProvider<T> provider = instance.createRMFailoverProxyProvider(conf, protocol);
return RetryProxy.create(protocol, provider, retryPolicy);
} else {
// 非高可用,instance为单例ClientRMProxy,protocol=ApplicationClientProtocol
InetSocketAddress rmAddress = instance.getRMAddress(conf, protocol);
LOG.info("Connecting to ResourceManager at " + rmAddress);
// 返回YarnRPC.create(conf).getProxy(protocol, rmAddress, conf);
// YarnRPC.create(conf)根据conf属性"yarn.ipc.rpc.class"获取rpc类,默认值HadoopYarnProtoRPC.class
// 获取代理HadoopYarnProtoRPC.getProxy(protocol, rmAddress, conf);
// 先获取客户端工厂,参数yarn.ipc.client.factory.class指定,默认值是org.apache.hadoop.yarn.factories.impl.pb.RpcClientFactoryPBImpl
// 再获取客户端:RpcClientFactoryPBImpl.getClient:实际返回ApplicationClientProtocol的客户端远程接口包装类:ApplicationClientProtocol+PBClientImpl对应的构造体(Long.TYPE, InetSocketAddress.class, Configuration.class);包名+".impl.pb.client"
T proxy = getProxy(conf, protocol, rmAddress);
// retryPolicy为根据yarn配置文件,针对不同异常重试时间间隔策略设置
// proxyProvider=new DefaultFailoverProxyProvider(protocol, implementation)
// 使用动态代理:获取protocol的动态代理。实际将调用RetryInvocationHandler的invoke方法(会调用上一行返回的proxy的方法)。Proxy.newProxyInstance(proxyProvider.getInterface().getClassLoader(), new Class[]{protocol}, new RetryInvocationHandler(proxyProvider, retryPolicy));
return RetryProxy.create(protocol, proxy, retryPolicy);
}
}