Datax插件二次开发之HdfsReader支持parquet

Datax插件二次开发之HdfsReader支持parquet

Date: December 17, 2021

1. 背景

在工作中,数据仓库主要使用parquet格式作为数据存储,有些场景中,需要使用datax进行数据出仓。但是,目前Alibaba Datax 的HdfsReader插件并不支持Parquet格式。在网上也查了不少博客和资料,并没有看到相关的插件开源,因此决定自己开发。

2. 操作步骤

2.1 代码开发

从alibaba Datax官网拉取代码,并新建分支,对hdfsreader模块进行调整,增加对parquet文件读取的相关代码块。主要修改点在下边两个类中:

在这里插入图片描述

根据ORC读取方式的代码,结合parquet的文件格式以及parquet API 进行改造,主要在DFSUtil.java 中增加parquetFileStartRead方法,代码如下,请参考:

public void parquetFileStartRead(String sourceParquetFilePath, Configuration readerSliceConfig,
                                     RecordSender recordSender, TaskPluginCollector taskPluginCollector) {
        LOG.info(String.format("Start Read parquetfile [%s].", sourceParquetFilePath));
        List<ColumnEntry> column = UnstructuredStorageReaderUtil
                .getListColumnEntry(readerSliceConfig, com.alibaba.datax.plugin.unstructuredstorage.reader.Key.COLUMN);
        String nullFormat = readerSliceConfig.getString(com.alibaba.datax.plugin.unstructuredstorage.reader.Key.NULL_FORMAT);
        StringBuilder allColumns = new StringBuilder();
        StringBuilder allColumnTypes = new StringBuilder();
        boolean isReadAllColumns = false;
        int columnIndexMax = -1;
        // 判断是否读取所有列
        if (null == column || column.size() == 0) {
            int allColumnsCount = getParquetAllColumnsCount(sourceParquetFilePath);
            columnIndexMax = allColumnsCount - 1;
            isReadAllColumns = true;
        } else {
            columnIndexMax = getMaxIndex(column);
        }
        for (int i = 0; i <= columnIndexMax; i++) {
            allColumns.append("col");
            allColumnTypes.append("string");
            if (i != columnIndexMax) {
                allColumns.append(",");
                allColumnTypes.append(":");
            }
        }
        if (columnIndexMax >= 0) {
            JobConf conf = new JobConf(hadoopConf);
            Path parquetFilePath = new Path(sourceParquetFilePath);
            Properties p = new Properties();
            p.setProperty("columns", allColumns.toString());
            p.setProperty("columns.types", allColumnTypes.toString());

            try {

                //方式1:不采用。原因:使用ParquetHiveSerDe 读取时,会将parquet中的String类型字段读取成BytesWritable,但难以转换为String
//                ParquetHiveSerDe serde = new ParquetHiveSerDe();
//                serde.initialize(conf, p);
//                StructObjectInspector inspector = (StructObjectInspector) serde.getObjectInspector();
//                InputFormat<?, ?> in = new MapredParquetInputFormat();
//                FileInputFormat.setInputPaths(conf, parquetFilePath.toString());
//
//                //If the network disconnected, will retry 45 times, each time the retry interval for 20 seconds
//                //Each file as a split
//                //TODO multy threads
//                InputSplit[] splits = in.getSplits(conf, 1);
//
//                RecordReader reader = in.getRecordReader(splits[0], conf, Reporter.NULL);
//                Object key = reader.createKey();
//                Object value = reader.createValue();
//                // 获取列信息
//                List<? extends StructField> fields = inspector.getAllStructFieldRefs();
//
//                List<Object> recordFields;
//                while (reader.next(key, value)) {
//                    recordFields = new ArrayList<Object>();
//
//                    for (int i = 0; i <= columnIndexMax; i++) {
//                        Object field = inspector.getStructFieldData(value, fields.get(i));
//                        recordFields.add(field);
//                    }
//                    transportOneRecord(column, recordFields, recordSender,
//                            taskPluginCollector, isReadAllColumns, nullFormat);
//                }

                //方式2:采用。使用Parquet的javaAPI进行读取
                GroupReadSupport support = new GroupReadSupport();
                ParquetReader<Group> reader = ParquetReader.builder(support, parquetFilePath).build();
                Group line = null ;
                List<Object> recordFields;
                while((line = reader.read()) != null ){
                    recordFields = new ArrayList<Object>();
                    //从line中获取每个字段
                    for (int i = 0; i <= columnIndexMax; i++) {
                        Object field = line.getValueToString(i, 0);
                        recordFields.add(field);
                    }
                    transportOneRecord(column, recordFields, recordSender,
                            taskPluginCollector, isReadAllColumns, nullFormat);
                }
                reader.close();
            } catch (Exception e) {
                String message = String.format("从parquetfile文件路径[%s]中读取数据发生异常,请联系系统管理员。"
                        , sourceParquetFilePath);
                LOG.error(message);
                throw DataXException.asDataXException(HdfsReaderErrorCode.READ_FILE_ERROR, message);
            }
        } else {
            String message = String.format("请确认您所读取的列配置正确!columnIndexMax 小于0,column:%s", JSON.toJSONString(column));
            throw DataXException.asDataXException(HdfsReaderErrorCode.BAD_CONFIG_VALUE, message);
        }
    }

