尽管 Debezium 可以轻松捕获数据库更改并将其记录在 Kafka 中,但您必须做出的更重要的决定之一是如何在 Kafka 中序列化这些更改事件。Kafka 中的每个消息都有一个键和一个值,对于 Kafka 来说,它们是不透明的字节数组。但是,当您设置 Kafka Connect 时,您必须说明 Debezium 事件的键和值应如何序列化为二进制形式,并且您的消费者也必须将它们反序列化回可用的形式。
Debezium 事件的键和值都是结构化的,因此 JSON 当然是一个合理的选择——它灵活、无处不在且与语言无关,但另一方面,它相当冗长。一个替代方案是 Avro,它同样灵活且与语言无关,但速度更快,生成的二进制表示更小。使用 Avro 需要您进行更多的设置工作和一些额外的软件,但其优势通常是值得的。
Kafka 序列化器和反序列化器
在我们继续深入之前,让我们回顾一下 Kafka 生产者和消费者通常如何进行序列化和反序列化。因为键和值是简单的不透明字节数组,所以你可以为键和值使用任何东西。例如,考虑一种情况,我们使用简单的整数作为键,字符串作为值。在这种情况下,这些消息的生产者将使用长整型序列化器将 long 键转换为二进制形式,并使用字符串序列化器将 String 值转换为二进制形式。同时,消费者使用长整型反序列化器将二进制键转换为可用的 long 值,并使用字符串反序列化器将二进制值转换回 String 对象。
在键和/或值需要更结构化的情况下,生产者和消费者可以编写成使用 JSON 结构作为键和/或值,并使用 Kafka 提供的JSON 序列化器和反序列化器来执行与存储在 Kafka 消息中的二进制形式的转换。如前所述,为键和/或值使用 JSON 非常灵活且与语言无关,但它也会产生相对较大的键和值,因为 JSON 值的字段和结构也需要被编码。
Avro 序列化
Avro 是一种数据序列化机制,它使用模式来定义数据的结构。Avro 在将数据写入二进制格式时依赖于此模式,并且该模式允许它以更紧凑的形式对数据中的字段进行编码。Avro 在读取数据时也依赖于模式。但有趣的是,Avro 模式被设计成可以演进的,所以实际上可以使用一个与写入时略有不同的模式来读取。这个特性使得 Avro 成为 Kafka 序列化和反序列化的绝佳选择。
Confluent 提供了一个使用 Avro 的 Kafka 序列化器和反序列化器以及一个单独的Schema Registry(模式注册表),其工作原理如下:当一个数字或字符串对象需要被序列化时,Avro 序列化器将确定给定类型的相应 Avro 模式,注册此模式及其使用的主题到 Schema Registry,获取该模式的唯一标识符,然后将模式的唯一标识符和编码后的值编码为二进制形式。下一条消息很可能具有相同的类型和模式,因此序列化器可以快速编码该消息的模式标识符和值,而无需与 Schema Registry 通信。只有在需要序列化它尚未见过的模式时,Avro 序列化器才会与 Schema Registry 通信。因此,这不仅速度快,而且产生非常紧凑的二进制形式,并允许生产者随着时间的推移演进其键和/或值模式。Schema Registry 还可以配置为仅当新模式版本兼容 Avro 模式演进规则时才允许注册,从而确保生产者不会生成消费者无法读取的消息。
同时,消费者使用Avro 反序列化器,其工作方式类似,尽管是反向的:当它读取键或值的二进制形式时,它首先查找模式标识符,如果之前没有见过,则向 Schema Registry 请求模式,然后使用该模式将剩余的二进制表示解码为其对象形式。同样,如果反序列化器之前见过某个特定的模式标识符,它已经有了解码数据所需的模式,而无需咨询 Schema Registry。
Kafka Connect 转换器
Kafka Connect 与许多 Kafka 生产者/消费者略有不同,因为键和值通常是结构化的。并且,Kafka Connect 没有要求连接器使用 JSON 对象,而是定义了自己的轻量级框架来使用模式定义数据结构,这使得编写处理结构化数据的连接器更加容易。Kafka Connect 定义了自己的转换器,它们类似于 Kafka 的(反)序列化器,不同之处在于 Kafka Connect 的转换器了解这些结构和模式,并可以将键和值序列化为二进制形式。Kafka Connect 提供了一个JSON 转换器,它将结构转换为 JSON,然后使用普通的 Kafka JSON 序列化器,因此下游消费者只需使用普通的 Kafka JSON 反序列化器即可获得 Kafka Connect 结构和模式的 JSON 表示。这正是Debezium 教程所使用的,并且 watch-topic 消费者知道要使用 JSON 反序列化器。
Kafka Connect 的一个出色特性是,连接器只需提供结构化消息,Kafka Connect 就会负责使用配置的转换器进行序列化。这意味着你可以将任何 Kafka Connect转换器与任何 Kafka Connect 连接器一起使用,包括 Debezium 的所有连接器。
Kafka Connect 的模式系统是专门为 Avro 设计的,因此 Kafka Connect 模式与 Avro 模式之间存在一对一的映射。Confluent 为 Kafka Connect 提供了一个Avro 转换器,该转换器将连接器提供的 Kafka Connect 结构序列化为紧凑的 Avro 二进制表示,同样使用 Schema Registry,就像 Avro 序列化器一样。消费者只需使用上面提到的普通 Avro 反序列化器。
对 Debezium 事件使用 Avro 进行序列化带来了几个显著的优势
-
Debezium 事件的编码二进制形式比 JSON 表示明显小。不仅结构化数据以更紧凑的形式进行编码,而且与该结构化数据相关的模式在二进制形式中表示为单个整数。
-
将 Debezium 事件编码为 Avro 二进制形式速度很快。只有当转换器遇到新模式时,它才需要咨询 Schema Registry;否则,该模式已经见过,其编码逻辑也已预先计算好。
-
Kafka Connect 的 Avro 转换器生成的 Avro 编码键值消息可以被任何使用 Avro 反序列化器的 Kafka 消费者读取。
-
Debezium 事件结构基于捕获更改的表的结构。当源表结构发生变化时(例如,因为对其应用了
ALTER语句),事件的结构和模式也会发生变化。如果这样做的方式是新的 Avro 模式兼容旧的 Avro 模式,那么即使事件结构随着时间演变,消费者也能在没有中断的情况下处理这些事件。 -
Avro 的模式机制比自由形式的 JSON 结构更加正式和严谨,并且在比较任何两条消息时,模式的变化都会被清晰地标识出来。
-
Avro 转换器、Avro(反)序列化器和 Schema Registry 都是开源的。
确实,使用 Avro 转换器和反序列化器需要运行一个 Schema Registry,并且该注册表成为你流处理基础设施的组成部分。然而,这与上述好处相比,付出的代价很小。
在 Debezium 中使用 Avro 转换器
如上所述,为了使 Debezium 教程尽可能简单,我们在教程中避免使用 Schema Registry 或 Avro 转换器。我们目前(尚未)在我们的 Docker 镜像中包含 Avro 转换器,尽管很快就会改变。
然而,当你在 Confluent Platform 或你自己的 Kafka Connect 安装中安装 Debezium 连接器时,绝对有可能使用 Kafka Connect 的 Avro 转换器。只需将 Kafka Connect 工作节点配置为对键和值使用 Avro 转换器。
key.converter=io.confluent.connect.avro.AvroConverter
value.converter=io.confluent.connect.avro.AvroConverter 而且,如果你想对 Kafka Connect 内部消息使用 Avro 转换器,那么也要配置这些。
internal.key.converter=io.confluent.connect.avro.AvroConverter
internal.value.converter=io.confluent.connect.avro.AvroConverter 再说一次,无需以任何不同的方式配置 Debezium 连接器。
关于 Debezium
Debezium 是一个开源的分布式平台,可以将现有数据库转变为事件流,使应用程序能够几乎即时地看到并响应数据库中已提交的每个行级更改。Debezium 构建在 Kafka 之上,并提供了 Kafka Connect 兼容的连接器,用于监控特定的数据库管理系统。Debezium 将数据更改的历史记录在 Kafka 日志中,这样您的应用程序可以随时停止和重新启动,并可以轻松地消费在未运行时错过的所有事件,确保所有事件都被正确且完整地处理。Debezium 在 Apache 许可证 2.0 下是 开源 的。