缓存编码和 Marshalling


Red Hat Data Grid 8.5

对数据网格缓存和 marshall Java 对象进行编码

Red Hat Customer Content Services

摘要

Data Grid 缓存可以将键和值存储在不同的编码中。本文档论述了如何为远程和嵌入式缓存对数据进行编码,并解释了如何在应用程序中使用各种介质类型。另外,本指南论述了如何使用 ProtoStream API 的 Data Grid 实现来 marshall Java 对象作为协议缓冲(Protobuf)。您还可以查找有关在 Java 类中添加 ProtoStream 注解的信息,然后为 marshalling 功能以及索引的查询生成 Protobuf 模式。

Red Hat Data Grid

Data Grid 是一个高性能分布式内存数据存储。

无架构数据结构
将不同对象存储为键值对的灵活性。
基于网格的数据存储
旨在在集群中分发和复制数据。
弹性扩展
动态调整节点数量,以便在不中断服务的情况下满足需求。
数据互操作性
从不同端点在网格中存储、检索和查询数据。

Data Grid 文档

红帽客户门户网站中提供了 Data Grid 的文档。

Data Grid 下载

访问红帽客户门户上的 Data Grid 软件下载

注意

您必须有一个红帽帐户才能访问和下载数据中心软件。

使开源包含更多

红帽致力于替换我们的代码、文档和 Web 属性中存在问题的语言。我们从这四个术语开始:master、slave、黑名单和白名单。由于此项工作十分艰巨,这些更改将在即将推出的几个发行版本中逐步实施。详情请查看 CTO Chris Wright 的信息

第 1 章 配置缓存编码

了解如何使用不同的介质类型配置数据网格缓存,以及编码如何影响您可以使用 Data Grid 的方法。

1.1. 缓存编码

编码是由介质类型标识的格式,Data Grid 用来将条目(键/值对)存储在缓存中。

远程缓存

Data Grid Server 使用缓存配置中设置的编码将条目存储在远程缓存中。

热 Rod 和 REST 客户端包括一个媒体类型,以及他们向 Data Grid Server 发出的每个请求的媒体类型。为了处理具有不同介质类型的多个客户端,Data Grid Server 会将数据按需转换为缓存配置中设置的介质类型。

如果远程缓存没有任何编码配置,Data Grid 服务器将键和值存储为通用 字节[],而无需任何介质类型信息,这可能会导致在为客户端请求不同格式转换数据时导致意外结果。

使用 ProtoStream 编码

当客户端请求包含介质类型时,Data Grid Server 会返回一个错误,它无法转换为缓存配置中设置的介质类型。

如果要使用多个客户端,如 Data Grid Console 或 CLI、Hot Rod 或 REST,建议使用 application/x-protostream 介质类型配置缓存编码。ProtoStream 编码还允许您使用服务器端任务并在远程缓存上执行索引查询。

嵌入式缓存

默认情况下,Data Grid 将嵌入式缓存中的条目存储为 Plain Old Java 对象(POJO)。

对于集群嵌入的缓存,数据网格需要将所有 POJO 放入一个字节阵列,可在节点间复制,然后返回 POJO。这意味着,如果您没有配置另一个 marshaller,您必须确保 Data Grid 可以序列化带有 ProtoStream marshaller 的 POJO。

注意

如果您在嵌入式缓存中存储 mutable POJO,您应该始终使用新的 POJO 实例更新值。例如,如果您将 HashMap 存储为键/值对,则 Data Grid 集群的其他成员不会看到对映射的任何本地修改。另外,如果 Data Grid 分离了对象同时更新 Map 实例,则可能会出现 ConcurrentModificationException

1.2. protobuf 缓存编码

协议缓冲器(Protobuf)是结构化数据的轻量级二进制介质类型。作为缓存编码,Protobuf 为您提供了 Hot Rod 和 REST 端点的不同编程语言中客户端应用之间的互操作性。

Data Grid 使用 ProtoStream 库将缓存编码为 Protobuf,使用 application/x-protostream 介质类型。

