具体流程代码
step1: 入口
知识点:
- 首先入口在
com.alibaba.datax.core.Engine
类,命令行参数有-job
、-jobid
和-mode
options.addOption("job", true, "Job config.");
options.addOption("jobid", true, "Job unique id.");
options.addOption("mode", true, "Job runtime mode.");
step2: 封装配置
entry()
先获取命令行参数-mode
,-jobid
,-job
。- 解析系统配置的
json
和用户自己编辑的json
文件配置,然后统一封装成Configuration
类,传给Engine.start()
使用。
- 读取核心配置文件
core.json
以及对应读写的plugin.json
configuration.merge(
// 读取核心core.json
ConfigParser.parseCoreConfig(CoreConstant.DATAX_CONF_PATH),
false);
// todo config优化,只捕获需要的plugin
String readerPluginName = configuration.getString(
CoreConstant.DATAX_JOB_CONTENT_READER_NAME);
String writerPluginName = configuration.getString(
CoreConstant.DATAX_JOB_CONTENT_WRITER_NAME);
- 这里获取的参数
jobid
,如果用户没有明确的指定,则datax.py
会指定jobid
默认值为-1
。
Engine
的start()
主要完成:
- 首先往配置类中绑定一些信息,比如ColumnCast等转换信息。
- 初始化PluginLoader(插件加载器), 用来获取插件配置
- 创建JobContainer,并且启动,JobContainer是一次数据同步job的运行容器.
public void start(Configuration allConf) {
// 绑定column转换信息 包括json中的字符信息
ColumnCast.bind(allConf);
/**
* 初始化PluginLoader,可以获取各种插件配置
*/
LoadUtil.bind(allConf);
// DATAX_CORE_CONTAINER_MODEL: core.container.model
boolean isJob = !("taskGroup".equalsIgnoreCase(allConf
.getString(CoreConstant.DATAX_CORE_CONTAINER_MODEL)));
//JobContainer会在schedule后再行进行设置和调整值
int channelNumber =0;
// 抽象容器类 有两种实现 JobContainer和TaskGroupContainer
AbstractContainer container;
long instanceId;
int taskGroupId = -1;
if (isJob) {
allConf.set(CoreConstant.DATAX_CORE_CONTAINER_JOB_MODE, RUNTIME_MODE);
container = new JobContainer(allConf);
instanceId = allConf.getLong(
CoreConstant.DATAX_CORE_CONTAINER_JOB_ID, 0);
} else {
// 基本用不到
container = new TaskGroupContainer(allConf);
instanceId = allConf.getLong(
CoreConstant.DATAX_CORE_CONTAINER_JOB_ID);
taskGroupId = allConf.getInt(
CoreConstant.DATAX_CORE_CONTAINER_TASKGROUP_ID);
channelNumber = allConf.getInt(
CoreConstant.DATAX_CORE_CONTAINER_TASKGROUP_CHANNEL);
}
//缺省打开perfTrace
boolean traceEnable = allConf.getBool(CoreConstant.DATAX_CORE_CONTAINER_TRACE_ENABLE, true);
boolean perfReportEnable = allConf.getBool(CoreConstant.DATAX_CORE_REPORT_DATAX_PERFLOG, true);
//standalone模式的 datax shell任务不进行汇报
if(instanceId == -1){
perfReportEnable = false;
}
// todo 不太明白这里要做什么
int priority = 0;
try {
priority = Integer.parseInt(System.getenv("SKYNET_PRIORITY"));
}catch (NumberFormatException e){
LOG.warn("prioriy set to 0, because NumberFormatException, the value is: "+System.getProperty("PROIORY"));
}
// 总配置文件中提取出跟一个job有关的配置
Configuration jobInfoConfig = allConf.getConfiguration(CoreConstant.DATAX_JOB_JOBINFO);
//初始化PerfTrace
PerfTrace perfTrace = PerfTrace.getInstance(isJob, instanceId, taskGroupId, priority, traceEnable);
perfTrace.setJobInfo(jobInfoConfig,perfReportEnable,channelNumber);
container.start();
}
step3: 初始化并启动JobContainer容器
- 根据配置初始化不同的容器(
JobContainer
和TaskGroupContainer
)并启动,一般都使用Job
容器,check job model first
,位置com.alibaba.datax.core.JobContainer#start
。 job
实例运行在jobContainer
容器中,它是所有任务的master
,负责初始化、拆分、调度、运行、回收、监控和汇报,但它并不做实际的数据同步操作。JobContainer
主要负责的工作全部在start()
中,包括init
、prepare
、split
、scheduler
。init()
负责Reader
、Writer
插件的初始化和热加载。prepare()
方法做Read、Write前置准备(如Presql
等)split()
方法根据配置的并发参数(channel
、bytes
和Record
必须配置其一,优先级bytes>=Record>channel
),对job
进行切分,返回多个task的配置列表。scheduler()
是真正的调度任务与运行,task的调度方式按照轮询式。
@Override
public void start() {
LOG.info("DataX jobContainer starts job.");
boolean hasException = false;
boolean isDryRun = false;
try {
// 计时开始
this.startTimeStamp = System.currentTimeMillis();
// 预检查
isDryRun = configuration.getBool(CoreConstant.DATAX_JOB_SETTING_DRYRUN, false);
if(isDryRun) {
///...省略,几乎用不到
} else {
//clone一份配置,因为要做修改
userConf = configuration.clone();
//前置处理
this.preHandle();
//初始化read和write插件
this.init();
//进行插件的前置操作,有些插件不需要
this.prepare();
//切分任务,为并发做准备 主要adjustChannelNumber
this.totalStage = this.split();
//任务调度,启动任务 轮询式
this.schedule();
//和preparea对应,插件的后置操作
this.post();
//任务后置处理
this.postHandle