2.2. 创建序列化上下文初始化器


序列化上下文初始化器可让您在 Data Grid 中注册以下内容:

  • 描述用户类型的 protobuf 模式。
  • 提供序列化和解序列化功能的 Marshallers。

在高级别中,您应该执行以下操作来创建序列化上下文初始化器:

  1. 在您的 Java 类中添加 ProtoStream 注解。
  2. 使用 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。

流程

  1. 使用 @ProtoField 标注您要 marshall 的 Java 字段,可直接在字段或 getter 或 setter 方法上添加。

    Java 类中的任何非注解字段都是临时的。例如,您有一个带有 15 个字段的 Java 类,并注解了其中五个字段。生成的架构仅包含 5 个字段,在 Data Grid 中存储类实例时,只有五个字段才会被编译。

  2. 使用 @ProtoFactory 为不可变对象注解构造器。注解的构造器必须初始化标有 @ProtoField 的所有字段。
  3. 使用 @ProtoEnumValue 注解任何 Java 枚举的成员。

以下 Author.javaBook.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 对象类,您无法直接注解。

流程

  1. 创建一个 适配器 类并添加 @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" 后缀。

流程

  1. 定义一个扩展 GeneratedSchema 或其超级接口 SerializationContextInitializer 的接口。

    注意

    GeneratedSchema 接口包含访问 Protobuf 模式的方法,而 SerializationContextInitializer 接口只支持注册方法。

  2. 使用 @ProtoSchema 给接口添加注释。
  3. 确保 includeClasses 参数包含生成的 SerializationContextInitializer 实现的所有类。
  4. 使用 schemaFileName 参数为生成的 .proto 模式指定一个名称。
  5. target/classes 下设置一个路径,其中使用 schemaFilePath 参数生成 schema 文件。
  6. 使用 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 作为同一消息的一部分时返回警告列表。

LENIENT,STRICT

没有更改字段 ID

比较当前和更新的定义,并在任何字段 ID 号已更改时返回警告列表。

LENIENT,STRICT

没有更改字段类型

比较当前和更新的定义,并在任何字段类型已更改时返回警告列表。

LENIENT,STRICT

没有删除没有保留的字段

比较当前和更新的定义,并在没有相应保留该字段名称或 ID 的情况下删除任何字段时返回警告列表。

LENIENT,STRICT

没有删除保留的字段

比较当前和更新的定义,并在删除了任何保留字段时返回警告列表。

STRICT

没有更改字段名称

比较当前和更新的定义,并在任何消息之前字段被重命名时返回警告列表。

STRICT

2.2.6. 注册序列化上下文初始化器

对于嵌入式缓存,Data Grid 会自动使用 java.util.ServiceLoader 在注解的 SerializationContextInitializer 实现中注册序列化上下文和 marshallers。

如果您希望,您可以禁用 SerializationContextInitializer 实现的自动注册,然后手动注册。

重要

如果手动注册一个 SerializationContextInitializer 实现,它将禁用自动注册。然后您必须手动注册所有其他实现。

流程

  1. ProtoSchema.service 注解设置 false

    @ProtoSchema(
          includeClasses = SomeClass.class,
          ...
          service = false
    )
  2. 手动以编程方式或声明性注册 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-protostreamapplication/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 强烈建议您手动实施 SerializationContextInitializerGeneratedSchema 接口。

可以使用 ProtobufTagMarshallerRawProtobufMarshaller 注解手动实现 SerializationContextInitializerGeneratedSchema 接口。

但是,手动实现需要大量开销,且容易出错。使用 protostream-processor 工件生成的实现是配置 ProtoStream marshalling 更有效且可靠的方法。

Red Hat logoGithubRedditYoutubeTwitter

学习

尝试、购买和销售

社区

关于红帽文档

通过我们的产品和服务,以及可以信赖的内容,帮助红帽用户创新并实现他们的目标。

让开源更具包容性

红帽致力于替换我们的代码、文档和 Web 属性中存在问题的语言。欲了解更多详情,请参阅红帽博客.

關於紅帽

我们提供强化的解决方案,使企业能够更轻松地跨平台和环境(从核心数据中心到网络边缘)工作。

© 2024 Red Hat, Inc.