以下示例显示了描述 Person 对象的 Protobuf 消息:

message Person {
    optional int32 id = 1;
    optional string name = 2;
    optional string surname = 3;
    optional Address address = 4;
    repeated PhoneNumber phoneNumbers = 5;
    optional uint32 age = 6;
    enum Gender {
        MALE = 0;
        FEMALE = 1;
    }
}
注意

protobuf 不支持循环对象。使用 Java 序列化或 JBoss Marshalling 进行 marshall 环形对象。

互操作性

由于它是语言中立,Protobuf 编码意味着,Data Grid 可以处理 Java、C++、Clo、Python、Go 等编写的客户端应用程序的请求。

protobuf 还使不同远程端点(Hot Rod 或 REST 上的客户端)能够在同一数据上运行。由于它使用 REST API,因此您可以通过 Data Grid 控制台访问和使用 Protobuf-encoded 缓存。

注意

您不能将 Data Grid 控制台与 application/x-protostream 以外的任何二进制编码一起使用。

您应该始终使用带有 application/x-protostream 介质类型的 Protobuf 缓存编码来与任何红帽技术集成,因为它允许应用程序和服务之间的通信。

queries

Data Grid 需要缓存中的结构化数据表示,以实现快速可靠的查询。要使用 Ickle 查询语言搜索缓存,您可以注册描述对象的 Protobuf 模式。

自定义类型

网格包括 ProtoStream API 的实现,它带有对常用类型的原生支持,包括 StringInteger。如果要在缓存中存储自定义类型,请使用 ProtoStream marshalling 生成并注册带有 Data Grid 的序列化上下文,以便它可以编译您的对象。

1.2.1. 编码缓存作为 ProtoStream

将 Data Grid 配置为使用 ProtoStream 库将缓存条目存储为协议缓冲器(Protobuf)。

流程

  • 为键和值指定 application/x-protostream 介质类型。

声明

<distributed-cache>
   <encoding>
      <key media-type="application/x-protostream"/>
      <value media-type="application/x-protostream"/>
   </encoding>
</distributed-cache>

programmatic

//Create cache configuration that encodes keys and values as ProtoStream
ConfigurationBuilder builder = new ConfigurationBuilder();
builder.clustering().cacheMode(CacheMode.DIST_SYNC)
       .encoding().key().mediaType("application/x-protostream")
       .encoding().value().mediaType("application/x-protostream");

另外,您也可以对键和值使用相同的编码:

声明

<encoding media-type="application/x-protostream"/>

programmatic

.encoding().mediaType("application/x-protostream");

1.3. 基于文本的缓存编码

基于文本的编码是人类可读的内容,如纯文本。类的 "Hello World" 示例条目可以存储在缓存中,如下所示:

key=hello
value=world

如果您使用 text/plain media 类型对缓存进行编码,则 Data Grid 可以转换到以下介质类型:

  • application/xml
  • application/json
  • application/x-protostream

以下示例配置使用 text/plain; charset=UTF-8 介质类型对键和值进行编码:

<distributed-cache>
   <encoding>
      <key media-type="text/plain; charset=UTF-8"/>
      <value media-type="text/plain; charset=UTF-8"/>
   </encoding>
</distributed-cache>

1.3.1. 客户端和基于文本的编码

如果您将编码配置为使用基于文本的介质类型存储键和值,那么您还需要配置客户端以便在这些缓存上运行。

热 Rod 客户端

Data Grid 使用 ProtoStream 库来原生处理 Stringbyte[] 类型。如果您使用 text/plain 介质类型配置缓存编码,Hot Rod 客户端可能不一定需要任何 marshaller 配置来执行缓存操作。

对于其他基于文本的介质类型,如 JSON 或 XML,Hot Rod 客户端可以使用 org.infinispan.commons.commons.marshall.UTF8StringMarshaller marshaller 转换为 文本/plain 介质类型。

REST 客户端

REST 客户端必须在请求标头中包含缓存的介质类型。

