apache beam入门之输入输出SDK调用(批处理)

目录:apache beam 个人使用经验总结目录和入门指导(Java)

众所周知数据处理分批处理和流处理,beam是基于底层计算引擎并支持2者的。 这一章先只介绍批处理的几种数据输入方式,即一次性全部输入全部数据的方式。

内存输入数据

内存输入方式之前介绍过了,可以通过组装Create类进行内存输入

PCollection<String> pcStart = pipeline.apply(
        Create.of(
                "HELLO!",
                "THIS IS BEAM DEMO!",
                "HAPPY STUDY!"));

也可也输入其他数据类型

PCollection<Integer> pcStartInt = pipeline.apply(Create.of(1,2,3,4));

Create.of里的内容可以是T… , 或者是Iterable<T>


本地文本

输入

PCollection<String> pText = 
	pipeline.apply(TextIO.read().from("beam.txt"));

from里是文本文件的路径
读入后,会自动根据换行符进行分割,分割成1个个String类型的数据集元素。
当我读入如下beam.txt文本,并用PrintStrFn进行输出时的情况:
在这里插入图片描述
可以看到确实是根据换行符进行了分割

输出

输出的sdk使用方式如下:

pText.apply(TextIO.write().to("/tmp/beamtest").withSuffix(".txt").withoutSharding());

注意事项:如果是基于分布式计算引擎,使用TextIo读取和输出本地文件很可能会失败,除非文件路径在每一个executor节点都存在。


hdfs文本输入

上面提到如果是在分布式计算引擎上计算,读取本地文件可能会失败,因此分布式读取文本都是基于hdfs文件进行操作的。
hdfs输入方式步骤如下:

1.pom文件增加hdfs依赖包

<dependency>
  <groupId>org.apache.beam</groupId>
  <artifactId>beam-sdks-java-io-hadoop-file-system</artifactId>
  <version>{beam.version}</version>
</dependency>
<dependency>
    <groupId>org.apache.hadoop</groupId>
    <artifactId>hadoop-hdfs</artifactId>
    <version>{hadoop.version}</version>
</dependency>
2.使用的PipelineOption至少要继承自HadoopFileSystemOptions
public interface MyTestOption extends HadoopFileSystemOptions {...

3.创建的option要设置hdfs配置

MyTestOption option = PipelineOptionsFactory.fromArgs(args).withoutStrictParsing().as(MyTestOption.class);
Configuration conf = new Configuration();
// 输入fs.defaultFS(可参考集群的core-site.xml)
conf.set("fs.defaultFS", "hdfs://hacluster");
// 输入文件系统执行引擎
conf.set("fs.hdfs.impl", "org.apache.hadoop.hdfs.DistributedFileSystem");
List<Configuration> list = new ArrayList<Configuration>();
list.add(conf);
option.setHdfsConfiguration(list);

4.调用TextIo

PCollection<String> pText = 
	pipeline.apply(TextIO.read().from("hdfs://hacluster/user/beam.txt"));

hdfs文本输出sdk使用方式同本地文本输出。


jdbc输入

输入

简单的jdbc输入可使用JdbcIO这个sdk, 首先得引入org.apache.beam的beam-sdks-java-io-jdbc这个包。
然后输入如下代码:

PCollection<String> pReadColumnFirst = pipeline.apply(
        JdbcIO.<String>read()
                // 组装数据连接信息
                .withDataSourceConfiguration(configuration)
                // 组装查询语句
                .withQuery("select name from worker")
                // 如何把resultSet转成需要的数据集元素
                .withRowMapper(new JdbcRowMapper())
                // 元素需要设定正确编码,否则可能无法识别。
                .withCoder(StringUtf8Coder.of())
);

configuration的生成方式如下:

JdbcIO.DataSourceConfiguration configuration
        = JdbcIO.DataSourceConfiguration
        .create(dbDriver, url)
        .withUsername(userName)
        .withPassword(password);

JdbcRowMapper的定义如下,需要实现JdbcIo.RowMapper接口:

static class JdbcRowMapper implements JdbcIO.RowMapper<String> {
    // 如何将Jdbc的ResultSet转成需要的元素
    @Override
    public String mapRow(ResultSet resultSet) throws Exception {
        return resultSet.getString(1);
    }
}

对于编码的选取,可以见apache beam入门之编码Coder相关

***注意事项: 该jdbcIO仅支持读少量数据,如果要读入超过jvm限制的数据,则会出现omm报错

输出

输出要用preStatement的方式做插入或更新操作

        pReadColumnFirst.apply(
                JdbcIO.<String>write()
                        .withDataSourceConfiguration(configuration)
                        .withStatement("update worker set level = 10 where name = ?")
                        .withPreparedStatementSetter(new JdbcPreStatementSetter()));

JdbcPreStatementSetter的实现如下,就是定义了如何把数据集中的元素set到Statement中

    static class JdbcPreStatementSetter implements JdbcIO.PreparedStatementSetter<String> {
        @Override
        public void setParameters(String element, PreparedStatement preparedStatement) throws Exception {
            preparedStatement.setObject(1, element);
        }
    }

自定义输入方式

如果beam自带的sdk无法满足需求(例如希望加入一些安全认证或者异常操作等),则可以用自定义的输入方式。
自定义输入需要先定义一个新的DoFn类,输入类型定义为Void,如下所示:

    static class SafeReadFn extends DoFn<Void, String> {
        @ProcessElement
        public void processElement(ProcessContext context) {
            // 可运行业务需要所的安全认证
            safeLogin();
            // 用自己定义的方式读取数据
            String readElement = readData(context);
            // 输出数据
            context.output(readElement);
        }
    }

注意这个执行类中,不需要取输入元素,因为输入元素Void一般都是null,不需要取。
然后先通过Create.of()建立1个空的Void元素,然后在处理这个元素时, 进行自定义输入即可。

PCollection<String> pReadData
        = pipeline.apply(Create.<Void>of(null))
        .apply(ParDo.of(new SafeReadFn()));

输出同理,如果是自定义输出(即后面不再有计算操作), 定义1个DoFn<类型, Void>即可

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章