缓存编码和 Marshalling
对数据网格缓存和 marshall Java 对象进行编码
摘要
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 的实现,它带有对常用类型的原生支持,包括 String
和 Integer
。如果要在缓存中存储自定义类型,请使用 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 库来原生处理 String
和 byte[]
类型。如果您使用 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-8
或Key-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
的编码,则数据网格会抛出错误消息。
如果您使用 JavaSerializationMarshaller
或 GenericJBossMarshaller
,您应该分别使用 application/x-java-serialized-object
或 application/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 文件保留。
流程
- 如果 Data Grid Server 正在运行,则停止它。
将 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-core
和 infinispan-client-hotrod
依赖项中。您不需要任何配置将适配器类存储为键或值。
但是,如果要使用适配器类作为 ProtoStream-annotated POJO 中的 marshallable 字段,您可以使用以下方法这样做:
-
使用
ProtoSchema
注解的dependentOn
元素指定CommonTypesSchema
和CommonContainerTypesSchema
类。
@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 | 可选或必需的 | 描述 |
---|---|---|---|
| 整数 | 必填 | 标签号在类内必须是唯一的。 |
| 类型 | 选填 | 声明字段的 Protobuf 类型。如果没有指定类型,则会从 Java 属性中推断出它。
您可以使用 |
| 类 | 选填 | 如果属性 type 是接口或抽象类,则指示实际集合类型。 |
| 类 | 选填 | 如果属性 type 是抽象类或接口,则表示实际的 Java 类型。该值必须为属性类型分配可即时的 Java 类型。
如果使用 |
| 字符串 | 选填 | 指定 Protobuf 模式的名称。 |
| 字符串 | 选填 | 如果字段在从缓存中读取时不可用,则指定字段的默认值。该值必须遵循 Java 字段类型的正确语法。 |
ProtoFactory
@ProtoFactory
标记用于创建消息类实例的单个构造器或静态工厂方法。
您可以使用此注解来支持不可变消息类。使用 @ProtoField
注解的所有字段都必须包含在参数中。
-
@ProtoFactory
结构或方法的字段名称和参数必须与对应的 Protobuf 消息匹配,但顺序不重要。 -
如果您没有将
@ProtoFactory
注解的构造器添加到类,则该类必须具有默认的 no-argument 结构器,否则编译过程中发生错误。
ProtoSchema
@ProtoSchema
生成扩展 SerializationContextInitializer
的类或接口的实施。
如果激活,ProtoStream 处理器会在编译时在带有 Impl
后缀或您使用 className
参数指定的名称相同的软件包中生成实现。
includeClasses
或 basePackages
参数引用 ProtoStream 处理器应扫描并包含在 Protobuf 模式和 marshaller 中的类。如果您没有设置这些参数,ProtoStream 处理器会扫描整个源路径,这可能会导致意外的结果,我们不建议这样做。您还可以使用 excludeClasses
参数和 basePackages
参数来排除类。
schemaFileName
和 schemaPackageName
参数在此名称下注册生成的 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。
在高级别中,您应该执行以下操作来创建序列化上下文初始化器:
- 在您的 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 更有效且可靠的方法。
第 3 章 使用替代和自定义 marshaller 实现
Data Grid 建议您将基于 Protobuf 的 marshalling 与 ProtoStream marshaller 搭配使用,以便您可以利用 Ickle 查询并使用 Data Grid CLI 和控制台。但是,如果需要,您可以使用替代的 marshallers 或自定义 marshaller 实现。
3.1. 允许对 Java 类进行反序列化
为了安全起见,Data Grid 不允许反序列化任意 Java 类。如果您使用 JavaSerializationMarshaller
或 GenericJBossMarshaller
,您必须将 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。
流程
-
将
infinispan-jboss-marshalling
依赖项添加到您的 classpath。 -
将 Data Grid 配置为使用
GenericJBossMarshaller
。 - 将您的 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 序列化。
流程
-
配置数据网格以使用
JavaSerializationMarshaller
。 - 将您的 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 () 方法访问配置的访问列表,该方法在启动时调用。
流程
-
实施
Marshaller
接口。 - 将 Data Grid 配置为使用您的 marshaller。
- 将您的 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" }