附上本人的gitee代码仓库: https://gitee.com/jackielee4cn/DataX

欢迎指正或Start。

3.使用样例

3.1 编译安装

下载源码,编译打包,找到模块文件 的 target/datax/plugin/reader/hdfsreader.zip 文件。将文件解压到datax安装目录的${DATAX_HOME}/plugin/reader/ 下 。注意提前备份原有默认的hdfsreader插件,以免出现问题时,进行回滚。

3.2 配置datax job

配置方式与官网的orc根式hdfsreader方式一致,只是这里的fileType除了可以使用text、orc、csv等格式外,还可以用 parquet 。具体样例内容如下:

test_hdfsreader_parquet.job

{
  "job": {
    "setting": {
      "speed": {
        "channel": 3,
        "byte": 10485760
      },
      "errorLimit": {
        "record": 0,
        "percentage": 0.02
      }
    },
    "content": [
      {
        "reader": {
          "name": "hdfsreader",
          "parameter": {
            "path": "/user/hive/warehouse/test.db/test_datax_parquet/date_id=20211201",
            "defaultFS": "hdfs://test01:8020",
            "fileType": "parquet",
            "skipHeader": false,
            "column": [
              {
                "index": "0",
                "type": "long"
              },
              {
                "index": "1",
                "type": "string"
              },
              {
                "index": "2",
                "type": "long"
              },
              {
                "index": "3",
                "type": "double"
              },
              {
                "index": "4",
                "type": "string"
              }
            ]
          }
        },
        "writer": {
          "name": "mysqlwriter",
          "parameter": {
	    "writeMode": "replace",
            "username": "write_user",
            "password": "Writeuser@123",
            "column": [
				"`f_id`",
				"`f_order_id`",
				"`f_is_refund`",
				"`f_amount`",
				"`f_time`"
            ],
            "connection": [
              {
                "table": [
                  "test_datax_parquet"
                ],
                "jdbcUrl": "jdbc:mysql://test02:3306/test?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&tinyInt1isBit=false&dontTrackOpenResources=true"
              }
            ]
          }
        }
      }
    ]
  }
}

3.3 执行job

python ${DATAX_HOME}/bin/datax.py test_hdfsreader_parquet.job

查看控制台日志,执行正常。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
DataX是一个用于数据同步的开源工具,它提供了丰富的插件支持不同的数据源和目标。根据引用[2],DataX插件的开发模式是基于Record的抽象,各个插件只需要按照规范进行开发即可。引用[3]中提到,DataX的打包成功后的包结构中包含了插件目录。 对于Elasticsearch读插件二次开发,你可以参考DataX插件开发规范和文档。首先,你需要了解Elasticsearch的数据结构和API,以便在插件中进行数据读取操作。然后,你可以在DataX插件目录中创建一个新的插件目录,并按照规范进行插件的开发。在插件的配置文件中,你需要指定Elasticsearch的连接信息和查询条件等参数。 在插件的开发过程中,你可以使用DataX提供的各种工具和接口来简化开发和测试。例如,你可以使用DataX的RecordReader接口来读取Elasticsearch中的数据,并将其转换为DataX的Record对象。你还可以使用DataX的各种工具类来处理数据转换和批量写入等操作。 最后,你可以使用DataX的命令行工具来运行你开发的插件,并通过配置文件指定插件的参数和数据源信息。例如,你可以使用类似于引用[1]的命令来运行你的Elasticsearch读插件,并指定数据源的路径和插件的配置文件。 总结起来,要进行DataX的Elasticsearch读插件二次开发,你需要了解Elasticsearch的数据结构和API,按照DataX插件开发规范进行插件的开发,使用DataX的工具和接口简化开发和测试,最后使用DataX的命令行工具来运行你开发的插件

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值