例如,如果您将缓存编码配置为 text/plain;charset=UTF-8,则 REST 客户端应发送以下标头:

  • accept: text/plain; charset=UTF-8 用于读取操作。
  • Content-Type: text/plain; charset=UTF-8Key-Content-Type: text/plain; charset=UTF-8 用于写操作。

1.4. Marshalled Java 对象

Data Grid 将 marshalled Java 对象存储在缓存中作为字节数阵列。例如,以下是存储为内存中值的 Person 对象的简单表示:

value=[61 6c 61 6e 0a 70 61 72 74 72 69 64 67 65]

要将 marshalled 对象存储在缓存中,您应该使用 ProtoStream marshaller,除非存在严格的要求。例如,当将客户端应用程序从旧版本的 Data Grid 迁移时,您可能需要将 JBoss marshalling 替换为您的 Hot Rod Java 客户端。

Data Grid 使用以下介质类型将 marshalled Java 对象存储为字节数组:

  • application/x-protostream
  • application/x-jboss-marshalling
  • application/x-java-serialized-object
注意

当存储 unmarshalled Java 对象时,Data Grid 使用 equals ()hashCode () 的对象实现。当存储 marshalled 对象时,会将 marshalled 字节用于相等和散列化。

1.4.1. 客户端和 marshalled 对象

当您将 Hot Rod Java 客户端配置为使用 marshaller 时,您必须使用该 marshaller 的编码配置缓存。

每个 marshaller 都使用不同的介质类型来生成可传输到 Data Grid Server 的 byte[] 内容。从服务器读取时,客户端 marshaller 会执行相反的操作,使用介质类型从 byte[] 内容生成数据。

您的缓存编码必须与 Hot Rod 客户端 marshaller 兼容。例如,如果您将缓存编码配置为 application/x-protostream,您可以在客户端中使用 ProtoStream marshaller 来在该缓存中操作。但是,如果客户端 marshaller 使用 Data Grid 无法转换为 application/x-protostream 的编码,则数据网格会抛出错误消息。

如果您使用 JavaSerializationMarshallerGenericJBossMarshaller,您应该分别使用 application/x-java-serialized-objectapplication/x-jboss-marshalling 介质类型对缓存进行编码。

ProtoStream 到 JSON 转换

Data Grid 使用 application/x-protostream 介质类型编码的键和值转换为 application/json

这允许 REST 客户端在请求标头中包含 JSON 介质类型,并在使用 ProtoStream 编码的缓存上执行操作:

  • accept:用于读取操作的 application/json
  • content-Type: 用于写入操作的 application/json

1.5. 普通旧 Java 对象(POJO)

为了获得最佳性能,Data Grid 建议仅将 unmarshalled POJO 存储在嵌入式缓存中。但是,您可以使用以下介质类型配置键和值:

  • application/x-java-object

1.5.1. 客户端和 POJO

虽然 Data Grid 不推荐这样做,但客户端可以在存储带有 application/x-java-object 介质类型的 unmarshalled POJO 的缓存上运行。

热 Rod 客户端

热 Rod 客户端 marshallers 必须可供 Data Grid 服务器使用,以便它可以反序列化 Java 对象。默认情况下,服务器上提供了 ProtoStream 和 Java Serialization marshallers。

REST 客户端

REST 客户端必须使用 JSON 或 XML 作为键和值,以便 Data Grid 可以转换到 POJO,或从 POJO 转换。

注意

Data Grid 要求您将 Java 类添加到 deserialization allowlist 中,以便将 XML 转换为 POJO。

1.6. 在 Data Grid Server 安装中添加 JAR

通过将自定义 JAR 文件添加到 classpath 中,将自定义 JAR 文件提供给 Data Grid 服务器。

重要
  • Data Grid 仅在启动期间加载 JAR 文件。

    在使集群备份前,您应该安全地关闭集群中的所有节点,并将任何 JAR 文件提供给每个节点。

  • 您应该仅将自定义 JAR 文件添加到 $RHDG_HOME/server/lib 目录中。

    $RHDG_HOME/lib 目录为 Data Grid JAR 文件保留。

