5.2. 将应用程序迁移到 AutoProtoSchemaBuilder 注解
之前的 Data Grid 版本使用 ProtoStream API 中的 MessageMarshaller
接口来配置 marshalling。
MessageMarshaller
API 和 ProtoSchemaBuilder
注解都从 Data Grid 8.1.1 中弃用,它对应于 ProtoStream 4.3.4。
使用 MessageMarshaller
接口涉及:
- 手动创建 Protobuf 模式。
-
将
ProtoSchemaBuilder
注释添加到 Java 类,然后生成 Protobuf 模式。
但是,配置 ProtoStream marshalling 的这些技术不作为 AutoProtoSchemaBuilder
注释(可从 Data Grid 81 开始获得)。只需在您的 Java 类中添加 AutoProtoSchemaBuilder
注释,并生成包含 Protobuf 模式和相关 marshallers 的 SerializationContextInitializer
实现。
红帽建议您开始使用 AutoProtoSchemaBuilder
注解从 ProtoStream marshaller 获取最佳结果。
以下代码示例演示了如何将应用从 MessageMarshaller
API 迁移到 AutoProtoSchemaBuilder
注释。
5.2.1. 基本 MessageMarshaller 实现
这个示例包含使用非默认类型的一些字段。text
字段有不同的顺序,固定32
字段与生成的 Protobuf 模式类型冲突,因为代码生成器默认使用 int
类型。
SimpleEntry.java
public class SimpleEntry { private String description; private Collection<String> text; private int intDefault; private Integer fixed32; // public Getter, Setter, equals and HashCode methods omitted for brevity }
SimpleEntryMarshaller.java
import org.infinispan.protostream.MessageMarshaller; public class SimpleEntryMarshaller implements MessageMarshaller<SimpleEntry> { @Override public void writeTo(ProtoStreamWriter writer, SimpleEntry testEntry) throws IOException { writer.writeString("description", testEntry.getDescription()); writer.writeInt("intDefault", testEntry.getIntDefault()); writer.writeInt("fix32", testEntry.getFixed32()); writer.writeCollection("text", testEntry.getText(), String.class); } @Override public SimpleEntry readFrom(MessageMarshaller.ProtoStreamReader reader) throws IOException { SimpleEntry x = new SimpleEntry(); x.setDescription(reader.readString("description")); x.setIntDefault(reader.readInt("intDefault")); x.setFixed32(reader.readInt("fix32")); x.setText(reader.readCollection("text", new LinkedList<String>(), String.class)); return x; } }
生成的 Protobuf 模式
syntax = "proto2"; package example; message SimpleEntry { required string description = 1; optional int32 intDefault = 2; optional fixed32 fix32 = 3; repeated string text = 4; }
迁移到 AutoProtoSchemaBuilder 注解
SimpleEntry.java
import org.infinispan.protostream.annotations.ProtoField; import org.infinispan.protostream.descriptors.Type; public class SimpleEntry { private String description; private Collection<String> text; private int intDefault; private Integer fixed32; @ProtoField(number = 1) public String getDescription() {...} @ProtoField(number = 4, collectionImplementation = LinkedList.class) public Collection<String> getText() {...} @ProtoField(number = 2, defaultValue = "0") public int getIntDefault() {...} @ProtoField(number = 3, type = Type.FIXED32) public Integer getFixed32() {...} // public Getter, Setter, equals and HashCode methods and convenient constructors omitted for brevity }
SimpleEntryInitializer.java
import org.infinispan.protostream.GeneratedSchema; import org.infinispan.protostream.annotations.AutoProtoSchemaBuilder; @AutoProtoSchemaBuilder(includeClasses = { SimpleEntry.class }, schemaFileName = "simple.proto", schemaFilePath = "proto", schemaPackageName = "example") public interface SimpleEntryInitializer extends GeneratedSchema { }
重要观察
-
字段 2 定义为
int
,之前版本中的 ProtoStream marshaller 没有检查。 因为 Java
int
字段不可为空,因此 ProtoStream 处理器将失败。
必须需要
Javaint
字段,或使用defaultValue
进行初始化。从 Java 应用程序的角度来看,
int
字段使用 "0" 初始化,因此您可以使用defaultValue
,而不影响任何 put 操作会设置它。如果始终存在数据视角,则更改为必需
不是问题,但可能会导致不同的客户端出现问题。-
为了兼容性,字段 3 必须明确设置为
Type.FIXED32
。 - 对于生成的 Protobuf 模式,必须以正确的顺序设置 文本集合。
Protobuf 模式中的 文本集合 顺序必须与迁移之前和之后相同。同样,您必须在迁移过程中设置 fixed32
类型。
如果没有,客户端应用程序可能会抛出以下异常,且无法启动:
Exception ( ISPN004034: Unable to unmarshall bytes )
在其他情况下,您可能会观察缓存的数据不完整或不准确。
5.2.2. 带有自定义类型的 MessageMarshaller 实现
本节提供了 MessageMarshaller
实现迁移示例,其中包含 ProtoStream 没有原生处理的字段。
以下示例使用 BigInteger
类,但适用于任何类,即使是数据网格适配器或自定义类。
BigInteger
类不可变,因此没有参数结构。
CustomTypeEntry.java
import java.math.BigInteger; public class CustomTypeEntry { final String description; final BigInteger bigInt; // public Getter, Setter, equals and HashCode methods and convenient constructors omitted for brevity }
CustomTypeEntryMarshaller.java
import org.infinispan.protostream.MessageMarshaller; public class CustomTypeEntryMarshaller implements MessageMarshaller<CustomTypeEntry> { @Override public void writeTo(ProtoStreamWriter writer, CustomTypeEntry testEntry) throws IOException { writer.writeString("description", testEntry.description); writer.writeString("bigInt", testEntry.bigInt.toString()); } @Override public CustomTypeEntry readFrom(MessageMarshaller.ProtoStreamReader reader) throws IOException { final String desc = reader.readString("description"); final BigInteger bInt = new BigInteger(reader.readString("bigInt")); return new CustomTypeEntry(desc, bInt); } }
CustomTypeEntry.proto
syntax = "proto2"; package example; message CustomTypeEntry { required string description = 1; required string bigInt = 2; }
使用适配器类迁移的代码
您可以使用 ProtoAdapter
注解以生成 Protobuf 模式的方式 marshall CustomType
类,它与您使用 MessageMarshaller
实现创建的 Protobuf 模式兼容。
使用这个方法,您可以:
-
不得在
CustomTypeEntry
类中添加注解。 -
创建一个
CustomTypeEntryAdapter
类,它使用@ProtoAdapter
注释来控制如何生成 Protobuf 模式和 marshaller。 包含
CustomTypeEntryAdapter
类,并包含@AutoProtoSchemaBuilder
注释。注意由于
AutoProtoSchemaBuilder
注释不引用CustomTypeEntry
类,因此该类中包含的任何注解都会被忽略。
以下示例显示了包含 CustomTypeEntry
类的 ProtoStream 注解的 CustomTypeEntryAdapter
类:
CustomTypeEntryAdapter.java
import java.math.BigInteger; import org.infinispan.protostream.annotations.ProtoAdapter; import org.infinispan.protostream.annotations.ProtoFactory; import org.infinispan.protostream.annotations.ProtoField; @ProtoAdapter(CustomTypeEntry.class) public class CustomTypeEntryAdapter { @ProtoFactory public CustomTypeEntry create(String description, String bigInt) { return new CustomTypeEntry(description, new BigInteger(bigInt)); } @ProtoField(number = 1, required = true) public String getDescription(CustomTypeEntry t) { return t.description; } @ProtoField(number = 2, required = true) public String getBigInt(CustomTypeEntry t) { return t.bigInt.toString(); } }
以下示例显示了带有 AutoProtoSchemaBuilder
注解的 SerializationContextInitializer
,该注解引用 CustomTypeEntryAdapter
类:
CustomTypeEntryInitializer.java
import org.infinispan.protostream.GeneratedSchema; import org.infinispan.protostream.annotations.AutoProtoSchemaBuilder; @AutoProtoSchemaBuilder(includeClasses = { CustomTypeEntryAdapter.class }, schemaFileName = "custom.proto", schemaFilePath = "proto", schemaPackageName = "example") public interface CustomTypeAdapterInitializer extends GeneratedSchema { }
在没有适配器类的情况下迁移的代码
您可以直接将 ProtoStream 注解添加到 CustomTypeEntry
类中,而不是创建适配器类。
在本例中,生成的 Protobuf 模式与通过 MessageMarshaller
接口添加的缓存中的数据不兼容,因为 BigInteger
是一个单独的消息。即使 adapter 字段写入相同的 String,也无法处理数据。
以下示例显示了直接包含 ProtoStream 注解的 CustomTypeEntry
类:
CustomTypeEntry.java
import java.math.BigInteger; public class CustomTypeEntry { @ProtoField(number = 1) final String description; @ProtoField(number = 2) final BigInteger bigInt; @ProtoFactory public CustomTypeEntry(String description, BigInteger bigInt) { this.description = description; this.bigInt = bigInt; } // public Getter, Setter, equals and HashCode methods and convenient constructors omitted for brevity }
以下示例显示了带有 AutoProtoSchemaBuilder
注解的 SerializationContextInitializer
,该注解引用 CustomTypeEntry
和 BigIntegerAdapter
类:
CustomTypeEntryInitializer.java
import org.infinispan.protostream.GeneratedSchema; import org.infinispan.protostream.annotations.AutoProtoSchemaBuilder; import org.infinispan.protostream.types.java.math.BigIntegerAdapter; @AutoProtoSchemaBuilder(includeClasses = { CustomTypeEntry.class, BigIntegerAdapter.class }, schemaFileName = "customtype.proto", schemaFilePath = "proto", schemaPackageName = "example") public interface CustomTypeInitializer extends GeneratedSchema { }
当您从前面的 SerializationContextInitializer
实现中生成 Protobuf 模式时,它会生成以下 Protobuf 模式:
CustomTypeEntry.proto
syntax = "proto2"; package example; message BigInteger { optional bytes bytes = 1; } message CustomTypeEntry { optional string description = 1; optional BigInteger bigInt = 2; }