如何在不发送模式或使用注册表的情况下使用 Akka 持久性发展 Avro 模式?



我们正在考虑为基于 scala 的 Akka Persistence 应用程序采用序列化方法。我们认为持久化事件可能会随着时间的推移而"演变",因此我们希望支持架构演变,并首先考虑 Avro。

我们希望避免在每条消息中包含完整的架构。但是,在可预见的未来,此 Akka 持久性应用是唯一将序列化和反序列化这些消息的应用,因此我们认为不需要单独的架构注册表。

检查 avro 和各种 scala 库的文档,我看到了将模式包含在消息中的方法,以及如何通过使用模式注册表"无模式"使用它,但是中间的情况呢?实现无架构但以某种方式包含一个标识符以便能够为反序列化对象查找正确的架构(在本地部署的代码库中可用)的正确方法是什么?我真的只是创建一个表示我的案例类的架构,但为架构版本添加一个额外的"标识符"字段,然后在运行时拥有某种标识符>模式的内存映射吗?

此外,对于架构的每个版本都有一个序列化程序/反序列化类是正确的方法,因此它知道如何将每个版本转换为最新版本/从最新版本转换?

最后,是否有关于如何对架构演变进行单元测试的建议?例如,将消息存储在 akka-persistence 中,然后实际更改 case 类的定义,然后杀死参与者并确保它正确演变。(我不明白如何在运行时更改 case 类的定义。

在花更多时间在这上面之后,以下是我想出的答案。

使用 avro4s,可以使用默认的data输出流将架构包含在每个序列化消息中。或者,您可以使用binary输出流,该流在序列化每条消息时仅省略架构。("二进制"在这里有点用词不当,因为它所做的只是省略模式。无论哪种情况,它仍然是一个Array[Byte]

Akka 本身提供了一个Serializer特征或一个SerializerWithStringManifest特征,它将自动在您序列化的任何对象中包含"模式标识符"字段。 因此,在创建自定义序列化程序时,可以扩展相应的特征、定义架构标识符并使用binary输出流。结合使用这些技术时,将成功使用无架构序列化,同时包含架构标识符。

一种常见的技术是"指纹"您的架构 - 将其视为字符串,然后计算其摘要(MD5、SHA-256 等)。如果构造指纹到架构的内存中映射,则可以用作应用程序的内存中架构注册表。

因此,在反序列化时,传入对象将具有用于序列化它的架构的架构标识符("编写器")。反序列化时,您应该知道用于反序列化它的架构的标识符("读取器")。Avro4s 支持一种使用构建器模式指定两者的方法,因此 avro 可以将对象从旧格式转换为新格式。这就是你支持"模式演变"的方式。由于其工作原理,不需要为每个架构版本使用单独的序列化程序。您的自定义序列化程序将知道如何改进您的对象,因为这是 Avro 免费为您提供的部分。

至于单元测试,最好的选择是探索性测试。实际上,在测试中定义案例类的多个版本,以及其架构的多个随附版本,然后通过编写测试来探索 Avro 的工作原理,这些测试将在该架构的不同版本之间演变对象。

不幸的是,这与你正在编写的代码没有直接关系,因为很难模拟在测试时实际更改你正在测试的代码。

我开发了一个原型来演示其中的几个答案,它可以在github上找到。它使用 avro、avro4s 和 akka 持久性。对于这个,我演示了一个通过跨提交实际更改代码库来更改代码库 - 您将签出提交 #1,运行代码,然后移动到提交 #2,等等。它针对 cassandra 运行,因此它将演示需要使用新模式改进的事件,所有这些都不使用外部模式注册表。

最新更新