流程

  1. 如果 Data Grid Server 正在运行,则停止它。
  2. 将 JAR 文件添加到 server/lib 目录中,例如:

    ├── server
    │   └── lib
    │       └── UserObjects.jar

第 2 章 使用 ProtoStream 实现自定义对象

Marshalling 是一个将 Java 对象转换为可在网络传输或存储到磁盘的二进制格式的进程。反向进程 unmarshalling 将数据从二进制格式转换回 Java 对象。

Data Grid 执行 marshalling 和 unmarshalling to:

  • 将数据发送到集群中的其他 Data Grid 节点。
  • 将数据存储在持久缓存存储中。
  • 在客户端和远程缓存之间传输对象。
  • 将对象存储在 JVM 堆之外的原生内存中。
  • 当缓存编码不是 application/x-java-object 时,将对象存储在 JVM 堆内存中。

在 Data Grid 缓存中存储自定义对象时,您应该使用带有 ProtoStream marshaller 的基于 Protobuf 的 marshalling。

2.1. ProtoStream marshalling

Data Grid 提供 ProtoStream API,以便您可以将所有 Java 对象作为协议缓冲器(Protobuf)进行 marshall。

ProtoStream 原生支持许多不同的 Java 数据类型,这意味着您不需要为这些类型配置 ProtoStream marshalling。对于自定义或用户类型,您需要提供一些信息,以便数据网格可以将这些对象划分到缓存中。

SerializationContext
包含 Protobuf 类型定义的存储库,从 Protobuf 模式(.proto 文件)和附带的 marshallers 加载。
SerializationContextInitializer
初始化 SerializationContext 的接口。

2.1.1. ProtoStream 类型

Data Grid 使用了一个 ProtoStream 库,它可以针对键和值处理以下类型,并在原语类型中处理未附带的等效类型:

  • byte[]
  • byte
  • 字符串
  • 整数
  • Long
  • å�Œ
  • 浮点值
  • 布尔值
  • short
  • 字符
  • java.util.Date
  • java.time.Instant
其他类型的集合

ProtoStream 库包括多个用于常见 Java 类型的适配器类,例如:

  • java.math.BigDecimal
  • java.math.BigInteger
  • java.util.UUID
  • java.util.BitSet

Data Grid 为 protostream-types 工件中的一些通用 JDK 类提供所有适配器类,它们包含在 infinispan-coreinfinispan-client-hotrod 依赖项中。您不需要任何配置将适配器类存储为键或值。

但是,如果要使用适配器类作为 ProtoStream-annotated POJO 中的 marshallable 字段,您可以使用以下方法这样做:

  • 使用 ProtoSchema 注解的 dependentOn 元素指定 CommonTypesSchemaCommonContainerTypesSchema 类。
@ProtoSchema(dependsOn = {org.infinispan.protostream.types.java.CommonTypes, org.infinispan.protostream.types.java.CommonContainerTypes}, schemaFileName = "library.proto", schemaFilePath = "proto", schemaPackageName = "example")
public interface LibraryInitalizer extends SerializationContextInitializer {
}
  • 使用 ProtoSchema 注解的 includeClasses 元素指定所需的适配器类
@ProtoSchema(includeClasses = { Author.class, Book.class, UUIDAdapter.class, java.math.BigInteger }, schemaFileName = "library.proto", schemaFilePath = "proto", schemaPackageName = "library")
public interface LibraryInitalizer extends SerializationContextInitializer {

}

2.1.2. ProtoStream 注解

ProtoStream API 包含您可以添加到 Java 应用程序的注解来定义 Protobuf 模式,后者为对象提供结构化格式。

本主题提供有关 ProtoStream 注解的额外详情。您应该参考 org.infinispan.protostream.annotations 软件包中的文档来获取完整的信息。

Proto

