2.2. 创建序列化上下文初始化器
序列化上下文初始化器可让您在 Data Grid 中注册以下内容:
- 描述用户类型的 protobuf 模式。
- 提供序列化和解序列化功能的 Marshallers。
在高级别中,您应该执行以下操作来创建序列化上下文初始化器:
- 在您的 Java 类中添加 ProtoStream 注解。
-
使用 Data Grid 提供的 ProtoStream 处理器编译您的
SerializationContextInitializer
实现。
org.infinispan.protostream.MessageMarshaller
接口已弃用,计划在以后的 ProtoStream 版本中删除。您应该忽略显示如何使用 MessageMarshaller
的代码示例或文档,直到它被完全删除。
2.2.1. 添加 ProtoStream 处理器
Data Grid 提供了一个 ProtoStream 处理器工件,用于在编译时处理类中的 Java 注解,以生成 Protobuf 模式、附带的 marshallers 和 SerializationContextInitializer
接口的共识实现。
流程
将
protostream-processor
添加到maven-compiler-plugin
的注解处理器配置中,到pom.xml
。<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>...</version> <configuration> <annotationProcessorPaths> <annotationProcessorPath> <groupId>org.infinispan.protostream</groupId> <artifactId>protostream-processor</artifactId> <version>...</version> </annotationProcessorPath> </annotationProcessorPaths> </configuration> </plugin> </plugins> </build>
2.2.2. 在 Java 类中添加 ProtoStream 注解
通过向 Java 类及其成员添加注解来声明 ProtoStream 元数据。然后,数据网格使用 ProtoStream 处理器从这些注解生成 Protobuf 模式和相关 marshallers。
流程
使用
@ProtoField
标注您要 marshall 的 Java 字段,可直接在字段或 getter 或 setter 方法上添加。Java 类中的任何非注解字段都是临时的。例如,您有一个带有 15 个字段的 Java 类,并注解了其中五个字段。生成的架构仅包含 5 个字段,在 Data Grid 中存储类实例时,只有五个字段才会被编译。
-
使用
@ProtoFactory
为不可变对象注解构造器。注解的构造器必须初始化标有@ProtoField
的所有字段。 -
使用
@ProtoEnumValue
注解任何 Java 枚举的成员。
以下 Author.java
和 Book.java
示例显示标有 @ProtoField
和 @ProtoFactory
的 Java 类:
Author.java
import org.infinispan.protostream.annotations.ProtoFactory; import org.infinispan.protostream.annotations.ProtoField; public class Author { @ProtoField(1) final String name; @ProtoField(2) final String surname; @ProtoFactory Author(String name, String surname) { this.name = name; this.surname = surname; } // public Getter methods omitted for brevity }
Book.java
import org.infinispan.protostream.annotations.ProtoFactory; import org.infinispan.protostream.annotations.ProtoField; public class Book { @ProtoField(number = 1) public final UUID id; @ProtoField(number = 2) final String title; @ProtoField(number = 3) final String description; @ProtoField(number = 4, defaultValue = "0") final int publicationYear; @ProtoField(number = 5, collectionImplementation = ArrayList.class) final List<Author> authors; @ProtoField(number = 6) public Language language; @ProtoFactory Book(UUID id, String title, String description, int publicationYear, List<Author> authors, Language language) { this.id = id; this.title = title; this.description = description; this.publicationYear = publicationYear; this.authors = authors; this.language = language; } // public Getter methods not included for brevity }
以下 Language.java
示例显示了一个 Java enum,带有 @ProtoEnumValue
以及对应的 Protobuf 模式:
language.java
import org.infinispan.protostream.annotations.ProtoEnumValue; public enum Language { @ProtoEnumValue(number = 0, name = "EN") ENGLISH, @ProtoEnumValue(number = 1, name = "DE") GERMAN, @ProtoEnumValue(number = 2, name = "IT") ITALIAN, @ProtoEnumValue(number = 3, name = "ES") SPANISH, @ProtoEnumValue(number = 4, name = "FR") FRENCH; }
language.proto
enum Language { EN = 0; DE = 1; IT = 2; ES = 3; FR = 4; }
2.2.3. 创建 ProtoStream 适配器类
ProtoStream 提供了一个 @ProtoAdapter
注解,您可以使用它来 marshall 外部第三方 Java 对象类,您无法直接注解。
流程
创建一个
适配器
类并添加@ProtoAdapter
注释,如下例所示:import java.util.UUID; import org.infinispan.protostream.annotations.ProtoAdapter; import org.infinispan.protostream.annotations.ProtoFactory; import org.infinispan.protostream.annotations.ProtoField; import org.infinispan.protostream.descriptors.Type; /** * Human readable UUID adapter for UUID marshalling */ @ProtoAdapter(UUID.class) public class UUIDAdapter { @ProtoFactory UUID create(String stringUUID) { return UUID.fromString(stringUUID); } @ProtoField(1) String getStringUUID(UUID uuid) { return uuid.toString(); } }
2.2.4. 生成序列化上下文初始化器
添加 ProtoStream 处理器并给 Java 类添加 @ProtoSchema
注解后,您可以将 @ProtoSchema 注解添加到接口,以便 Data Grid 生成 Protobuf 模式、附带的 marshallers 和 SerializationContextInitializer
的共识实现。
默认情况下,生成的实现名称是注解的类名称,带有 "Impl" 后缀。
流程
定义一个扩展
GeneratedSchema
或其超级接口SerializationContextInitializer
的接口。注意GeneratedSchema
接口包含访问 Protobuf 模式的方法,而SerializationContextInitializer
接口只支持注册方法。-
使用
@ProtoSchema
给接口添加注释。 -
确保
includeClasses
参数包含生成的SerializationContextInitializer
实现的所有类。 -
使用
schemaFileName
参数为生成的.proto
模式指定一个名称。 -
在
target/classes
下设置一个路径,其中使用schemaFilePath
参数生成 schema 文件。 -
使用
schemaPackageName
参数为生成的.proto
模式指定软件包名称。
以下示例显示了带有 @ProtoSchema
标注的 GeneratedSchema
接口:
@ProtoSchema( includeClasses = { Book.class, Author.class, UUIDAdapter.class, Language.class }, schemaFileName = "library.proto", schemaFilePath = "proto/", schemaPackageName = "book_sample") interface LibraryInitializer extends GeneratedSchema { }
后续步骤
如果您使用嵌入式缓存,Data Grid 会自动注册 SerializationContextInitializer
实现。
如果使用远程缓存,您必须将 SerializationContextInitializer
实现注册到 Data Grid Server。
2.2.5. 协议缓冲最佳实践
协议缓冲文档提供了有关如何设计消息以及如何 发展 架构以保持向后兼容性 的最佳实践 列表。
当检测到模式被更新并拒绝更新时,数据网格可以自动执行兼容性检查。检查类型可以通过全局 序列化
配置的 schema-compatibility
属性进行配置。可用级别有:
-
UNRESTRICTED
: 不执行任何检查 -
LENIENT
: 强制执行规则的子集 -
STRICT
: 所有规则都被强制执行(默认)
下表显示了为每个级别启用的兼容性检查规则
规则 | 描述 | 级别 |
---|---|---|
没有使用保留的字段 | 比较当前和更新的定义,并在任何消息之前保留字段或 ID 作为同一消息的一部分时返回警告列表。 |
|
没有更改字段 ID | 比较当前和更新的定义,并在任何字段 ID 号已更改时返回警告列表。 |
|
没有更改字段类型 | 比较当前和更新的定义,并在任何字段类型已更改时返回警告列表。 |
|
没有删除没有保留的字段 | 比较当前和更新的定义,并在没有相应保留该字段名称或 ID 的情况下删除任何字段时返回警告列表。 |
|
没有删除保留的字段 | 比较当前和更新的定义,并在删除了任何保留字段时返回警告列表。 |
|
没有更改字段名称 | 比较当前和更新的定义,并在任何消息之前字段被重命名时返回警告列表。 |
|
2.2.6. 注册序列化上下文初始化器
对于嵌入式缓存,Data Grid 会自动使用 java.util.ServiceLoader
在注解的 SerializationContextInitializer
实现中注册序列化上下文和 marshallers。
如果您希望,您可以禁用 SerializationContextInitializer
实现的自动注册,然后手动注册。
如果手动注册一个 SerializationContextInitializer
实现,它将禁用自动注册。然后您必须手动注册所有其他实现。
流程
为
ProtoSchema.service
注解设置false
。@ProtoSchema( includeClasses = SomeClass.class, ... service = false )
-
手动以编程方式或声明性注册
SerializationContextInitializer
实现,如下例所示:
声明
<serialization> <context-initializer class="org.infinispan.example.LibraryInitializerImpl"/> <context-initializer class="org.infinispan.example.another.SCIImpl"/> </serialization>
programmatic
GlobalConfigurationBuilder builder = new GlobalConfigurationBuilder(); builder.serialization() .addContextInitializers(new LibraryInitializerImpl(), new SCIImpl());
2.2.7. 将 Protobuf 模式注册到 Data Grid 服务器
将 Protobuf 模式注册到 Data Grid Server,以执行 Ickle 查询,或者从 application/x-protostream
转换到其他介质类型,如 application/json
。
先决条件
使用 ProtoStream 处理器生成 Protobuf 模式。
您可以在
target/<schemaFilePath>/
目录中找到生成的 Protobuf 模式。具有
CREATE
权限的用户。注意安全授权需要
CREATE
权限来添加模式。使用默认设置时,您至少需要deployer
角色。
流程
使用以下方法之一向 Data Grid 服务器添加 Protobuf 模式:
- 在任何浏览器中打开 Data Grid Console,选择 Schema 选项卡,然后选择 Add Protobuf 模式。
使用 Data Grid 命令行界面(CLI)中的
schema
命令和--upload=
参数。schema --upload=person.proto person
使用 REST API 将
POST
请求的有效负载中包含 Protobuf 模式。POST/rest/v2/schemas/<schema_name>
使用带有 Hot Rod 客户端生成的
SerializationContextInitializer
实现来注册 Protobuf 模式,如下例所示:/** * Register generated Protobuf schema with Data Grid Server. * This requires the RemoteCacheManager to be initialized. * * @param initializer The serialization context initializer for the schema. */ private void registerSchemas(SerializationContextInitializer initializer) { // Store schemas in the '___protobuf_metadata' cache to register them. // Using ProtobufMetadataManagerConstants might require the query dependency. final RemoteCache<String, String> protoMetadataCache = remoteCacheManager.getCache(ProtobufMetadataManagerConstants.PROTOBUF_METADATA_CACHE_NAME); // Add the generated schema to the cache. protoMetadataCache.put(initializer.getProtoFileName(), initializer.getProtoFile()); // Ensure the registered Protobuf schemas do not contain errors. // Throw an exception if errors exist. String errors = protoMetadataCache.get(ProtobufMetadataManagerConstants.ERRORS_KEY_SUFFIX); if (errors != null) { throw new IllegalStateException("Some Protobuf schema files contain errors: " + errors + "\nSchema :\n" + initializer.getProtoFileName()); } }
使用
SerializationContextInitializer
实现和自定义类将 JAR 文件添加到$RHDG_HOME/server/lib
目录。当您这样做时,Data Grid 服务器在启动时注册 Protobuf 模式。但是,您必须将存档添加到每台服务器安装中,因为模式不会保存在
___protobuf_metadata
缓存中,或者在集群中自动分发。注意如果您需要 Data Grid 服务器执行任何
application/x-protostream
到application/x-java-object
转换,则必须执行此操作,在这种情况下,还必须为您的 POJO 添加任何 JAR 文件。
后续步骤
使用您的 Hot Rod 客户端注册 SerializationContextInitializer
,如下例所示:
ConfigurationBuilder remoteBuilder = new ConfigurationBuilder(); remoteBuilder.addServer().host(host).port(Integer.parseInt(port)); // Add your generated SerializationContextInitializer implementation. LibraryInitalizer initializer = new LibraryInitalizerImpl(); remoteBuilder.addContextInitializer(initializer);
2.2.8. 手动序列化上下文初始化器实现
Data Grid 强烈建议您手动实施 SerializationContextInitializer
或 GeneratedSchema
接口。
可以使用 ProtobufTagMarshaller
和 RawProtobufMarshaller
注解手动实现 SerializationContextInitializer
或 GeneratedSchema
接口。
但是,手动实现需要大量开销,且容易出错。使用 protostream-processor
工件生成的实现是配置 ProtoStream marshalling 更有效且可靠的方法。