Datax插件二次开发之HdfsWriter支持parquet
Date: December 24, 2021
1. 背景
目前,公司的OLAP和AD-HOC组件主要使用impala,而当前我们的impala版本支持parquet\textfile格式,却不支持ORC格式,因此会有同步数据时,进行parquet格式写入的需求。
在网上查了下资料,只找到一个支持parquet的hdfswriter插件,但是有网友乐(tian)于(bu)分(zhi)享(chi)的贴了一个版本,没有源码不说,下载下来还各种坑(只支持单表,不支持分区表),实则是明里埋坑暗里收钱,让人无语。
因此,决定自己开发个插件,开源出来,供大家一起使用。
2. 操作步骤
2.1 代码开发
通过拉取datax源码,对hdfswriter模块进行修改,主要修改HdfsHelper.java和HdfsWriter.java 两个类文件,模仿模块中orc的代码块,结合parquet API 进行开发,主要是在HdfsHelper.java中添加parquetFileStartWrite 方法,具体代码如下:
/**
* 写parquetfile类型文件
* @param lineReceiver
* @param config
* @param fileName
* @param taskPluginCollector
*/
public void parquetFileStartWrite(RecordReceiver lineReceiver, Configuration config, String fileName,
TaskPluginCollector taskPluginCollector){
List<Configuration> columns = config.getListConfiguration(Key.COLUMN);
String compress = config.getString(Key.COMPRESS, null);
List<String> columnNames = getColumnNames(columns);
List<ObjectInspector> columnTypeInspectors = getColumnTypeInspectors(columns);
StructObjectInspector inspector = (StructObjectInspector)ObjectInspectorFactory
.getStandardStructObjectInspector(columnNames, columnTypeInspectors);
ParquetHiveSerDe parquetHiveSerDe = new ParquetHiveSerDe ();
MapredParquetOutputFormat outFormat = new MapredParquetOutputFormat();
if(!"NONE".equalsIgnoreCase(compress) && null != compress ) {
Class<? extends CompressionCodec> codecClass = getCompressCodec(compress);
if (null != codecClass) {
outFormat.setOutputCompressorClass(conf, codecClass);
}
}
try {
Properties colProp= new Properties();
colProp.setProperty("columns",String.join(",",columnNames));
List<String> colTypes = new ArrayList<>();
columns.forEach(col ->colTypes.add(col.getString(Key.TYPE)));
colProp.setProperty("columns.types",String.join(",",colTypes));
RecordWriter writer = (RecordWriter) outFormat.getHiveRecordWriter(conf,new Path(fileName), ObjectWritable.class,true,colProp,Reporter.NULL);
Record record = null;
while ((record = lineReceiver.getFromReader()) != null) {
MutablePair<List<Object>, Boolean> transportResult = transportOneRecord(record,columns,taskPluginCollector);
if (!transportResult.getRight()) {
writer.write(null, parquetHiveSerDe.serialize(transportResult.getLeft(), inspector));
}
}
writer.close(Reporter.NULL);
} catch (Exception e) {
String message = String.format("写文件文件[%s]时发生IO异常,请检查您的网络是否正常!", fileName);
LOG.error(message);
Path path = new Path(fileName);
deleteDir(path.getParent());
throw DataXException.asDataXException(HdfsWriterErrorCode.Write_FILE_IO_ERROR, e);
}
}
2.2 相关说明
虽然在hdfswriter模块中加入 parquet 代码块中后,能够在hdfs中进行parquet格式写入。但是测试的时候,却发现原来支持的ORC格式写入却发生异常了,几经debug,也没发现问题出在哪里,因此怀疑问题出现在包的版本上。
由于DATAX的hdfswriter中hive和hadoop版本为:
<properties>
<hive.version>1.1.1</hive.version>
<hadoop.version>2.7.1</hadoop.version>
</properties>
需要适配我们的CDH集群,在引入parquet时,我们更改了DATAX的hdfswriter中hive和hadoop版本:
<properties>
<hive.version>1.1.0-cdh5.12.1</hive.version>
<hadoop.version>2.6.0-cdh5.12.1</hadoop.version>
<!--<hive.version>1.1.1</hive.version>-->
<!--<hadoop.version>2.7.1</hadoop.version>-->
</properties>
所以,目测当时阿里开发人员在做文件类型支持时,肯定也是考虑过parquet了的,但是确实存在版本兼容问题,因此舍弃了parquet文件格式,而选择了新型的ORC。大家在使用时,如果出现版本问题,请调整hive和hadoop版本进行重新编译。
那么,我选新建插件模块,来绕开这个问题,于是新建了一个模块: hdfsparquetwriter 用来在支持parquet写入,具体代码仓库为:https://gitee.com/jackielee4cn/DataX.git 。
欢迎进行指正和Star 。
3.使用样例
3.1 编译安装
下载源码,编译打包,找到模块文件 的 target/datax/plugin/writer/hdfswriter.zip 文件。将文件解压到datax安装目录的${DATAX_HOME}/plugin/writer/ 下 。
3.2 配置datax job
配置方式与官网的orc根式hdfswriter方式一致,只是这里的fileType,只支持text、csv、rcfile、parquet ,不支持ORC (orc相关方法被注释掉了 ) ,并且wtier.name为【hdfsparquetwriter】,相当于是hdfswriter插件的一个补丁插件。
具体样例内容如下:
test_hdfswriter_parquet.job
{
"core": {
"transport": {
"channel": {
"speed": {
"record": "100000"
}
}
}
},
"job": {
"setting": {
"speed": {
"channel": 2
},
"errorLimit": {
"record": 0,
"percentage": 0.02
}
},
"content": [
{
"reader": {
"name": "mysqlreader",
"parameter": {
"username": "write_user111",
"password": "aaa@123",
"column": [
"`f_id`",
"`f_order_id`",
"`f_is_refund`",
"`f_amount`",
"`f_time`"
],
"splitPk": "f_id",
"where": "f_time<='2021-12-01 01:00:00' ",
"connection": [
{
"table": [
"test_datax_parquet"
],
"jdbcUrl": [
"jdbc:mysql://test02:3306/test?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&tinyInt1isBit=false&dontTrackOpenResources=true"
]
}
]
}
},
"writer": {
"name": "hdfsparquetwriter",
"parameter": {
"defaultFS": "hdfs://test01:8020",
"fileType": "parquet",
"path": "/user/hive/warehouse/test.db/test_datax_parquet/date_id=20211201",
"fileName": "test_datax_text",
"writeMode": "append",
"fieldDelimiter": "\u0001",
"column": [
{
"name": "f_id",
"type": "bigint"
},
{
"name": "f_order_id",
"type": "string"
},
{
"name": "f_is_refund",
"type": "int"
},
{
"name": "f_amount",
"type": "double"
},
{
"name": "f_time",
"type": "string"
}
]
}
}
}
]
}
}
3.3 执行job
python ${DATAX_HOME}/bin/datax.py test_hdfswriter_parquet.job
查看控制台日志,执行正常。