組件
- Reader:Reader為數(shù)據(jù)采集模塊,負(fù)責(zé)采集數(shù)據(jù)源的數(shù)據(jù),將數(shù)據(jù)發(fā)送給Framework。
- Writer: Writer為數(shù)據(jù)寫入模塊,負(fù)責(zé)不斷向Framework取數(shù)據(jù),并將數(shù)據(jù)寫入到目的端。
- Transformer:在數(shù)據(jù)同步、傳輸過(guò)程中,存在用戶對(duì)于數(shù)據(jù)傳輸進(jìn)行特殊定制化的需求場(chǎng)景,包括裁剪列、轉(zhuǎn)換列等工作
-
Job
:Job
是DataX用以描述從一個(gè)源頭到一個(gè)目的端的同步作業(yè),是DataX數(shù)據(jù)同步的最小業(yè)務(wù)單元。 -
Task
:Task
是把Job
拆分得到的最小執(zhí)行單元。 -
JobContainer
:Job
執(zhí)行器,負(fù)責(zé)Job
全局拆分、調(diào)度、前置語(yǔ)句和后置語(yǔ)句等工作的工作單元。 -
TaskGroupContainer
:TaskGroup
執(zhí)行器,負(fù)責(zé)執(zhí)行一組Task
的工作單元。 -
TaskGroup
: 描述的是一組Task
集合。在同一個(gè)TaskGroupContainer
執(zhí)行下的Task
集合稱之為TaskGroup
參數(shù)
datax.py腳本接收參數(shù)
-j:jvm參數(shù)
--jobid: 在local與distribute模式下運(yùn)行的作業(yè)唯一id
-m: 運(yùn)行datax時(shí)的-Dmode參數(shù),可選standalone, local, distribute
-p: 運(yùn)行datax時(shí)的額外的附加的運(yùn)行參數(shù)
-r: 查看reader模板,與-w一起使用,${datax.home}/plugin/reader/${該讀插件名稱}/plugin_job_template.json
-w: 查看writer模板,與-r一起使用,${datax.home}/plugin/reader/${該寫插件名稱}/plugin_job_template.json
C:/dev/Python27/python.exe datax.py -p"-Dlast=123 -Dend=456" --jobid=123456 C:/Users/Lenovo/Desktop/datax/jobConf/mysql2mysql.json
#分配了啟動(dòng) 限制堆大小為1g,不可擴(kuò)展,發(fā)生了 內(nèi)存溢出錯(cuò)誤dump路徑為C:\Users\Lenovo\PycharmProjects/log
java -server
-Xms1g -Xmx1g -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=C:\Users\Lenovo\PycharmProjects/log
-Dloglevel=info -Dfile.encoding=UTF-8
-Dlogback.statusListenerClass=ch.qos.logback.core.status.NopStatusListener
-Djava.security.egd=file:///dev/urandom
-Ddatax.home=C:\Users\Lenovo\PycharmProjects
-Dlogback.configurationFile=C:\Users\Lenovo\PycharmProjects/conf/logback.xml
-classpath C:\Users\Lenovo\PycharmProjects/lib/*
-Dlog.file.name=onf\mysql2mysql_json
-Dlast=123 -Dend=456 com.alibaba.datax.core.Engine
-mode standalone -jobid 123456 -job C:\Users\Lenovo\Desktop\datax\jobConf\mysql2mysql.json
#默認(rèn)以standalone模式啟動(dòng)
datax.home=當(dāng)前運(yùn)行目錄
logback.configurationFile=${data.home}/conf/logback.xml
classpath=${datax.home}/lib/*
CoreConstant中會(huì)提取datax.home這個(gè)環(huán)境變量供全局使用,拼接成core.json,plugin.json的地址
- 一些環(huán)境變量
- mode:standalone, local, distribute 選擇作業(yè)運(yùn)行模式
- jobid:在local與distribute模式下運(yùn)行的作業(yè)唯一id
- job:作業(yè)配置文件路徑
- classpath
-
Standalone
: 單進(jìn)程運(yùn)行,沒(méi)有外部依賴。 -
Local
: 單進(jìn)程運(yùn)行,統(tǒng)計(jì)信息、錯(cuò)誤信息匯報(bào)到集中存儲(chǔ)。 -
Distrubuted
: 分布式多進(jìn)程運(yùn)行,依賴DataX Service
服務(wù)。
運(yùn)行流程
作業(yè)配置加載
通過(guò)作業(yè)配置文件路徑(-job參數(shù))來(lái)加載作業(yè)配置文件。
CoreConstant通過(guò)環(huán)境變量獲取core配置文件路徑(datax.home拼接),加載core配置。
通過(guò)
job.content[0].reader/writer.name
讀取該作業(yè)的插件名,通過(guò)job.preHandler.pluginName
/job.postHandler.pluginName
讀取該作業(yè)的前置或后置處理插件名。通過(guò)CoreConstant獲取以上所有讀取到的插件名的絕對(duì)路徑。-
通過(guò)路徑來(lái)加載插件配置文件內(nèi)容。插件的配置文件按如下約束。
{ "name": "mysqlwriter", "class": "com.alibaba.datax.plugin.writer.mysqlwriter.MysqlWriter", "description": "", "developer": "" }
在
Configuration.from(String json)
讀取任意配置文件時(shí)都會(huì)將${xxx}
或$xxx
占位符替換成xxx對(duì)應(yīng)的環(huán)境變量。即-Dlast=123
使配置文件中${last}
替換成123
, 該邏輯存在StrUtil.replaceVariable(json)
中-
將core,job,plugin配置合并,生成全局使用的配置
Configuration
。{ "entry":{……}, "common":{……}, "core":{ "container":{ "job":{ "id": ${jobId}, ………………其他配置 } } }, "job":{……}, "plugin":{ "reader":{ "${pluginName}":{ "name": "", "class": "", "description": "", "developer": "" } }, "writer":{ "${pluginName}":{……} } } }
最后做過(guò)濾輸出和檢查配置
引擎啟動(dòng)
- 從common取出需要的轉(zhuǎn)換格式
yyyy-MM-dd
或編碼UTF-8
,用于String與Date或Bytes的互相轉(zhuǎn)換 - 將配置
Configuration
傳入LoadUtil
Jar加載器,后面會(huì)使用LoadUtil進(jìn)行插件Jar的動(dòng)態(tài)加載。包括對(duì)每個(gè)插件的加載隔離機(jī)制和加載器緩沖的實(shí)現(xiàn)。 - 根據(jù)
core.container.model
判斷使用TaskGroupContainer
還是使用JobContainer
,默認(rèn)使用JobContainer
- PerfTrace初始化,默認(rèn)不使用PerfTrace,獲取
job.JobInfo
默認(rèn)無(wú)該配置項(xiàng), - 容器啟動(dòng)
JobContainer
其中加載操作中的加載插件時(shí):
為避免jar沖突,比如hbase可能有多個(gè)版本的讀寫依賴jar包,JobContainer和TaskGroupContainer
就需要脫離當(dāng)前classLoader去加載這些jar包,執(zhí)行完成后,又退回到原來(lái)classLoader上繼續(xù)執(zhí)行接下來(lái)的代碼
每個(gè)Jar的執(zhí)行加載都有單一的類加載器進(jìn)行隔離加載,JarLoader會(huì)緩沖到j(luò)arLoaderCenter中
LoadUtil.getJarLoader 就會(huì)根據(jù)插件類型和名字去jarLoaderCenter中獲取加載器,獲取不到之后才會(huì)重新構(gòu)造一個(gè)加載器
preHandle前置處理器:根據(jù)
job.preHandler.pluginName
加載已存在的插件,并執(zhí)行插件的preHandler
方法-
init初始化:
根據(jù)
job.content[0].reader/writer.name
插件名來(lái)加載reader和writer插件,并保存reader/wirter PluginName
,賦值
Configuration
,賦值Job插件
本身和對(duì)端插件
的配置job.content[0].reader/writer.parameter
與對(duì)端的插件名子。并且執(zhí)行他們的init方法。
prepare準(zhǔn)備:執(zhí)行reader/writer的
prepare
方法-
split切分任務(wù):
-
根據(jù)
job.setting.speed.byte
,core.transport.channel.speed.byte
和job.setting.speed.record
,core.transport.channel.speed.record
的值計(jì)算出并發(fā)task數(shù)needChannelNumber
,具體算法作業(yè)byte限速除于單個(gè)channel的byte限速 得到 byte限速下的所需channel 作業(yè)record限速除于單個(gè)channel的record限速 得到record限速下的所需channel 對(duì)比兩個(gè)channel數(shù)取最小的作為needChannelNumber 若job.setting.speed.byte與job.setting.speed.record設(shè)置為空 則直接使用job.setting.speed.channel作為needChannelNumber 若都為空,則拋出異常
-
-
執(zhí)行reader和writer的
split
方法,獲取經(jīng)過(guò)split
每個(gè)Task的reader和writer的配置。執(zhí)行reader和writer最細(xì)粒度的切分,需要注意的是,writer的切分結(jié)果要參照reader的切分結(jié)果, 達(dá)到切分后數(shù)目相等,才能滿足1:1的通道模型,所以這里可以將reader和writer的配置整合到一起。 計(jì)算出的needChannelNumber/tableNUm * 分裂因子 = 最終需要的Task數(shù)量
在split方法中需要根據(jù)tables數(shù)量,splitPk進(jìn)行分隔任務(wù),每個(gè)任務(wù)下的connection都會(huì)根據(jù)切分結(jié)果與column,where來(lái)生成一個(gè)querySql。
-
獲取作業(yè)的
transformer
配置,每個(gè)Task的reader和writer配置再加上該transformer
的配置合并。將原本的job.content
替換。即原本只有單個(gè)content,經(jīng)過(guò)split后產(chǎn)生多個(gè)content,并為其設(shè)置遞增的taskId{ "job": { "content": [ { "taskId": 1, "reader": { "parameter": { "querySql": "" } }, "transformer":[], "writer": {} }, { "taskId": 2, "reader": {}, "transformer":[], "writer": {} }, { "taskId": 3, "reader": {}, "transformer":[], "writer": {} } ], "setting": { "speed": { "channel": "" } } } }
-
schedule調(diào)度:
parseAndGetResourceMarkAndTaskIdMap
:以reader.parameter.loadBalanceResourceMark
資源名做分組。得出一個(gè) 資源名稱 --> taskId(List) 的 map 映射關(guān)系。在split階段,會(huì)對(duì)插件的loadBalanceResourceMark
進(jìn)行設(shè)置,通常是使用jdbc連接的host-
doAssign:根據(jù)
parseAndGetResourceMarkAndTaskIdMap
的結(jié)果,將需要運(yùn)行Task按一個(gè)特定的規(guī)則分配到taskGroup中。每個(gè)TaskGroup都將獲得一份Configuration
克隆,設(shè)置每個(gè)taskConfiguration的content中的core.container.taskGroup.id
。并且修正job.content
,使他的配置文件回到單content狀態(tài)a 庫(kù)上有表:0, 1, 2 b 庫(kù)上有表:3, 4 c 庫(kù)上有表:5, 6, 7 如果有 4個(gè) taskGroup 打豎遍歷添加到taskGroup 資源: 0 3 5|1 4 6| 2 7 taskGroup: 0 3 5 1|4 6 2 7 則 doAssign 后的結(jié)果為: taskGroup-0: 0, 4, taskGroup-1: 3, 6, taskGroup-2: 5, 2, taskGroup-3: 1, 7
-
adjustChannelNumPerTaskGroup:修正因?yàn)闊o(wú)法平均分配的少一個(gè)task的taskGroup的
core.container.taskGroup.channel
的更改3個(gè)task分配到2個(gè)taskGroup中時(shí),會(huì)造成一個(gè)taskGroup的channel為2,一個(gè)taskGroup的channel為1 所以要將少了一個(gè)task的taskGroup的channel進(jìn)行修正優(yōu)化。
為每個(gè)taskGroup修正
core.container.job.mode
為standaloneStandAloneScheduler#registerCommunication:為每個(gè)taskGroup注冊(cè)Communication(狀態(tài)及統(tǒng)計(jì)信息交互)
StandAloneScheduler#startAllTaskGroup:為每個(gè)taskGroup創(chuàng)建
TaskGroupContainer
并代理到TaskGroupContainerRunner
啟動(dòng)TaskGroupContainer
。其中動(dòng)態(tài)加載transfomer,數(shù)據(jù)采集就在這個(gè)步驟之內(nèi)。
post:執(zhí)行reader和writer的
post
方法postHandle:根據(jù)
job.postHandler.pluginName
加載已存在的插件,并執(zhí)行插件的postHandler
方法invokeHooks:根據(jù)
/hook
目錄調(diào)用外部hook
TaskGroupContainer
類圖
reader與writer的數(shù)據(jù)傳輸