阿里开源项目dataX简介

一、安装教程

http://www.myexception.cn/open-source/1866902.html
http://yangyoupeng-cn-fujitsu-com.iteye.com/blog/1832143

二、应用程序入口在Engine.java类中

/**
	 * Program entry </br>> NOTE: The DataX Process exists code </br> exit with
	 * 0: Job succeed </br> exit with 1: Job failed </br> exit with 2: Job
	 * failed, e.g. connetion interrupted, if we try to rerun it in a few
	 * seconds, it may succeed.
	 * 
	 *
     * @param args  cmd arguments
     *
     * @throws Exception*/
	public static void main(String[] args) throws Exception {
		String jobDescFile = null;
		if (args.length < 1) {
			System.exit(JobConfGenDriver.produceXmlConf());
		} else if (args.length == 1) {
			jobDescFile = args[0];
		} else {
			System.out.printf("Usage: java -jar engine.jar job.xml .");
			System.exit(ExitStatus.FAILED.value());
		}
		
		confLog("BEFORE_CHRIST");
		JobConf jobConf = ParseXMLUtil.loadJobConfig(jobDescFile);
		confLog(jobConf.getId());
		EngineConf engineConf = ParseXMLUtil.loadEngineConfig();
		Map<String, PluginConf> pluginConfs = ParseXMLUtil.loadPluginConfig();

		Engine engine = new Engine(engineConf, pluginConfs);

		int retcode = 0;
		try {
			retcode = engine.start(jobConf);
		} catch (Exception e) {
			logger.error(ExceptionTracker.trace(e));
			System.exit(ExitStatus.FAILED.value());
		}
		System.exit(retcode);
	}


这是一个main函数,可以Run as->Java Application。参数可以传一个任务文件路径,见第三步,不传该参数时就生成一个任务文件。
该函数的运行流程如下:

三、生成XML任务流程

dataX将一次数据导出导入操作称为一个任务(job),任务以xml形式保存在jobs目录下。生成一个任务的流程如下:


dataX支持不同数据库之间的导入导出操作,不同数据库组件以插件的形式调用。用户选择reader、writer的过程其实就是在选择插件。控制台输入如下:

Taobao DataX V1.0 
Data Source List :
0	mysql
Please choose [0-0]: 0
Data Destination List :
0	mysql
1	redis
Please choose [0-1]: 0
Generate D:\dlc\workspaces-datax\dataexchange/jobs/mysqlreader_to_mysqlwriter_1472197442111.xml successfully .


xml文件格式如下:

<?xml version="1.0" encoding="UTF-8"?>


<jobs>
  <job id="mysqlreader_to_mysqlwriter_job">
    <reader>
      <plugin>mysqlreader</plugin>
      <!--
name:ip
description:Mysql database's ip address
mandatory:true
-->
      <param key="ip" value="?"/>
      <!--
default:3306
name:port
description:Mysql database's port
mandatory:true
-->
      <param key="port" value="3306"/>
      <!--
name:dbname
description:Mysql database's name
mandatory:true
-->
      <param key="dbname" value="?"/>
      <!--
name:username
description:Mysql database's login name
mandatory:true
-->
      <param key="username" value="?"/>
      <!--
name:password
description:Mysql database's login password
mandatory:true
-->
      <param key="password" value="?"/>
      <!--
default:
name:tables
description:tables to export data, format can support simple regex, table[0-63]
range:
mandatory:true
-->
      <param key="tables" value="?"/>
      <!--
default:
name:where
description:where clause, like 'modified_time > sysdate'
range:
mandatory:false
-->
      <param key="where" value="?"/>
      <!--
default:
name:sql
description:self-defined sql statement
range:
mandatory:false
-->
      <param key="sql" value="?"/>
      <!--
default:*
name:columns
description:columns to be selected, default is *
range:
mandatory:false
-->
      <param key="columns" value="*"/>
      <!--
default:UTF-8
name:encoding
description:mysql database's encode
range:UTF-8|GBK|GB2312
mandatory:false
-->
      <param key="encoding" value="UTF-8"/>
      <!--
name:mysql.params
description:mysql driver params, starts with no &, e.g. loginTimeOut=3000&yearIsDateType=false
range:
mandatory:false
-->
      <param key="mysql.params" value="?"/>
      <!--
default:1
name:concurrency
description:concurrency of the job
range:1-10
mandatory:false
-->
      <param key="concurrency" value="1"/>
    </reader>
    <writer>
      <plugin>mysqlwriter</plugin>
      <!--
name:ip
description:Mysql database ip address
mandatory:true
-->
      <param key="ip" value="?"/>
      <!--
default:3306
name:port
description:Mysql database port
mandatory:true
-->
      <param key="port" value="3306"/>
      <!--
name:dbname
description:Mysql database name
mandatory:true
-->
      <param key="dbname" value="?"/>
      <!--
name:username
description:Mysql database login username
mandatory:true
-->
      <param key="username" value="?"/>
      <!--
name:password
description:Mysql database login password
mandatory:true
-->
      <param key="password" value="?"/>
      <!--
default:
name:table
description:table to be dumped data into
range:
mandatory:true
-->
      <param key="table" value="?"/>
      <!--
name:colorder
description:order of columns
range:
mandatory:false
-->
      <param key="colorder" value="?"/>
      <!--
default:UTF-8
name:encoding
description:
range:UTF-8|GBK|GB2312
mandatory:false
-->
      <param key="encoding" value="UTF-8"/>
      <!--
name:pre
description:execute sql before dumping data
mandatory:false
-->
      <param key="pre" value="?"/>
      <!--
name:post
description:execute sql after dumping data
mandatory:false
-->
      <param key="post" value="?"/>
      <!--
