avro - Avro union:前向兼容性?
问题描述
我们的系统由多个微服务组成,这些微服务发出和使用以 avro 格式编码的事件(参见底部的架构)。一个特定的用例如下:服务 A 在主题 T1 上发出一个事件(类型为 InvoiceEvents),服务 B 和 C(不同的开发团队)正在从 T1 消费。例如,服务 B 是税务团队的一部分,而服务 C 是产品履行团队的一部分。
我期待以下是真的(但似乎不是):
- 通过添加新的联合类型(即“有效负载”字段的 InvoiceCreated),架构可以从版本 1 (v1) 演变为版本 2 (v2) - 查看底部的示例架构。
- 生产服务 A 升级到 v2(即生产遵循 v2 的事件)
- 一些消费服务(例如服务 C)仍然可以使用 v1,因为它们对新的事件类型(例如 InvoiceCreated)不感兴趣。在这种情况下,“有效负载”字段将在反序列化时使用默认(空)值。
- 最终且仅当出于业务原因需要时,服务 C 才能升级到使用 v2,如果需要对新事件类型(即 InvoiceCreated)做出反应。
但是服务 C 不能反序列化 InvoiceCreated 类型的新事件。具体来说,它是抛出:
org.apache.avro.AvroTypeException: Found com.elsevier.q2c.schema.avro.invoice.InvoiceCreated, expecting unionorg.apache.avro.AvroTypeException: Found com.elsevier.q2c.schema.avro.invoice.InvoiceCreated, expecting union at org.apache.avro.io.ResolvingDecoder.doAction(ResolvingDecoder.java:292) at
avro 联合类型是否不向前兼容(如上所述)?它们是否仅像 Confluent Schema Registry测试所暗示的那样向后兼容。避免微服务耦合的建议方法是什么?我猜不能使用avro unions..
谢谢!!
没有明确答案的相关链接:Avro-union-compatibility-mode-enhancement-proposal
架构 v1:
[
...
{
"type":"record",
"name":"InvoiceEvents",
"namespace":"bla.bla.schema.avro.invoice",
"fields":[
{
"name":"payload",
"type":[
"null",
"bla.bla.schema.avro.invoice.InvoiceDrafted"
],
"default":null
}
]
}
]
架构 v2(添加了新的联合类型:InvoiceCreated):
[
...
{
"type":"record",
"name":"InvoiceEvents",
"namespace":"bla.bla.schema.avro.invoice",
"fields":[
{
"name":"payload",
"type":[
"null",
"bla.bla.schema.avro.invoice.InvoiceDrafted",
"bla.bla.schema.avro.invoice.InvoiceCreated",
],
"default":null
}
]
}
]
解决方案
经过一番思考,我们可能会选择选项 3,因为不跳过/丢失事件对项目来说比解耦更重要:
- 处理自定义反序列化器和跳过事件中的异常(可能会丢失有趣的事件 - 为了不丢失事件,所有消费服务必须在所有生产服务之前升级)
- 将所有自定义记录联合转换为单独的可选字段(可能会丢失有趣的事件,因为更改是前向兼容的并且消费服务不会阻塞)
- 在所有使用新自定义记录类型的模式的消费服务中接受反序列化错误/块消费和碰撞版本(这保证不会丢失任何有趣的事件)。
如果有更好的选择,请发表评论,我错过了!
更新: 似乎选项(2)现在可以以更简洁的方式实现,因为现在您可以拥有多种类型的主题(https://github.com/confluentinc/schema-registry/pull/680)。这意味着一个主题可以有不同的值类型(例如 InvoiceCreated、InvoiceEdited 等),而无需使用 avro 联合,而每种不同的类型都有自己的演变路线!