@proto 定义协议缓冲消息,无需使用 @Proto Field 注释给所有字段标注。

  • 使用此注解从带有公共字段的记录或类快速生成消息。
  • 字段必须是公共的,它们将根据声明顺序分配递增数字。
  • 可以使用 ProtoField 注解覆盖字段的自动默认值。
警告

使用自动 Protobuf 字段编号只适用于快速原型。对于生产环境,您应该遵循 协议缓冲最佳实践,以确保未来/反馈与您的模式兼容。

ProtoField

@ProtoField 定义 Protobuf message 字段。

此注解适用于字段以及 getter 和 setter 方法。除非使用的是 @Proto 注释,否则类必须至少有一个字段标上 @ProtoField,然后 Data Grid 可以将其 marshall 作为 Protobuf。

参数value可选或必需的描述

number

整数

必填

标签号在类内必须是唯一的。

type

类型

选填

声明字段的 Protobuf 类型。如果没有指定类型,则会从 Java 属性中推断出它。

您可以使用 @ProtoField (type) 元素来更改 Protobuf 类型,类似于将 Java int 更改为 fixed32。Java 属性的任何不兼容的声明都会导致编译器错误。

collectionImplementation

选填

如果属性 type 是接口或抽象类,则指示实际集合类型。

javaType

选填

如果属性 type 是抽象类或接口,则表示实际的 Java 类型。该值必须为属性类型分配可即时的 Java 类型。

如果使用 javaType 参数声明类型,则所有用户代码都必须遵循该类型。如果条目为 unmarshalled,则为该条目生成的 marshaller 使用该实现。如果本地客户端使用不同于声明的实现,则会导致 ClassCastExceptions。

name

字符串

选填

指定 Protobuf 模式的名称。

defaultValue

字符串

选填

如果字段在从缓存中读取时不可用,则指定字段的默认值。该值必须遵循 Java 字段类型的正确语法。

ProtoFactory

@ProtoFactory 标记用于创建消息类实例的单个构造器或静态工厂方法。

您可以使用此注解来支持不可变消息类。使用 @ProtoField 注解的所有字段都必须包含在参数中。

  • @ProtoFactory 结构或方法的字段名称和参数必须与对应的 Protobuf 消息匹配,但顺序不重要。
  • 如果您没有将 @ProtoFactory 注解的构造器添加到类,则该类必须具有默认的 no-argument 结构器,否则编译过程中发生错误。
ProtoSchema

@ProtoSchema 生成扩展 SerializationContextInitializer 的类或接口的实施。

如果激活,ProtoStream 处理器会在编译时在带有 Impl 后缀或您使用 className 参数指定的名称相同的软件包中生成实现。

includeClassesbasePackages 参数引用 ProtoStream 处理器应扫描并包含在 Protobuf 模式和 marshaller 中的类。如果您没有设置这些参数,ProtoStream 处理器会扫描整个源路径,这可能会导致意外的结果,我们不建议这样做。您还可以使用 excludeClasses 参数和 basePackages 参数来排除类。

schemaFileNameschemaPackageName 参数在此名称下注册生成的 Protobuf 模式。如果没有设置这些参数,则注释的简单类名称将与未命名或默认软件包一起使用。模式名称必须以 .proto 文件扩展名结尾。您还可以使用 marshallersOnly 来只生成 marshallers,并阻止 Protobuf 模式生成。

ProtoStream 进程自动生成 META-INF/services 服务元数据文件,您可以使用这些文件以便 Data Grid 服务器自动获取 JAR 来注册 Protobuf 模式。

dependentOn 参数列出了实施 SerializedContextInitializer 的注解类,以首先执行。如果类没有实现 SerializedContextInitializer,或使用 ProtoSchema 注解,则会出现编译时间错误。

ProtoAdapter

@ProtoAdapter 是无法直接注解的类或枚举的 marshalling adapter。

如果您将此注解用于:

  • 注解的类必须具有一个 @ProtoFactory 注解的 marshalled 类并为每个字段标注的 accessor 方法。这些方法可以是 instance 或 static 方法,其第一个参数必须是 marshalled 类。
  • Enums,目标枚举中必须存在相同的命名 enum 值。