default:0
name:limit
description:error limit
range:[0-65535]
mandatory:false
-->
      <param key="limit" value="0"/>
      <!--
name:set
mandatory:false
-->
      <param key="set" value="?"/>
      <!--
default:false
name:replace
range:[true/false]
mandatory:false
-->
      <param key="replace" value="false"/>
      <!--
name:mysql.params
description:mysql driver params
range:params1|params2|...
mandatory:false
-->
      <param key="mysql.params" value="?"/>
      <!--
default:1
name:concurrency
description:concurrency of the job
range:1-100
mandatory:false
-->
      <param key="concurrency" value="1"/>
    </writer>
  </job>
</jobs>

用户需要根据注释修改这个xml文件,使之可以正确连接数据库,并导入导出指定的数据。文件的执行见第四步。


PS:

开头的流程图里已经说过了,该xml文件是根据ParamKey.java生成的,因此当ParamKey.java变化时,要及时拷贝一份到相应插件目录下。

hdfs的RCF文件是压缩文件,无法读取,解决办法是将RCF文件改为text


四、执行任务
修改Engine.java的main函数,主要是为了比较方便的使用第三步生成的任务文件。修改后仍然Run as->Java Application

//		String jobDescFile = null;
//		if (args.length < 1) {
//			System.exit(JobConfGenDriver.produceXmlConf());
//		} else if (args.length == 1) {
//			jobDescFile = args[0];
//		} else {
//			System.out.printf("Usage: java -jar engine.jar job.xml .");
//			System.exit(ExitStatus.FAILED.value());
//		}
		String jobDescFile = "D:\\dlc\\workspaces-datax\\dataexchange\\jobs\\mysqlreader_to_rediswriter_1472175472641.xml";
任务的执行在Engine.start函数中

public int start(JobConf jobConf) throws Exception {
		...
<span style="white-space:pre">		</span>// 初始化reader线程池
		NamedThreadPoolExecutor readerPool = initReaderPool(jobConf,
				storagePool);
<span style="white-space:pre">		</span>// 初始化writer线程池
		List<NamedThreadPoolExecutor> writerPool = initWriterPool(jobConf,
				storagePool);
<span style="white-space:pre">		</span>...
<span style="white-space:pre">		</span>// 生成报告


初始化线程池的过程如下:

(1)读取任务文件获得一个PluginParam对象,而后经过任务分割器(一般在相应插件的目录下,实现了Splitter接口的类)将一个PluginParam分隔为多个。切分条件一般是任务文件中配置的多个表,多个表之间通常有固定的分隔符或解析规则,然后每个表又成为独立的任务。

(2)根据任务文件中配置的concurrency参数,以及第(1)步中实际切分得到的job数量,初始化线程池的大小(选择两个值中较小的那个)

这里要注意concurrency参数,这个并不是简单的描述线程数量,所以不要指望通过增加这个数值来提高导入导出速度。e.g. 如果插件没有提供Splitter,就算任务文件中指定了多个表,也还是单线程的,因为没有任务分割器去做分割的操作。


顺便提一下,limit参数也不是限制导入导出的数据行数的!

(3)执行任务。任务执行流程由ReaderWorker.java和WriterWorker.java的run方法描述。举个栗子

/**
	 * Read data, main execute logic code of {@link Reader} <br>
	 * NOTE: When catches exception, {@link ReaderWorker} exit process immediately.
	 * 
	 * */
	@Override
	public void run() {
		try {
			int iRetcode = (Integer) init.invoke(myObject, new Object[] {});
			if (iRetcode != 0) {
				logger.error("Reader initialize failed .");
				System.exit(ExitStatus.FAILED.value());
				return;
			}
			iRetcode = (Integer) connect.invoke(myObject,
					new Object[] {});
			if (iRetcode != 0) {
				logger.error("Reader connect to DB failed .");
				System.exit(ExitStatus.FAILED.value());
				return;
			}
			iRetcode = (Integer) startRead.invoke(myObject,
					new Object[] { this.sender });
			if (iRetcode != 0) {
				logger.error("Reader startRead failed .");
				System.exit(ExitStatus.FAILED.value());
				return;
			}
			iRetcode = (Integer) finish.invoke(myObject, new Object[] {});
			if (iRetcode != 0) {
				logger.error("Reader finish loading failed .");
				System.exit(ExitStatus.FAILED.value());
				return;
			}
		}  catch (Exception e) {
			logger.error(ExceptionTracker.trace(e));
			System.exit(ExitStatus.FAILED.value());
		}
	}
其中myObject是reader对象,init\connect\startRead\finish是Reader.java中定义的抽象方法。所以这几个方法会被依次调用。

writer是类似的。主要的一个区别是:writer在任务文件(xml)中可以是一个list,即可以配置多个writer,每一个最终都是一个单独的线程。而reader在任务文件中只有一个。


如果用户想实现自己的插件,那么继承Reader.java或Writer.java,并实现这几个抽象方法就可以了


五、其他

(1)前面讲的是代码调试,真正的任务文件是这样执行的

①cd到bin目录,该目录下有一个datax.py,是一个python文件

cd   /xxx/datax/bin

②执行

./datax.py /xxx/datax/jobs/hdfsreader_to_rediswriter.xml

(2)安装教程中使用了rpmbuilder以及ant,其实就是把源码打包、拷贝到指定目录。如果不想写spec文件,手动打包拷贝文件也是可以的

(3)dataX不支持hadoop2,这是需要更新dataX的libs目录下的hadoop依赖。这个过程可能会引入更多的依赖,因此会有大量的ClassNotFound、NoClassDefound异常,引入对应的依赖就可以了。我修改后的依赖如下(只做参考):



(4)dataX使用log4j记录日志,保存于dataX/logs目录下




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值