如何在不发送架构或使用注册表的情况下使用Akka Persistence改进Avro架构?

2018-02-03 scala serialization avro akka-persistence

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

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

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

另外,为模式的每个版本提供一个序列化器/反序列化类的正确方法是否正确,因此它知道如何将每个版本与最新版本进行转换?

最后,是否有关于如何对模式演化进行单元测试的建议?例如,将一条消息存储在akka-persistence中,然后实际更改case类的定义,然后杀死actor并确保其正确演化。 (我看不到如何在运行时更改case类的定义。)

Answers

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

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

Akka本身提供了Serializer特征或SerializerWithStringManifest特征,它们将在您序列化的对象中自动包含“模式标识符”的字段。 因此,当您创建自定义序列化程序时,您可以扩展适当的特征,定义模式标识符并使​​用binary输出流。结合使用这些技术后,您将成功使用无模式序列化,同时包含模式标识符。

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

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

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

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

我开发了一个原型,演示了几个答案,并且可以在github上找到 。它使用avro,avro4s和akka持久性。对于此示例,我通过在提交之间进行实际更改来演示了更改的代码库-您将检出提交#1,运行代码,然后移至提交#2,依此类推。它针对cassandra运行,因此将演示需要重播的事件无需使用外部架构注册表即可使用新架构进行开发。

Related