ProtoName

@ProtoName 是一个可选注释,用于指定 Protobuf 消息或 enum 类型名称。它可用于类、记录和枚举。

ProtoEnumValue

@ProtoEnumValue 定义 Protobuf enum 值。您只能将此注解应用到 Java enum 的成员。

ProtoReserved 和 ProtoReservedStatements

@ProtoReserved@ProtoReservedStatements保留的 语句添加到生成的消息或枚举定义中,以防止以后使用数字、范围和名称。

ProtoTypeId

@ProtoTypeId (可选)为 Protobuf 消息或 enum 类型指定全局唯一的数字类型标识符。

注意

您不应该将此注解添加到类,因为 Data Grid 在内部使用,标识符可能会在不通知的情况下改变。

ProtoUnknownFieldSet

@ProtoUnknownFieldSet (可选)表示字段或类型为 {@link org.infinispan.protostream.UnknownFieldSet} 的 JavaBean 属性,它存储了任何未知字段。

注意

Data Grid 不推荐使用这个注解,因为它不再被 Google 支持,并可能在以后被删除。

其他注解

Data Grid 将类、字段和方法上的任何其他注解复制为生成的 Protobuf 模式中的注释。这包括索引注释,如 @Indexed@Basic

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 更有效且可靠的方法。

第 3 章 使用替代和自定义 marshaller 实现

Data Grid 建议您将基于 Protobuf 的 marshalling 与 ProtoStream marshaller 搭配使用,以便您可以利用 Ickle 查询并使用 Data Grid CLI 和控制台。但是,如果需要,您可以使用替代的 marshallers 或自定义 marshaller 实现。

3.1. 允许对 Java 类进行反序列化

为了安全起见,Data Grid 不允许反序列化任意 Java 类。如果您使用 JavaSerializationMarshallerGenericJBossMarshaller,您必须将 Java 类添加到反序列化允许列表中。

注意

反序列化允许列表应用到缓存管理器,因此您的 Java 类可以被所有缓存反序列化。

流程

  • 将 Java 类添加到 Data Grid 配置或系统属性中的 deserialization allow 列表中。

声明

<infinispan>
  <cache-container>
    <serialization version="1.0"
                   marshaller="org.infinispan.marshall.TestObjectStreamMarshaller">
      <allow-list>
        <class>org.infinispan.test.data.Person</class>
        <regex>org.infinispan.test.data.*</regex>
      </allow-list>
    </serialization>
  </cache-container>
</infinispan>

系统属性

// Specify a comma-separated list of fully qualified class names
-Dinfinispan.deserialization.allowlist.classes=java.time.Instant,com.myclass.Entity

// Specify a regular expression to match classes
-Dinfinispan.deserialization.allowlist.regexps=.*

3.2. 使用 JBoss Marshalling

JBoss Marshalling 是一个基于序列化的 marshalling 库,是之前的 Data Grid 版本中的默认 marshaller。

流程

  1. infinispan-jboss-marshalling 依赖项添加到您的 classpath。
  2. 将 Data Grid 配置为使用 GenericJBossMarshaller
  3. 将您的 Java 类添加到反序列化允许列表中。

声明

<serialization marshaller="org.infinispan.jboss.marshalling.commons.GenericJBossMarshaller">
  <allow-list>
    <class>org.infinispan.concrete.SomeClass</class>
    <regex>org.infinispan.example.*</regex>
  </allow-list>
</serialization>

programmatic

GlobalConfigurationBuilder builder = new GlobalConfigurationBuilder();
builder.serialization()
       .marshaller(new GenericJBossMarshaller())
       .allowList()
       .addRegexps("org.infinispan.example.", "org.infinispan.concrete.SomeClass");

其他资源

3.3. 使用 Java 序列化

您可以使用 Java 序列化与 Data Grid 进行 marshall 对象来实现 Java Serializable 接口。

提示

