1 个不稳定版本

0.7.0 2022 年 8 月 12 日
0.6.5 2020 年 7 月 18 日

#862 in 编码

Apache-2.0

305KB
6.5K SLoC

mz-avro

在 Rust 中操作 Apache Avro 的库。

此文档正在建设中。

许可证

本项目采用 Apache 许可证,版本 2.0。请注意,这不是由 Apache Avro 维护的官方项目。

贡献

鼓励每个人做出贡献!您可以通过在 GitHub 仓库上分叉并提交拉取请求或打开问题来做出贡献。所有贡献都将采用 Apache 许可证,版本 2.0 进行许可。


lib.rs:

avro

Apache Avro 是一种数据序列化系统,它提供了丰富的数据结构和紧凑、快速的二进制数据格式。

Avro 中的所有数据都是模式化的,如下例所示

{
    "type": "record",
    "name": "test",
    "fields": [
        {"name": "a", "type": "long", "default": 42},
        {"name": "b", "type": "string"}
    ]
}

在 Rust 中处理 Avro 数据的基本有两种方式

  • 基于 Avro 模式作为 Avro 特化的数据类型
  • 作为通用的 Rust 类型,具有自定义序列化逻辑实现 AvroDecode(目前仅支持反序列化,不支持序列化)。

avro 提供了一种轻松有效地读取和写入这两种数据表示的方法。

安装库

添加到您的 Cargo.toml

[dependencies]
avro = "x.y"

或者如果您想利用 Snappy 编码器

[dependencies.avro]
version = "x.y"
features = ["snappy"]

定义一个模式

Avro 数据不能没有 Avro 模式。模式在写入和读取时都 必须 使用,并且它们携带有关我们处理的数据类型的有关信息。Avro 模式用于模式验证和解析 Avro 数据。

Avro 模式以 JSON 格式定义,并可以从原始字符串中解析出来

use mz_avro::Schema;

let raw_schema = r#"
    {
        "type": "record",
        "name": "test",
        "fields": [
            {"name": "a", "type": "long", "default": 42},
            {"name": "b", "type": "string"}
        ]
    }
"#;

// if the schema is not valid, this function will return an error
let schema: Schema = raw_schema.parse().unwrap();

// schemas can be printed for debugging
println!("{:?}", schema);

有关模式和您可以在其中封装的信息的更多信息,请参阅Avro 规范的相关部分。

写入数据

一旦我们定义了模式,我们就可以将数据序列化为 Avro,并在过程中对其进行验证。

注意: 该库还提供了一个低级接口,用于在不生成标记和头信息的情况下对单个数据进行Avro字节码编码(适用于高级用途),但我们强烈建议使用Writer接口以实现完全的Avro兼容性。如果您感兴趣,请阅读API参考。

鉴于我们上面定义的模式是一个Avro 记录,我们将使用库提供的关联类型来指定要序列化的数据。

use mz_avro::types::Record;
use mz_avro::Writer;
#
// a writer needs a schema and something to write to
let mut writer = Writer::new(schema.clone(), Vec::new());

// the Record type models our Record schema
let mut record = Record::new(schema.top_node()).unwrap();
record.put("a", 27i64);
record.put("b", "foo");

// schema validation happens here
writer.append(record).unwrap();

// flushing makes sure that all data gets encoded
writer.flush().unwrap();

// this is how to get back the resulting avro bytecode
let encoded = writer.into_inner();

大多数情况下,模式倾向于将记录定义为一个顶层容器,封装所有要转换的字段并为其提供文档,但如果我们要直接定义一个Avro值,库通过Value接口提供了这种能力。

use mz_avro::types::Value;

let mut value = Value::String("foo".to_string());

使用编解码器压缩数据

Avro在编码数据时支持三种不同的压缩编解码器

  • Null:不压缩数据;
  • Deflate:使用RFC 1951中指定的deflate算法写入数据块,通常使用zlib库实现。注意,此格式(与RFC 1950中的“zlib格式”不同)没有校验和。
  • Snappy:使用Google的Snappy压缩库。每个压缩块后面跟随块中未压缩数据的4字节大端CRC32校验和。您必须启用snappy功能才能使用此编解码器。

要指定用于压缩数据的编解码器,只需在创建Writer时指定即可。

use mz_avro::Writer;
use mz_avro::Codec;
#
let mut writer = Writer::with_codec(schema, Vec::new(), Codec::Deflate);

读取数据

就读取Avro编码数据而言,我们可以直接使用与数据一起编码的模式来读取它们。库将自动为我们完成,就像它已经为我们完成了压缩编解码器一样。


use mz_avro::Reader;
#
// reader creation can fail in case the input to read from is not Avro-compatible or malformed
let reader = Reader::new(&input[..]).unwrap();

如果,相反,我们想指定一个与数据写入时使用的模式不同(但兼容)的读取器模式,我们可以按以下方式操作。

use mz_avro::Schema;
use mz_avro::Reader;
#

let reader_raw_schema = r#"
    {
        "type": "record",
        "name": "test",
        "fields": [
            {"name": "a", "type": "long", "default": 42},
            {"name": "b", "type": "string"},
            {"name": "c", "type": "long", "default": 43}
        ]
    }
"#;

let reader_schema: Schema = reader_raw_schema.parse().unwrap();

// reader creation can fail in case the input to read from is not Avro-compatible or malformed
let reader = Reader::with_schema(&reader_schema, &input[..]).unwrap();

在读取数据时,库还将自动执行模式解析。

有关模式兼容性和解析的更多信息,请参阅Avro规范

处理Rust中的Avro数据反序列化有两种方法,如下所示。

注意: 该库还提供了一个低级接口,用于在不生成标记和头信息的情况下对单个数据进行Avro字节码解码(适用于高级用途),但我们强烈建议使用Reader接口以利用所有Avro功能。如果您感兴趣,请阅读API参考。

Avro的方式

我们可以直接从Reader迭代器中读取Value实例。

use mz_avro::Reader;
#
let mut reader = Reader::new(&input[..]).unwrap();

// value is a Result of an Avro Value in case the read operation fails
for value in reader {
    println!("{:?}", value.unwrap());
}

自定义反序列化(高级)

通过为将决定如何解码各种模式片段的一个或多个结构体实现AvroDecode,可以避免解码到Value的中间阶段。

此API正在变动,更完整的文档即将推出。目前,Materialize提供了一个最完整的示例。

依赖项

~5.5–8MB
~145K SLoC