base FlinkX源码1.12_release版本
flinkX启动主要依赖两部分:
flink-client
它主要完成解析用户的提交指令,完成作业提交
flink-dist
这部分是打包后的target,包含了各个异构数据源的connector,和关键的flinkx-core
启动脚本:flinx/bin/flinkx
A.提交过程:
入口类:com.dtstack.flinkx.client.Launcher
从main方法开始:
1.通过OptionParser类解析校验启动时传入的参数
OptionParser optionParser = new OptionParser(args);
Options launcherOptions = optionParser.getOptions();
2.从配置中读取flink_dist目录位置,如果配置中没有,就从环境变量中获取
findDefaultConfigDir(launcherOptions);
3.如果不是local模式启动,还需要读取flink配置和hadoop配置位置(优先参数,其次环境变量)
4.将参数List转换为Map,方便get
5.获取参数中-p的内容,将其替换到-job中(这个-p配置是k=v,k=v,k=v形式,此处会替换掉-job配置中的${k}内容)
String s = temp.get("-p");
if (StringUtils.isNotBlank(s)) {
HashMap<String, String> parameter = JsonModifyUtil.CommandTransform(s);
temp.put("-job", JsonModifyUtil.JsonValueReplace(temp.get("-job"), parameter));
}
6.创建JobDeployer对象,这里边主要就是各种处理后的配置信息
JobDeployer jobDeployer = new JobDeployer(launcherOptions, argList);
7.通过判断启动参数中的启动模式,创建ClusterClientHelper对象,不同模式submit任务的方式有差异
例如:
local模式:这个没什么好说的,直接执行Main
String[] args = jobDeployer.getProgramArgs().toArray(new String[0]);
Main.main(args);
yarn-session模式:这个机制和原生flink一样,维持一个flink集群在yarn上,每次向这个集群提交job,细节如下:
a.启动参数,yarn,hadoop配置获取
Options launcherOptions = jobDeployer.getLauncherOptions();
List<String> programArgs = jobDeployer.getProgramArgs();
Configuration flinkConfig = launcherOptions.loadFlinkConfiguration();
String yarnConfDir = launcherOptions.getHadoopConfDir();
b.初始化yarn客户端
YarnClient yarnClient = YarnClient.createYarnClient()
c.获取当前flink集群的applicationId,按需调整内存和cpu配置
applicationId = getAppIdFromYarn(yarnClient, flinkConfig);
d.配置高可用信息
e.初始化YarnClusterDescriptor
f.生成jobGraph,提交作业,拿到ClusterClient
ClusterClient clusterClient =
yarnClusterDescriptor.retrieve(applicationId).getClusterClient();
JobGraph jobGraph =
JobGraphUtil.buildJobGraph(launcherOptions, programArgs.toArray(new String[0]));
JobID jobID = (JobID) clusterClient.submitJob(jobGraph).get();
yarn-PerJob模式:这个就是直接读取配置提交yarn 任务了,最后也是拿到一个ClusterClient
8.至此完成任务提交,不同模式提交后(local, yarn-perJob,yarn-session,k8s等),真正执行任务的是flink-core中的Main
B.执行过程:
flink-core中
入口类:com.dtstack.flinkx.Main
1.读取各种参数
Options options = new OptionParser(args).getOptions();
String job = URLDecoder.decode(options.getJob(), Charsets.UTF_8.name());
Properties confProperties = PropertiesUtil.parseConf(options.getConfProp());
2.创建flink StreamExecutionEnvironment StreamTableEnvironment
StreamExecutionEnvironment env = EnvFactory.createStreamExecutionEnvironment(options);
StreamTableEnvironment tEnv =
EnvFactory.createStreamTableEnvironment(env, confProperties, options.getJobName());
3.根据参数中的作业类型判断是sql任务还是同步任务。
case SQL:
exeSqlJob(env, tEnv, job, options);
break;
case SYNC:
exeSyncJob(env, tEnv, job, options);
break;
这里先看同步任务:
4.检查同步任务需要的各种配置,source & sink & metric 插件的路径
configStreamExecutionEnvironment(env, options, config);
5. DataSyncFactoryUtil.discoverSource 这个是source的实例化重点
SourceFactory sourceFactory = DataSyncFactoryUtil.discoverSource(config, env);
a.主要是根据配置json中的插件名,反射到对应的connector
return ClassLoaderManager.newInstance(
urlList,
cl -> {
Class<?> clazz = cl.loadClass(pluginClassName);
Constructor<?> constructor =
clazz.getConstructor(
SyncConf.class, StreamExecutionEnvironment.class);
return (SourceFactory) constructor.newInstance(config, env);
});
b.通过connector可以得到一个SourceFactory的实例(这里的SourceFactory概念,相当于旧版本FlinkX的Reader)
如果我们想自定义一个source,核心是实现SourceFactory,InputFormat,和InputFormatBuilder。
InputFormatBuilder负责生成InputFormat,SourceFactory的createSource方法通过InputFormat生成dataStream。
以HDFS和JDBC为例,这里的flink,flinkX,用户自定义接口的继承和实现关系可以参考下图:
6.调用factory的createSource获得ds后,根据Reader配置调整parallelism,转换sql,reblance等,如果有转换sql,还需要将ds注册成Table
if (transformer) {
dataStream = syncStreamToTable(tableEnv, config, dataStreamSource);
}
7. DataSyncFactoryUtil.discoverSink 这个是sink的实例化重点。
SinkFactory sinkFactory = DataSyncFactoryUtil.discoverSink(config);
DataStreamSink<RowData> dataStreamSink = sinkFactory.createSink(dataStream);
实现规范类似source
自定义Sink端的话,需要实现SinkFactory,OutputFormat,和OutputFarmatBuilder
最后是通过createSink,获得dsSink对象
8.根据Writer配置,调整parallelism
9.env.execute 启动flink作业,如果是本地模式,会将结果打印
JobExecutionResult result = env.execute(options.getJobName());
if (env instanceof MyLocalStreamEnvironment) {
PrintUtil.printResult(result.getAllAccumulatorResults());
}