Java 序列化提供比 ProtoStream marshalling 更差的性能。只有在有严格的要求时才应使用 Java 序列化。

流程

  1. 配置数据网格以使用 JavaSerializationMarshaller
  2. 将您的 Java 类添加到反序列化允许列表中。

声明

<serialization marshaller="org.infinispan.commons.marshall.JavaSerializationMarshaller">
  <allow-list>
    <class>org.infinispan.concrete.SomeClass</class>
    <regex>org.infinispan.example.*</regex>
  </allow-list>
</serialization>

programmatic

GlobalConfigurationBuilder builder = new GlobalConfigurationBuilder();
builder.serialization()
       .marshaller(new JavaSerializationMarshaller())
       .allowList()
       .addRegexps("org.infinispan.example.", "org.infinispan.concrete.SomeClass");

3.4. 使用自定义 marshallers

Data Grid 提供了一个 Marshaller 接口,您可以为自定义 marshallers 实施。

提示

自定义 marshaller 实现可以通过 initialize () 方法访问配置的访问列表,该方法在启动时调用。

流程

  1. 实施 Marshaller 接口。
  2. 将 Data Grid 配置为使用您的 marshaller。
  3. 将您的 Java 类添加到反序列化允许列表中。

声明

<serialization marshaller="org.infinispan.example.marshall.CustomMarshaller">
  <allow-list>
    <class>org.infinispan.concrete.SomeClass</class>
    <regex>org.infinispan.example.*</regex>
  </allow-list>
</serialization>

programmatic

GlobalConfigurationBuilder builder = new GlobalConfigurationBuilder();
builder.serialization()
      .marshaller(new org.infinispan.example.marshall.CustomMarshaller())
      .allowList().addRegexp("org.infinispan.example.*");

第 4 章 数据转换

Data Grid 使用 transcoders 在由介质类型识别的各种编码间转换数据。

4.1. hot Rod DataFormat API

通过 Hot Rod 端点对远程缓存进行读写操作默认使用客户端 marshaller。热 Rod 为 Java 客户端提供了一个 DataFormat API,可用于使用不同的介质类型编码和/或 marshallers 执行缓存操作。

键和值的不同 marshallers

您可以在运行时覆盖键和值的 marshallers。

例如,要绕过 Hot Rod 客户端中的所有序列化,并读取远程缓存中存储的 byte[] 数组:

// Existing RemoteCache instance
RemoteCache<String, Pojo> remoteCache = ...

// IdentityMarshaller is a no-op marshaller
DataFormat rawKeyAndValues =
DataFormat.builder()
          .keyMarshaller(IdentityMarshaller.INSTANCE)
          .valueMarshaller(IdentityMarshaller.INSTANCE)
          .build();

// Creates a new instance of RemoteCache with the supplied DataFormat
RemoteCache<byte[], byte[]> rawResultsCache =
remoteCache.withDataFormat(rawKeyAndValues);
重要

对带有 keyMarshaller ()keyType () 方法的密钥使用不同的 marshallers 和数据格式可能会干扰客户端智能路由机制,从而导致 Data Grid 集群中的额外的网络跃点。如果性能至关重要,您应该对客户端和服务器上的密钥使用相同的编码。

读取不同编码中的数据

org.infinispan.commons.dataconversion.MediaType 指定的不同编码中请求并发送数据,如下所示:

// Existing remote cache using ProtostreamMarshaller
RemoteCache<String, Pojo> protobufCache = ...

// Request values returned as JSON
// Use the UTF8StringMarshaller to convert UTF-8 to String
DataFormat jsonString =
DataFormat.builder()
          .valueType(MediaType.APPLICATION_JSON)
          .valueMarshaller(new UTF8StringMarshaller())
          .build();
RemoteCache<byte[], byte[]> rawResultsCache =
protobufCache.withDataFormat(jsonString);

使用自定义值 marshallers

您可以使用自定义 marshallers 作为值,如以下示例中返回值 org.codehaus.jackson.JsonNode 对象。

