apache beam入门之编码Coder相关

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

为什么需要Coder

apache beam在做计算时, 会对数据进行序列化和反序列化,以方便进行分布式节点间的传输。
因此任何PCollection都会指定1个Coder编码,来确定当前数据集里的数据类型,以便在计算时进行正确的序列化操作。

当我们通过apply组装好1个PCollection后,其coder也就需要被指定了。

beam默认识别编码

正常情况下,beam能够自动识别编码,如下例子,我们生成1个字符串的数据集,并转成整数:

// 生成初始的输入数据
// 相当于往管道里塞入了3个自己写的字符串元素
PCollection<String> pcStart = pipeline.apply(
    Create.of(
            "45654",
            "213",
            "2233"));

System.out.println("pcStart coder=" + pcStart.getCoder());

// 字符串转整数
PCollection<Integer> pcMid = pcStart.apply(MapElements.via(new SimpleFunction<String, Integer>() {
    @Override
    public Integer apply(String str) {
        return Integer.valueOf(str);
    }
}));

System.out.println("pcMid coder=" + pcMid.getCoder());

控制台打印:
在这里插入图片描述

各类型对应的编码

java类型编码
DoubleDoubleCoder
InstantInstantCoder
IntegerVarIntCoder
IterableIterableCoder
KVKvCoder
ListListCoder
MapMapCoder
LongVarLongCoder
StringStringUtf8Coder
TableRowTableRowJsonCoder
VoidVoidCoder
byteByteArrayCoder
TimestampedValueTimestampedValueCoder

需要手动setCoder的时机

我们也可以在PCollection进行apply之后, 进行手动setCoder, 有2种情况需要手动setCoder:

  1. 希望使用带有特殊处理的序列化反序列化编码(例如为了安全考虑做加密)
  2. beam无法识别当前编码的情况
     

第2种情况我遇到的比较多, 大部分是因为实际编码只能在运行期去获知的情况,举个例子:
我生成2个Row元素,可以理解为数据库里表的某一行

// 生成2个数据行元素, 第一个字段是整数,第二个字段是字符串
Schema schema = Schema.builder().addInt32Field("id").addStringField("name").build();
Row row1 = Row.withSchema(schema).addValue(1).addValue("tom").build();
Row row2 = Row.withSchema(schema).addValue(2).addValue("jack").build();

PCollection<Row> pcStart = pipeline.apply(Create.of(row1, row2));

接着我们要筛选出某个字段进行打印,筛选DoFn类如下:

    static class SelectColumnFn extends SimpleFunction<Row, Object> {
        private int index; // 字段序号

        public SelectColumnFn(int index) {
            this.index = index;
        }

        @Override
        public Object apply(Row row) {
            return row.getValue(index);
        }
    }

其中index来自于程序输入

// 获取需要打印的字段索引
int index = Integer.valueOf(args[0]);
PCollection pcMid = pcStart.apply(MapElements.via(new SelectColumnFn(index)));

// 输出到控制台
pcMid.apply(ParDo.of(new PrintStrFn()));

// 运行结果
pipeline.run().waitUntilFinish();

我们给输入参数置为0, 希望打印id字段,然后运行时却报错了
在这里插入图片描述

从提示可以看到beam此时不知道用什么编码了,需要我们来指定
因此我们可以加上一段setCoder的代码:

// 获取需要打印的字段索引
int index = Integer.valueOf(args[0]);
PCollection pcMid = pcStart.apply(MapElements.via(new SelectColumnFn(index)));

// 根据索引,设置不同的编码
if (index == 0) {
    pcMid.setCoder(BigEndianIntegerCoder.of());
}
else if(index == 1) {
    pcMid.setCoder(StringUtf8Coder.of());
}

// 输出到控制台
pcMid.apply(ParDo.of(new PrintStrFn()));

// 运行结果
pipeline.run().waitUntilFinish();

运行正常:
在这里插入图片描述

如何创建自定义编码

如果我们需要1种自己特有的编码方式,或者需要用到一些自定义类,那么就要进行自定义编码。
例如我们要对个人信息对象进行计算,但是在分布式传输中希望一些信息能加密,则我们就需要进行自定义编码
个人信息定义如下:

public class PersonInfo{
    String name;
    int age;
    String idCardNumber; // 身份证号码

    public PersonInfo(String name, int age, String idCardNumber) {
        this.name = name;
        this.age = age;
        this.idCardNumber = idCardNumber;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public String getIdCardNumber() {
        return idCardNumber;
    }
}

接着定义个人信息编码类,在里面指定encode和decode的方式即可。

public class PersonInfoCoder extends StructuredCoder<PersonInfo> {
    private final Coder<String> nameCoder = StringUtf8Coder.of();
    private final Coder<Integer> ageCoder = BigEndianIntegerCoder.of();
    private final Coder<String>  idCardNumberCoder = StringUtf8Coder.of();

    // 静态创建方法,与beam默认编码保持相同的创建方式
    public static Coder<PersonInfo> of() {
        return INSTANCE;
    }

    // 编码操作
    @Override
    public void encode(PersonInfo value, OutputStream outStream) throws CoderException, IOException {
        // 把名字按照原定编码输入到outStream
        this.nameCoder.encode(value.getName(), outStream);
        // 把年龄按照原定编码输入到outStream
        this.ageCoder.encode(value.getAge(), outStream);

        String idCard = value.getIdCardNumber();
        // 加密后再进行编码
        this.idCardNumberCoder.encode(encrypt(idCard), outStream);
    }

    // 解码操作
    @Override
    public PersonInfo decode(InputStream inStream) throws CoderException, IOException {
        String name = this.nameCoder.decode(inStream);
        int age = this.ageCoder.decode(inStream);
        // 解码后,要进行解密
        String idCard = decrypt(this.idCardNumberCoder.decode(inStream));

        return new PersonInfo(name, age, idCard);
    }

    // 获取编码列表
    @Override
    public List<? extends Coder<?>> getCoderArguments() {
        return Arrays.asList(this.nameCoder, this.ageCoder, this.idCardNumberCoder);
    }

    // 识别编码是否已被指定,有一些场景下编码可能是运行期指定的。
    @Override
    public void verifyDeterministic() throws NonDeterministicException {
        verifyDeterministic(this, "idCard coder id need be string", this.idCardNumberCoder);
    }
}

如何指定自定义编码

完成以上操作后, 通过pcollection.setCoder(coder)去给对应的PCollection指定对应的编码即可。
也可以通过注解

@DefaultCoder(PersonInfoCoder.class)
public class PersonInfo {
  ...
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值