在本例中,Data Grid 服务器处理数据转换,并在不支持指定介质类型时抛出异常。

DataFormat jsonNode =
DataFormat.builder()
          .valueType(MediaType.APPLICATION_JSON)
          .valueMarshaller(new CustomJacksonMarshaller()
          .build();

RemoteCache<String, JsonNode> jsonNodeCache =
remoteCache.withDataFormat(jsonNode);

返回值为 XML

以下代码片段返回值为 XML:

Object xmlValue = remoteCache
      .withDataFormat(DataFormat.builder()
      .valueType(MediaType.APPLICATION_XML)
      .valueMarshaller(new UTF8StringMarshaller())
      .build())
      .get(key);

例如,前面的 get (key) 调用返回值,例如:

<?xml version="1.0" ?><string>Hello!</string>

4.2. 使用嵌入式缓存按需转换数据

嵌入式缓存具有 application/x-java-object 的默认请求编码,以及与您为缓存配置的介质类型对应的存储编码。这意味着,Data Grid marshalls POJO 从应用程序到缓存的存储编码,然后将 POJO 返回给应用程序。在有些复杂的场景中,您可以使用 AdvancedCache API 将默认转换改为 POJO,或从 POJO 更改为其他编码。

以下示例使用 withMediaType () 方法根据需要返回作为 application/json 的值。

使用 MediaType 的高级缓存

DefaultCacheManager cacheManager = new DefaultCacheManager();

// Encode keys and values as Protobuf
ConfigurationBuilder cfg = new ConfigurationBuilder();
cfg.encoding().key().mediaType("application/x-protostream");
cfg.encoding().value().mediaType("application/x-protostream");

cacheManager.defineConfiguration("mycache", cfg.build());

Cache<Integer, Person> cache = cacheManager.getCache("mycache");

cache.put(1, new Person("John","Doe"));

// Use Protobuf for keys and JSON for values
Cache<Integer, byte[]> jsonValuesCache = (Cache<Integer, byte[]>) cache.getAdvancedCache().withMediaType("application/x-protostream", "application/json");

byte[] json = jsonValuesCache.get(1);

JSON 格式返回的值

{
   "_type":"org.infinispan.sample.Person",
   "name":"John",
   "surname":"Doe"
}

法律通告

Copyright © 2024 Red Hat, Inc.
The text of and illustrations in this document are licensed by Red Hat under a Creative Commons Attribution–Share Alike 3.0 Unported license ("CC-BY-SA"). An explanation of CC-BY-SA is available at http://creativecommons.org/licenses/by-sa/3.0/. In accordance with CC-BY-SA, if you distribute this document or an adaptation of it, you must provide the URL for the original version.
Red Hat, as the licensor of this document, waives the right to enforce, and agrees not to assert, Section 4d of CC-BY-SA to the fullest extent permitted by applicable law.
Red Hat, Red Hat Enterprise Linux, the Shadowman logo, the Red Hat logo, JBoss, OpenShift, Fedora, the Infinity logo, and RHCE are trademarks of Red Hat, Inc., registered in the United States and other countries.
Linux® is the registered trademark of Linus Torvalds in the United States and other countries.
Java® is a registered trademark of Oracle and/or its affiliates.
XFS® is a trademark of Silicon Graphics International Corp. or its subsidiaries in the United States and/or other countries.
MySQL® is a registered trademark of MySQL AB in the United States, the European Union and other countries.
Node.js® is an official trademark of Joyent. Red Hat is not formally related to or endorsed by the official Joyent Node.js open source or commercial project.
The OpenStack® Word Mark and OpenStack logo are either registered trademarks/service marks or trademarks/service marks of the OpenStack Foundation, in the United States and other countries and are used with the OpenStack Foundation's permission. We are not affiliated with, endorsed or sponsored by the OpenStack Foundation, or the OpenStack community.
All other trademarks are the property of their respective owners.
Red Hat logoGithubRedditYoutube

关于红帽文档

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

让开源更具包容性

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

關於紅帽

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

© 2024 Red Hat, Inc.