© 2010-2019 原始作者。

您可以为自己使用或分发本文件,但前提是您不得为此类副本收取任何费用,并且每个副本(无论是印刷版还是电子版)都包含此版权声明。

前言

Pivotal GemFire 的 Spring Data 专注于将 Spring 框架强大的非侵入式编程模型和概念与 Pivotal GemFire 集成,以简化使用 Pivotal GemFire 作为数据管理解决方案时 Java 应用程序的配置和开发。

本文档假定您已经对核心 Spring 框架和 Pivotal GemFire 概念有基本了解和一些熟悉。

尽管已尽一切努力确保本文档全面、完整且无错误,但某些主题超出了本文档的范围,可能需要更多解释(例如,使用 HA 的分区进行数据分发管理,同时仍保持一致性)。此外,可能存在一些印刷错误。如果您发现错误,甚至更严重的错误,请通过在 JIRA 中提出相应问题,提请 Spring Data 团队注意这些问题。

1. 简介

Pivotal GemFire 的 Spring Data 参考指南解释了如何使用 Spring 框架配置和开发 Pivotal GemFire 应用程序。它介绍了基本概念并提供了大量示例,帮助您快速入门。

2. 要求

Pivotal GemFire 的 Spring Data 需要 Java 8.0、Spring Framework 5 和 Pivotal GemFire 9.8.2。

3. 新特性

从 1.2.0.RELEASE 开始,这个项目(以前称为 Spring GemFire)已更名为 Pivotal GemFire 的 Spring Data,以反映它现在是 Spring Data 项目的一个模块,并且基于 Pivotal GemFire 构建。

3.1. 1.2 版本的新特性

  • 通过 SDG gfe XML 命名空间完全支持 Pivotal GemFire 配置。现在可以完全配置 Pivotal GemFire 组件,而无需本地 cache.xml 文件。

  • 对 Pivotal GemFire 6.6.x 的 WAN Gateway 支持。请参阅 配置 WAN 网关

  • 使用专用 SDG XML 命名空间 gfe-data 支持 Spring Data 存储库。请参阅 Pivotal GemFire 的 Spring Data 存储库

  • gfe-data XML 命名空间支持注册 Pivotal GemFire 函数。请参阅 配置函数服务

  • 已将顶级 <disk-store> 元素添加到 SDG gfe XML 命名空间,以允许区域以及支持持久备份或溢出的其他 Pivotal GemFire 组件之间共享持久存储。请参阅 [bootstrap-diskstore]

    <*-region> 元素不再允许嵌套的 <disk-store> 元素。
  • Pivotal GemFire 子区域通过嵌套的 <*-region> 元素支持。

  • 已添加 <local-region> 元素来配置本地区域。

  • 支持 Pivotal GemFire 7.0 中重新设计的 WAN Gateway。

3.2. 1.3 版本的新特性

  • 升级到 Spring Framework 3.2.8。

  • 升级到 Spring Data Commons 1.7.1。

  • 对 Pivotal GemFire 函数的注解支持。现在可以通过使用注解来声明和注册编写为 POJO 的函数。此外,函数执行被定义为带注解的接口,类似于 Spring Data 存储库的工作方式。请参阅 函数执行的注解支持

  • 已将 <datasource> 元素添加到 SDG XML 命名空间,以简化与 Pivotal GemFire 数据网格建立基本客户端连接

  • 已将 <json-region-autoproxy> 元素添加到 SDG gfe-data XML 命名空间,以支持 Pivotal GemFire 7.0 中引入的 JSON 功能,从而使 Spring AOP 能够在区域数据访问操作上自动执行必要的转换。

  • 升级到 Pivotal GemFire 7.0.1 并添加了对新 AsyncEventQueue 属性的 XML 命名空间支持。

  • 添加了对设置区域订阅兴趣策略的支持。

  • 支持函数执行的 void 返回。有关完整详细信息,请参阅 函数执行的注解支持

  • 支持持久化本地区域。请参阅 本地区域

  • 支持 Pivotal GemFire 客户端缓存上的条目生存时间 (TTL) 和条目空闲时间 (TTI)。请参阅 配置 Pivotal GemFire ClientCache

  • 通过使用单个 Pivotal GemFire 集群,支持多个基于 Pivotal GemFire 的 Spring Data Web 应用程序在 tc Server 内部并发运行。

  • 通过使用 SDG gfe XML 命名空间,支持所有缓存区域定义上的 concurrency-checks-enabled。请参阅 [bootstrap:region:common:attributes]

  • 支持客户端本地区域上的 CacheLoadersCacheWriters

  • 支持在 Pivotal GemFire 缓存子区域上注册 CacheListenersAsyncEventQueuesGatewaySenders

  • 支持区域中的 PDX 持久密钥。

  • 在 Spring 上下文中指定 colocated-with 属性进行协同定位时,支持正确的 Partition Region bean 创建。

  • 使用 SDG gfe XML 命名空间中适当的嵌套 <*-region> 元素语法,完全支持缓存子区域。

3.3. 1.4 版本的新特性

  • 升级到 Pivotal GemFire 7.0.2。

  • 升级到 Spring Framework 3.2.13.RELEASE。

  • 升级到 Spring Data Commons 1.8.6.RELEASE。

  • 将 Pivotal GemFire 的 Spring Data 与 Spring Boot 集成,其中包括 spring-boot-starter-data-gemfire POM 和一个 Spring Boot 示例应用程序,该应用程序演示了使用 SDG 配置并使用 Spring Boot 引导的 Pivotal GemFire 缓存事务。

  • 添加了对从 Gfsh 启动时在 Pivotal GemFire 服务器中引导 Spring ApplicationContext 的支持。请参阅 在 Pivotal GemFire 中引导 Spring ApplicationContext

  • 添加了对将应用程序域对象和实体持久化到多个 Pivotal GemFire 缓存区域的支持。请参阅 实体映射

  • 添加了对将应用程序域对象和实体持久化到 Pivotal GemFire 缓存子区域的支持,避免了当子区域可唯一识别但名称相同时的冲突。请参阅 实体映射

  • 对所有 Pivotal GemFire 缓存区域类型的数据策略和区域快捷方式添加了严格的 XSD 类型规则。

  • 将 SDG <*-region> 元素的默认行为从查找更改为始终创建一个新区域,并提供了一个选项,可以使用 ignore-if-exists 属性恢复旧行为。请参阅 常用区域属性[bootstrap:region:common:regions-subregions-lookups-caution]

  • Pivotal GemFire 的 Spring Data 现在可以完全在 JDK 7 和 JDK 8 上构建和运行。

3.4. 1.5 版本的新特性

  • 与 Pivotal GemFire 7.0.2 保持兼容性。

  • 升级到 Spring Framework 4.0.9.RELEASE。

  • 升级到 Spring Data Commons 1.9.4.RELEASE。

  • 将参考指南转换为 Asciidoc。

  • 重新支持在 OSGi 容器中部署 Pivotal GemFire 的 Spring Data。

  • 删除了 Pivotal GemFire XML 命名空间区域类型元素中指定的所有默认值,转而依赖 Pivotal GemFire 的默认值。

  • 增加了自动创建 DiskStore 目录位置的便利性。

  • SDG 注解的函数实现现在可以从 Gfsh 执行。

  • 启用了手动启动 Pivotal GemFire GatewayReceivers

  • 添加了对自动区域查找的支持。请参阅 [bootstrap:region:auto-lookup]

  • 添加了对区域模板的支持。请参阅 [bootstrap:region:common:region-templates]

3.5. 1.6 版本的新特性

  • 升级到 Pivotal GemFire 8.0.0。

  • 与 Spring Framework 4.0.9.RELEASE 保持兼容性。

  • 升级到 Spring Data Commons 1.10.2.RELEASE。

  • 添加了对 Pivotal GemFire 8 新的基于集群的配置服务的支持。

  • 启用了在 Spring 配置的 Pivotal GemFire 服务器中采用“自动重新连接”功能。

  • 允许创建并发和并行的 AsyncEventQueuesGatewaySenders

  • 添加了对 Pivotal GemFire 8 区域数据压缩的支持。

  • 添加了用于设置 DiskStore 使用率临界和警告百分比的属性。

  • 支持向 GatewaySenders 添加 EventSubstitutionFilters 的功能。

3.6. 1.7 版本的新特性

  • 升级到 Pivotal GemFire 8.1.0。

  • 升级到 Spring Framework 4.1.9.RELEASE。

  • 升级到 Spring Data Commons 1.11.6.RELEASE。

  • 添加了对 Apache Geode 的早期访问支持。

  • 添加了对在 Spring XML、cache.xml 甚至 Pivotal GemFire 的集群配置服务中配置的现有区域上添加 Spring 定义的 CacheListenersCacheLoadersCacheWriters 的支持。

  • 添加了对 SpringContextBootstrappingInitializer 的 Spring JavaConfig 支持。

  • 添加了对 SpringContextBootstrappingInitializer 中自定义 ClassLoaders 的支持,以加载 Spring 定义的 bean 类。

  • 添加了对 LazyWiringDeclarableSupport 重新初始化和 WiringDeclarableSupport 的完全替换的支持。

  • <gfe:pool> 元素中添加了 locatorsservers 属性,允许使用 Spring 的属性占位符配置可变定位器和服务器端点列表。

  • 启用了在非 Spring 配置的 Pivotal GemFire 服务器上使用 <gfe-data:datasource> 元素。

  • 添加了多索引定义和创建支持。

  • 基于注解的数据过期

  • [gemfire-repositories:oql-extensions]

  • 添加了对缓存和区域数据快照的支持。请参阅 配置快照服务

3.7. 1.8 版本的新特性

  • 升级到 Pivotal GemFire 8.2.0。

  • 升级到 Spring Framework 4.2.9.RELEASE。

  • 升级到 Spring Data Commons 1.12.11.RELEASE。

  • 添加了 Maven POM 以使用 Maven 构建 SDG。

  • 添加了对 CDI 的支持。

  • 启用了在没有 Pool 的情况下配置 ClientCache

  • <gfe:cache><gfe:client-cache> 元素的 use-bean-factory-locator 属性默认为 false

  • <gfe:client-cache> 中添加了 durable-client-iddurable-client-timeout 属性。

  • 使 GemfirePersistentProperty 现在能正确处理其他非实体、类似标量的类型(例如 BigDecimalBigInteger)。

  • 防止 SDG 定义的 Pools 在使用这些 PoolsRegions 之前被销毁。

  • 处理了定义为存储库查询方法的 Pivotal GemFire OQL 查询不区分大小写的情况。

  • 将 SDG Spring 缓存抽象支持中的 GemFireCache.evict(key) 更改为调用 Region.remove(key)

  • 修复了在客户端 Region 上进行存储库查询时出现的 RegionNotFoundException,该 Region 与为 Pivotal GemFire 服务器组配置的特定 Pool 相关联。

  • GatewaySenders/Receivers 不再与 Spring 容器绑定。

3.8. 1.9 版本的新特性

  • 升级到 Pivotal GemFire 8.2.11。

  • 升级到 Spring Framework 4.3.18.RELEASE。

  • 升级到 Spring Data Commons 1.13.13.RELEASE。

  • 引入了一个全新的、受 Spring Boot 启发的基于注解的配置模型。

  • GemfireTransactionManager 中添加了对暂停和恢复的支持。

  • 在存储库中添加了支持,当不存在 @Id 注解时,使用 bean id 属性作为区域键。

  • 当使用 @EnablePdx 时,将 MappingPdxSerializer 用作默认的 Pivotal GemFire 序列化策略。

  • 启用了 GemfireCacheManager 显式列出要在 Spring 缓存抽象中使用的区域名称。

  • 配置了 Pivotal GemFire 缓存、CacheServers、Locators、Pools、Regions、Indexes、DiskStores、Expiration、Eviction、Statistics、Mcast、HttpService、Auth、SSL、Logging、System Properties。

  • 在类路径上添加了多个 Spring Data 模块的存储库支持。

3.9. 2.0 版本的新特性

  • 升级到 Pivotal GemFire 9.1.1。

  • 升级到 Spring Data Commons 2.0.8.RELEASE。

  • 升级到 Spring Framework 5.0.7.RELEASE。

  • 通过按关注点打包不同的类和组件来重组 SDG 代码库。

  • 为 Java 8 类型添加了广泛的支持,尤其是在 SD 存储库抽象中。

  • 更改了存储库接口和抽象,例如,ID 不再需要是 java.io.Serializable

  • @EnableEntityDefinedRegions 注解的 ignoreIfExists 属性默认设置为 true

  • @Indexed 注解的 override 属性默认设置为 false

  • @EnableIndexes 重命名为 @EnableIndexing

  • 引入了 InterestsBuilder 类,以便在使用 JavaConfig 时轻松方便地表达客户端和服务器之间对键和值的兴趣。

  • 在注解配置模型中添加了对堆外内存、Redis 适配器和 Pivotal GemFire 新安全框架的支持。

3.10. 2.1 版本的新特性

  • 升级到 Pivotal GemFire 9.8.2。

  • 升级到 Spring Framework 5.1.0.RELEASE。

  • 升级到 Spring Data Commons 2.1.0.RELEASE。

  • 添加了对并行缓存/区域快照的支持,以及在加载快照时调用回调函数。

  • 添加了对注册 QueryPostProcessors 的支持,以自定义从存储库查询方法生成的 OQL。

  • 添加了对 o.s.d.g.mapping.MappingPdxSerializer 中包含/排除 TypeFilters 的支持。

  • 更新了文档。

参考指南

4. 文档结构

以下章节解释了 Pivotal GemFire 的 Spring Data 提供的核心功能

  • 使用 Spring 容器引导 Pivotal GemFire 描述了为配置、初始化和访问 Pivotal GemFire 缓存、区域和相关分布式系统组件提供的配置支持。

  • 使用 Pivotal GemFire API 解释了 Pivotal GemFire API 与 Spring 中可用的各种数据访问功能之间的集成,例如基于模板的数据访问、异常转换、事务管理和缓存。

  • 使用 Pivotal GemFire 序列化 描述了 Pivotal GemFire 对托管对象的序列化和反序列化的增强功能。

  • POJO 映射 描述了使用 Spring Data 存储在 Pivotal GemFire 中的 POJO 的持久化映射。

  • Pivotal GemFire 的 Spring Data 存储库 描述了如何创建和使用 Spring Data 存储库,通过基本的 CRUD 和简单的查询操作访问存储在 Pivotal GemFire 中的数据。

  • 函数执行的注解支持 描述了如何使用注解创建和使用 Pivotal GemFire 函数,以在数据所在的位置执行分布式计算。

  • 连续查询 (CQ) 描述了如何使用 Pivotal GemFire 的连续查询 (CQ) 功能,根据 Pivotal GemFire 的 OQL(对象查询语言)定义和注册的兴趣来处理事件流。

  • 在 Pivotal GemFire 中引导 Spring ApplicationContext 描述了如何使用 Gfsh 配置和引导在 Pivotal GemFire 服务器中运行的 Spring ApplicationContext

  • 示例应用程序 描述了分发中提供的示例,以说明 Pivotal GemFire 的 Spring Data 中可用的各种功能。

5. 使用 Spring 容器引导 Pivotal GemFire

Pivotal GemFire 的 Spring Data 使用 Spring IoC 容器提供 Pivotal GemFire 内存数据网格 (IMDG) 的完整配置和初始化。该框架包含几个类,以帮助简化 Pivotal GemFire 组件的配置,包括:缓存、区域、索引、磁盘存储、函数、WAN 网关、持久化备份以及其他几个分布式系统组件,以最少的努力支持各种应用程序用例。

本节假定您对 Pivotal GemFire 有基本了解。有关更多信息,请参阅 Pivotal GemFire 产品文档

5.1. 使用 Spring 优于 Pivotal GemFire cache.xml 的优点

Pivotal GemFire 的 Spring Data 的 XML 命名空间支持 Pivotal GemFire 内存数据网格 (IMDG) 的完整配置。XML 命名空间是 Spring 上下文中配置 Pivotal GemFire 的两种方法之一,以便在 Spring 容器中正确管理 Pivotal GemFire 的生命周期。在 Spring 上下文中配置 Pivotal GemFire 的另一种方法是使用 基于注解的配置

虽然出于遗留原因仍支持 Pivotal GemFire 的原生 cache.xml,但鼓励使用 XML 配置的 Pivotal GemFire 应用程序开发人员在 Spring XML 中完成所有操作,以利用 Spring 提供的许多出色功能,例如模块化 XML 配置、属性占位符和覆盖、SpEL(Spring 表达式语言)和环境配置文件。在 XML 命名空间背后,Pivotal GemFire 的 Spring Data 大量使用 Spring 的 FactoryBean 模式来简化 Pivotal GemFire 组件的创建、配置和初始化。

Pivotal GemFire 提供了几个回调接口,例如 CacheListenerCacheLoaderCacheWriter,允许开发人员添加自定义事件处理程序。使用 Spring 的 IoC 容器,您可以将这些回调配置为正常的 Spring bean 并将其注入到 Pivotal GemFire 组件中。与原生 cache.xml 相比,这是一个显著的改进,原生 cache.xml 提供了相对有限的配置选项,并要求回调实现 Pivotal GemFire 的 Declarable 接口(请参阅 连线 Declarable 组件,了解如何在 Spring 容器中使用 Declarables)。

此外,IDE(例如 Spring Tool Suite (STS))为 Spring XML 命名空间提供了出色的支持,包括代码完成、弹出注解和实时验证。

5.2. 使用核心命名空间

为了简化配置,Pivotal GemFire 的 Spring Data 为配置核心 Pivotal GemFire 组件提供了专用的 XML 命名空间。可以通过使用 Spring 的标准 <bean> 定义直接配置 bean。但是,所有 bean 属性都通过 XML 命名空间公开,因此使用原始 bean 定义几乎没有好处。

有关 Spring 中基于 XML Schema 的配置的更多信息,请参阅 Spring Framework 参考文档中的附录
Spring Data Repository 支持使用单独的 XML 命名空间。有关如何配置 Pivotal GemFire 的 Spring Data Repository 的更多信息,请参阅 Pivotal GemFire 的 Spring Data Repository

要使用 Pivotal GemFire 的 Spring Data XML 命名空间,请在您的 Spring XML 配置元数据中声明它,如以下示例所示

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:gfe="https://www.springframework.org/schema/gemfire" (1)(2)
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
    http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
    https://www.springframework.org/schema/gemfire https://www.springframework.org/schema/gemfire/spring-gemfire.xsd (3)
">

  <bean id ... >

  <gfe:cache ...> (4)

</beans>
1 Pivotal GemFire 的 Spring Data XML 命名空间前缀。任何名称都可以,但在整个参考文档中都使用 gfe
2 XML 命名空间前缀映射到 URI。
3 XML 命名空间 URI 位置。请注意,即使该位置指向一个外部地址(它确实存在且有效),Spring 也会在本地解析模式,因为它包含在 Pivotal GemFire 的 Spring Data 库中。
4 使用带 gfe 前缀的 XML 命名空间声明示例。

您可以将默认命名空间从 beans 更改为 gfe。这对于主要由 Pivotal GemFire 组件组成的 XML 配置非常有用,因为它避免了声明前缀。为此,请交换前面所示的命名空间前缀声明,如以下示例所示

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/gemfire" (1)
       xmlns:beans="http://www.springframework.org/schema/beans" (2)
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
    http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
    https://www.springframework.org/schema/gemfire https://www.springframework.org/schema/gemfire/spring-gemfire.xsd
">

  <beans:bean id ... > (3)

  <cache ...> (4)

</beans>
1 此 XML 文档的默认命名空间声明指向 Pivotal GemFire 的 Spring Data XML 命名空间。
2 Spring 原始 bean 定义的 beans 命名空间前缀声明。
3 使用 beans 命名空间的 bean 声明。注意前缀。
4 使用 gfe 命名空间的 bean 声明。注意由于 gfe 是默认命名空间而没有前缀。

5.3. 使用数据访问命名空间

除了核心 XML 命名空间 (gfe) 之外,Pivotal GemFire 的 Spring Data 还提供了一个数据访问 XML 命名空间 (gfe-data),主要旨在简化 Pivotal GemFire 客户端应用程序的开发。此命名空间目前包含对 Pivotal GemFire 存储库和函数执行的支持,以及一个 <datasource> 标签,它提供了一种连接到 Pivotal GemFire 集群的便捷方式。

5.3.1. 连接到 Pivotal GemFire 的简单方法

对于许多应用程序来说,使用默认值与 Pivotal GemFire 数据网格的基本连接就足够了。Pivotal GemFire 的 Spring Data 的 <datasource> 标签提供了一种简单的数据访问方式。数据源会创建一个 ClientCache 和连接 Pool。此外,它会查询集群服务器以获取所有现有根区域,并为每个区域创建一个(空)客户端区域代理。

<gfe-data:datasource>
  <locator host="remotehost" port="1234"/>
</gfe-data:datasource>

<datasource> 标签在语法上与 <gfe:pool> 相似。它可以配置一个或多个嵌套的 locatorserver 元素来连接到现有数据网格。此外,所有可用于配置 Pool 的属性都受支持。此配置会自动为连接到 Locator 的集群成员上定义的每个区域创建客户端区域 bean,因此它们可以被 Spring Data 映射注解 (GemfireTemplate) 无缝引用并自动注入到应用程序类中。

当然,您可以显式配置客户端区域。例如,如果您想在本地内存中缓存数据,如以下示例所示

<gfe-data:datasource>
  <locator host="remotehost" port="1234"/>
</gfe-data:datasource>

<gfe:client-region id="Example" shortcut="CACHING_PROXY"/>

5.4. 配置缓存

要使用 Pivotal GemFire,您需要创建新缓存或连接到现有缓存。在当前版本的 Pivotal GemFire 中,每个 VM(更严格地说,每个 ClassLoader)只能打开一个缓存。在大多数情况下,缓存应该只创建一次。

本节描述对等 Cache 成员的创建和配置,适用于对等 (P2P) 拓扑和缓存服务器。Cache 成员也可以用于独立应用程序和集成测试。但是,在典型的生产系统中,大多数应用程序进程充当缓存客户端,而是创建 ClientCache 实例。这在配置 Pivotal GemFire ClientCache客户端区域部分中进行了描述。

可以使用以下简单声明创建具有默认配置的对等 Cache

<gfe:cache/>

在 Spring 容器初始化期间,任何包含此缓存定义的 ApplicationContext 都会注册一个 CacheFactoryBean,该工厂 bean 创建一个名为 gemfireCache 的 Spring bean,该 bean 引用一个 Pivotal GemFire Cache 实例。此 bean 引用现有 Cache,或者,如果不存在,则引用新创建的 Cache。由于未指定附加属性,因此新创建的 Cache 应用默认缓存配置。

所有依赖于 Cache 的 Pivotal GemFire 的 Spring Data 组件都遵循此命名约定,因此您无需显式声明 Cache 依赖项。如果您愿意,可以通过使用各种 SDG XML 命名空间元素提供的 cache-ref 属性使依赖项显式化,如下所示,您可以使用 id 属性覆盖缓存的 bean 名称

<gfe:cache id="myCache"/>

Pivotal GemFire Cache 可以使用 Spring 完全配置。但是,Pivotal GemFire 的原生 XML 配置文件 cache.xml 也受支持。对于需要原生配置 Pivotal GemFire 缓存的情况,您可以通过使用 cache-xml-location 属性提供对 Pivotal GemFire XML 配置文件的引用,如下所示

<gfe:cache id="cacheConfiguredWithNativeCacheXml" cache-xml-location="classpath:cache.xml"/>

在此示例中,如果需要创建缓存,它将使用位于类路径根目录下的名为 cache.xml 的文件进行配置。

该配置利用 Spring 的 Resource 抽象来定位文件。Resource 抽象允许使用各种搜索模式,具体取决于运行时环境或资源位置中指定的(如果有)前缀。

除了引用外部 XML 配置文件之外,您还可以指定使用 Spring Properties 支持功能的任何 Pivotal GemFire System 属性

例如,您可以使用 util 命名空间中定义的 properties 元素直接定义 Properties 或从属性文件中加载属性,如下所示

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:gfe="https://www.springframework.org/schema/gemfire"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
    http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
    https://www.springframework.org/schema/gemfire https://www.springframework.org/schema/gemfire/spring-gemfire.xsd
    http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd
">

  <util:properties id="gemfireProperties" location="file:/path/to/gemfire.properties"/>

  <gfe:cache properties-ref="gemfireProperties"/>

</beans>

建议使用属性文件将特定于环境的设置外部化到应用程序配置之外。

缓存设置仅在需要创建新缓存时才适用。如果 VM 中已存在打开的缓存,则这些设置将被忽略。

5.4.1. 高级缓存配置

对于高级缓存配置,cache 元素提供了许多配置选项,这些选项作为属性或子元素公开,如以下列表所示

(1)
<gfe:cache
    cache-xml-location=".."
    properties-ref=".."
    close="false"
    copy-on-read="true"
    critical-heap-percentage="90"
    eviction-heap-percentage="70"
    enable-auto-reconnect="false" (2)
    lock-lease="120"
    lock-timeout="60"
    message-sync-interval="1"
    pdx-serializer-ref="myPdxSerializer"
    pdx-persistent="true"
    pdx-disk-store="diskStore"
    pdx-read-serialized="false"
    pdx-ignore-unread-fields="true"
    search-timeout="300"
    use-bean-factory-locator="true" (3)
    use-cluster-configuration="false" (4)
>

  <gfe:transaction-listener ref="myTransactionListener"/> (5)

  <gfe:transaction-writer> (6)
    <bean class="org.example.app.gemfire.transaction.TransactionWriter"/>
  </gfe:transaction-writer>

  <gfe:gateway-conflict-resolver ref="myGatewayConflictResolver"/> (7)

  <gfe:dynamic-region-factory/> (8)

  <gfe:jndi-binding jndi-name="myDataSource" type="ManagedDataSource"/> (9)

</gfe:cache>
1 属性支持各种缓存选项。有关此示例中所示的任何内容的更多信息,请参阅 Pivotal GemFire 产品文档close 属性确定在关闭 Spring 应用程序上下文时是否应关闭缓存。默认值为 true。但是,对于多个应用程序上下文使用缓存的用例(在 Web 应用程序中很常见),请将此值设置为 false
2 enable-auto-reconnect 属性设置为 true(默认为 false)允许断开连接的 Pivotal GemFire 成员自动重新连接并重新加入 Pivotal GemFire 集群。有关更多详细信息,请参阅 Pivotal GemFire 产品文档
3 use-bean-factory-locator 属性设置为 true(默认为 false)仅在同时使用 Spring (XML) 配置元数据和 Pivotal GemFire cache.xml 配置 Pivotal GemFire 缓存节点(无论是客户端还是对等节点)时适用。此选项允许 cache.xml 中表达的 Pivotal GemFire 组件(例如 CacheLoader)与 Spring 应用程序上下文中定义的 bean(例如 DataSource)自动连接。此选项通常与 cache-xml-location 结合使用。
4 use-cluster-configuration 属性设置为 true(默认为 false)使 Pivotal GemFire 成员能够从 Locator 检索通用、共享的基于集群的配置。有关更多详细信息,请参阅 Pivotal GemFire 产品文档
5 使用 bean 引用的 TransactionListener 回调声明示例。引用的 bean 必须实现 TransactionListener。可以实现 TransactionListener 来处理事务相关事件(例如 afterCommit 和 afterRollback)。
6 使用内部 bean 声明的 TransactionWriter 回调声明示例。该 bean 必须实现 TransactionWriterTransactionWriter 是一个可以否决事务的回调。
7 使用 bean 引用的 GatewayConflictResolver 回调声明示例。引用的 bean 必须实现 https://gemfire-98-javadocs.docs.pivotal.io//org/apache/geode/cache/util/GatewayConflictResolver.html [GatewayConflictResolver]。GatewayConflictResolver 是一个 Cache 级别的插件,用于决定如何处理源自其他系统并通过 WAN Gateway 到达的事件。
8 启用 Pivotal GemFire 的 DynamicRegionFactory,它提供分布式区域创建服务。
9 声明一个 JNDI 绑定,将外部 DataSource 加入到 Pivotal GemFire 事务中。
启用 PDX 序列化

前面的示例包含许多与 Pivotal GemFire 增强型序列化框架 PDX 相关的属性。虽然对 PDX 的完整讨论超出了本参考指南的范围,但需要注意的是,PDX 是通过注册一个 PdxSerializer 来启用的,该序列化器通过设置 pdx-serializer 属性来指定。

Pivotal GemFire 提供了一个使用 Java 反射的实现类 (org.apache.geode.pdx.ReflectionBasedAutoSerializer)。然而,开发人员通常会提供自己的实现。属性的值只是对实现 PdxSerializer 接口的 Spring bean 的引用。

有关序列化支持的更多信息,请参阅 使用 Pivotal GemFire 序列化

启用自动重新连接

设置 <gfe:cache enable-auto-reconnect="[true|false*]> 属性为 true 时应谨慎。

通常,只有在使用 Pivotal GemFire 的 Spring Data XML 命名空间配置和引导添加到集群的新非应用程序 Pivotal GemFire 服务器时,才应启用“自动重新连接”。换句话说,当 Pivotal GemFire 的 Spring Data 用于开发和构建碰巧也是 Pivotal GemFire 集群的对等 Cache 成员的 Pivotal GemFire 应用程序时,不应启用“自动重新连接”。

此限制的主要原因是,大多数 Pivotal GemFire 应用程序使用对 Pivotal GemFire Cache 或区域的引用来执行数据访问操作。这些引用由 Spring 容器“注入”到应用程序组件(例如存储库)中,供应用程序使用。当对等成员被强制从集群的其余部分断开连接时,可能是因为对等成员变得无响应或网络分区将一个或多个对等成员分离成一个太小而无法作为独立分布式系统运行的组,对等成员会关闭,并且所有 Pivotal GemFire 组件引用(缓存、区域等)都变得无效。

本质上,每个对等成员中当前的强制断开处理逻辑会从头开始拆除系统。JGroups 堆栈关闭,分布式系统进入关闭状态,最后,缓存关闭。实际上,所有内存引用都变得陈旧并丢失。

与分布式系统断开连接后,对等成员进入“重新连接”状态,并定期尝试重新加入分布式系统。如果对等成员成功重新连接,该成员将从现有成员重建其分布式系统“视图”,并接收新的分布式系统 ID。此外,所有缓存、区域和其他 Pivotal GemFire 组件都将重新构建。因此,所有旧引用(可能已由 Spring 容器注入到应用程序中)现在都已陈旧且不再有效。

Pivotal GemFire 不保证(即使使用 Pivotal GemFire 公共 Java API)应用程序缓存、区域或其他组件引用会通过重新连接操作自动刷新。因此,Pivotal GemFire 应用程序必须注意刷新自己的引用。

不幸的是,也没有办法收到断开连接事件,也没有办法收到重新连接事件。如果是这样,就会有一种干净的方法来知道何时调用 ConfigurableApplicationContext.refresh(),即使它适用于应用程序这样做,这就是为什么 Pivotal GemFire 的这个“功能”不建议用于对等 Cache 应用程序的原因。

有关“自动重新连接”的更多信息,请参阅 Pivotal GemFire 的 产品文档

使用基于集群的配置

Pivotal GemFire 的集群配置服务是一种便捷的方式,任何加入集群的对等成员都可以通过使用 Locator 维护的共享持久配置获得集群的“一致视图”。使用基于集群的配置可确保对等成员的配置在成员加入时与 Pivotal GemFire 分布式系统兼容。

Pivotal GemFire 的 Spring Data 的此功能(将 use-cluster-configuration 属性设置为 true)的工作方式与 cache-xml-location 属性相同,只是 Pivotal GemFire 配置元数据的来源是通过 Locator 从网络获取,而不是驻留在本地文件系统中的原生 cache.xml 文件。

所有 Pivotal GemFire 本机配置元数据,无论是来自 cache.xml 还是来自集群配置服务,都将在任何 Spring (XML) 配置元数据之前应用。因此,Spring 的配置旨在“增强”本机 Pivotal GemFire 配置元数据,并且最有可能特定于应用程序。

再次,要启用此功能,请在 Spring XML 配置中指定以下内容

<gfe:cache use-cluster-configuration="true"/>
虽然某些 Pivotal GemFire 工具(例如 Gfsh)在进行类似模式的更改时会“记录”其操作(例如,gfsh>create region --name=Example --type=PARTITION),但 Pivotal GemFire 的 Spring Data 的配置元数据不会被记录。直接使用 Pivotal GemFire 的公共 Java API 时也是如此。它也不会被记录。

有关 Pivotal GemFire 集群配置服务的更多信息,请参阅产品文档

5.4.2. 配置 Pivotal GemFire CacheServer

Pivotal GemFire 的 Spring Data 包含对配置 CacheServer 的专用支持,允许通过 Spring 容器进行完整配置,如以下示例所示

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:gfe="https://www.springframework.org/schema/gemfire"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
    http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
    https://www.springframework.org/schema/gemfire https://www.springframework.org/schema/gemfire/spring-gemfire.xsd
">

  <gfe:cache/>

  <!-- Example depicting serveral Pivotal GemFire CacheServer configuration options -->
  <gfe:cache-server id="advanced-config" auto-startup="true"
       bind-address="localhost" host-name-for-clients="localhost" port="${gemfire.cache.server.port}"
       load-poll-interval="2000" max-connections="22" max-message-count="1000" max-threads="16"
       max-time-between-pings="30000" groups="test-server">

    <gfe:subscription-config eviction-type="ENTRY" capacity="1000" disk-store="file://${java.io.tmpdir}"/>

  </gfe:cache-server>

  <context:property-placeholder location="classpath:cache-server.properties"/>

</beans>

前面的配置显示了 cache-server 元素和许多可用选项。

此配置不硬编码端口,而是使用 Spring 的 context 命名空间来声明 property-placeholder属性占位符 读取一个或多个属性文件,然后在运行时用值替换属性占位符。这样做允许管理员更改值而无需接触主应用程序配置。Spring 还提供了 SpEL 和一个 环境抽象 来支持将特定于环境的属性从主代码库中外部化,从而简化了跨多台机器的部署。
为避免初始化问题,Pivotal GemFire 的 Spring Data 启动的 CacheServer 在 Spring 容器完全初始化后才启动。这样做允许声明式定义的潜在区域、监听器、写入器或实例化器在服务器开始接受连接之前完全初始化和注册。在以编程方式配置这些元素时请记住这一点,因为服务器可能在您的组件之前启动,因此不会立即被连接的客户端看到。

5.4.3. 配置 Pivotal GemFire ClientCache

除了定义 Pivotal GemFire 对等 Cache 之外,Pivotal GemFire 的 Spring Data 还支持在 Spring 容器中定义 Pivotal GemFire ClientCacheClientCache 定义在配置和使用上与 Pivotal GemFire 对等 缓存 相似,并受 org.springframework.data.gemfire.client.ClientCacheFactoryBean 支持。

使用默认配置的 Pivotal GemFire 缓存客户端的最简单定义如下

<beans>
  <gfe:client-cache/>
</beans>

client-cache 支持许多与 缓存 元素相同的选项。但是,与功能完备的对等 Cache 成员不同,缓存客户端通过一个池连接到远程缓存服务器。默认情况下,会创建一个池来连接到在 localhost 上运行并监听端口 40404 的服务器。除非区域配置为使用特定池,否则所有客户端区域都使用默认池。

可以使用 pool 元素定义池。此客户端池可用于配置直接连接到服务器的单个实体,或通过一个或多个 Locator 连接到整个缓存。

例如,要自定义 client-cache 使用的默认池,开发人员需要定义一个池并将其连接到缓存定义,如以下示例所示

<beans>
  <gfe:client-cache id="myCache" pool-name="myPool"/>

  <gfe:pool id="myPool" subscription-enabled="true">
    <gfe:locator host="${gemfire.locator.host}" port="${gemfire.locator.port}"/>
  </gfe:pool>
</beans>

<client-cache> 元素还有一个 ready-for-events 属性。如果该属性设置为 true,则客户端缓存初始化将包含对 ClientCache.readyForEvents() 的调用。

客户端区域 更详细地介绍了客户端配置。

Pivotal GemFire 的 DEFAULT 池和 Pivotal GemFire 的 Spring Data 池定义

如果 Pivotal GemFire ClientCache 仅为本地,则不需要池定义。例如,您可以定义以下内容

<gfe:client-cache/>

<gfe:client-region id="Example" shortcut="LOCAL"/>

在这种情况下,“示例”区域是 LOCAL,并且客户端和服务器之间不分发数据。因此,不需要池。这对于任何客户端的本地区域都是如此,如 Pivotal GemFire 的 ClientRegionShortcut(所有 LOCAL_* 快捷方式)所定义。

但是,如果客户端区域是服务器端区域的(缓存)代理,则需要一个池。在这种情况下,有几种定义和使用池的方法。

ClientCache、一个池和一个基于代理的区域都已定义但未明确标识时,Pivotal GemFire 的 Spring Data 会自动解析引用,如以下示例所示

<gfe:client-cache/>

<gfe:pool>
  <gfe:locator host="${geode.locator.host}" port="${geode.locator.port}"/>
</gfe:pool>

<gfe:client-region id="Example" shortcut="PROXY"/>

在前面的示例中,ClientCache 被标识为 gemfireCache,Pool 被标识为 gemfirePool,客户端区域被标识为“Example”。然而,ClientCachegemfirePool 初始化 Pivotal GemFire 的 DEFAULT Pool,并且客户端区域在客户端和服务器之间分发数据时使用 gemfirePool

基本上,Pivotal GemFire 的 Spring Data 会将前面的配置解析为以下内容

<gfe:client-cache id="gemfireCache" pool-name="gemfirePool"/>

<gfe:pool id="gemfirePool">
  <gfe:locator host="${geode.locator.host}" port="${geode.locator.port}"/>
</gfe:pool>

<gfe:client-region id="Example" cache-ref="gemfireCache" pool-name="gemfirePool" shortcut="PROXY"/>

Pivotal GemFire 仍然会创建一个名为 DEFAULT 的池。Pivotal GemFire 的 Spring Data 会导致 DEFAULT 池从 gemfirePool 初始化。这在定义了多个池且客户端区域使用单独的池,或者根本不声明池的情况下非常有用。

考虑以下情况

<gfe:client-cache pool-name="locatorPool"/>

<gfe:pool id="locatorPool">
  <gfe:locator host="${geode.locator.host}" port="${geode.locator.port}"/>
</gfe:pool>

<gfe:pool id="serverPool">
  <gfe:server host="${geode.server.host}" port="${geode.server.port}"/>
</gfe:pool>

<gfe:client-region id="Example" pool-name="serverPool" shortcut="PROXY"/>

<gfe:client-region id="AnotherExample" shortcut="CACHING_PROXY"/>

<gfe:client-region id="YetAnotherExample" shortcut="LOCAL"/>

在此设置中,Pivotal GemFire client-cache DEFAULT 池从 locatorPool 初始化,如 pool-name 属性所指定。没有 Spring Data for Pivotal GemFire 定义的 gemfirePool,因为这两个池都被显式标识(命名)——分别是 locatorPoolserverPool

“示例”区域明确引用并专门使用 serverPoolAnotherExample 区域使用 Pivotal GemFire 的 DEFAULT 池,该池再次根据客户端缓存 bean 定义的 pool-name 属性从 locatorPool 配置。

最后,YetAnotherExample 区域不使用池,因为它是一个 LOCAL 区域。

AnotherExample 区域将首先查找名为 gemfirePool 的池 bean,但这需要定义一个匿名池 bean(即 <gfe:pool/>)或一个显式命名为 gemfirePool 的池 bean(例如,<gfe:pool id="gemfirePool"/>)。
如果我们将 locatorPool 的名称更改为 gemfirePool 或将池 bean 定义设为匿名,其效果与前面的配置相同。

5.5. 配置区域

需要一个区域来存储和检索缓存中的数据。org.apache.geode.cache.Region 是一个扩展 java.util.Map 的接口,允许使用熟悉的键值语义进行基本数据访问。Region 接口被连接到需要它的应用程序类,因此实际的区域类型与编程模型解耦。通常,每个区域都与一个域对象关联,类似于关系数据库中的表。

Pivotal GemFire 实现以下类型的区域

  • REPLICATE - 数据在集群中定义该区域的所有缓存成员之间复制。这提供了非常高的读取性能,但写入执行复制需要更长时间。

  • PARTITION - 数据在集群中定义该区域的许多缓存成员之间分区(分片)到存储桶中。这提供了高读写性能,适用于单个节点过大的大型数据集。

  • LOCAL - 数据仅存在于本地节点。

  • Client - 从技术上讲,客户端区域是一个 LOCAL 区域,它充当集群中缓存服务器上托管的 REPLICATE 或 PARTITION 区域的 PROXY。它可以保存本地创建或获取的数据。或者,它可以是空的。本地更新与缓存服务器同步。此外,客户端区域可以订阅事件以保持与访问同一服务器区域的远程进程引起的更改保持最新(同步)。

有关各种区域类型及其功能以及配置选项的更多信息,请参阅 Pivotal GemFire 关于 区域类型 的文档。

5.5.1. 使用外部配置的区域

要引用已在 Pivotal GemFire 原生 cache.xml 文件中配置的区域,请使用 lookup-region 元素。只需使用 name 属性声明目标区域名称。例如,要为名为 Orders 的现有区域声明一个标识为 ordersRegion 的 bean 定义,您可以使用以下 bean 定义

<gfe:lookup-region id="ordersRegion" name="Orders"/>

如果未指定 name,则 bean 的 id 将用作区域的名称。上面的示例变为

<!-- lookup for a Region called 'Orders' -->
<gfe:lookup-region id="Orders"/>
如果区域不存在,将抛出初始化异常。要配置新区域,请继续阅读下面的相应部分。

在前面的示例中,由于未显式定义缓存名称,因此使用了默认命名约定 (gemfireCache)。或者,可以使用 cache-ref 属性引用缓存 bean

<gfe:cache id="myCache"/>
<gfe:lookup-region id="ordersRegion" name="Orders" cache-ref="myCache"/>

lookup-region 允许您检索现有、预配置的区域,而无需公开区域语义或设置基础设施。

5.5.2. 自动区域查找

当您在 <gfe:cache> 元素上使用 cache-xml-location 属性时,auto-region-lookup 允许您将 Pivotal GemFire 原生 cache.xml 文件中定义的所有区域导入到 Spring ApplicationContext 中。

例如,考虑以下 cache.xml 文件

<?xml version="1.0" encoding="UTF-8"?>
<cache xmlns="https://geode.apache.org/schema/cache"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://geode.apache.org/schema/cache https://geode.apache.org/schema/cache/cache-1.0.xsd"
       version="1.0">

  <region name="Parent" refid="REPLICATE">
    <region name="Child" refid="REPLICATE"/>
  </region>

</cache>

您可以按如下方式导入前面的 cache.xml 文件

<gfe:cache cache-xml-location="cache.xml"/>

然后,您可以使用 <gfe:lookup-region> 元素(例如 <gfe:lookup-region id="Parent"/>)将特定的区域作为 bean 引用到 Spring 容器中,或者您可以通过使用以下方式导入 cache.xml 中定义的所有区域

<gfe:auto-region-lookup/>

Pivotal GemFire 的 Spring Data 会自动为 cache.xml 中定义的所有尚未通过显式 <gfe:lookup-region> bean 声明添加到 Spring 容器的 Pivotal GemFire 区域创建 bean。

重要的是要认识到,Pivotal GemFire 的 Spring Data 使用 Spring BeanPostProcessor 在缓存创建和初始化后对其进行后处理,以确定要作为 bean 添加到 Spring ApplicationContext 中的 Pivotal GemFire 中定义的区域。

您可以像注入 Spring ApplicationContext 中定义的任何其他 bean 一样注入这些“自动查找”的区域,但有一个例外:您可能需要定义与“gemfireCache”bean 的 depends-on 关联,如下所示

package example;

import ...

@Repository("appDao")
@DependsOn("gemfireCache")
public class ApplicationDao extends DaoSupport {

    @Resource(name = "Parent")
    private Region<?, ?> parent;

    @Resource(name = "/Parent/Child")
    private Region<?, ?> child;

    ...
}

前面的示例仅适用于您使用 Spring 的 component-scan 功能的情况。

如果您使用 Spring XML 配置声明组件,那么您将执行以下操作

<bean class="example.ApplicationDao" depends-on="gemfireCache"/>

这样做可以确保 Pivotal GemFire 缓存和 cache.xml 中定义的所有区域在使用 <gfe:auto-region-lookup> 元素时,在任何具有自动连接引用的组件之前创建。

5.5.3. 配置区域

Pivotal GemFire 的 Spring Data 通过以下元素提供对配置任何类型区域的全面支持

  • LOCAL 区域:<local-region>

  • PARTITION 区域:<partitioned-region>

  • REPLICATE 区域:<replicated-region>

  • Client 区域:<client-region>

有关 区域类型 的全面描述,请参阅 Pivotal GemFire 文档。

常用区域属性

下表列出了所有区域类型可用的属性

表 1. 常用区域属性
名称 描述

cache-ref

Pivotal GemFire 缓存 bean 引用

定义 Pivotal GemFire 缓存的 bean 名称(默认为 'gemfireCache')。

cloning-enabled

布尔值(默认值:false

当为 true 时,更新将应用于值的克隆,然后将克隆保存到缓存。当为 false 时,值在缓存中原地修改。

close

布尔值(默认值:false

确定区域在关闭时是否应关闭。

concurrency-checks-enabled

布尔值(默认值:true

确定成员是否执行检查以提供对分布式区域并发或乱序更新的一致处理。

data-policy

请参阅 Pivotal GemFire 的 数据策略

区域的数据策略。请注意,并非所有区域类型都支持所有数据策略。

destroy

布尔值(默认值:false

确定区域在关闭时是否应销毁。

disk-store-ref

配置的磁盘存储的名称。

对通过 disk-store 元素创建的 bean 的引用。

disk-synchronous

布尔值(默认值:true

确定磁盘存储写入是否同步。

id

任何有效的 bean 名称。

如果未指定 name 属性,则为默认区域名称。

ignore-if-exists

布尔值(默认值:false

如果区域已存在于缓存中,则忽略此 bean 定义,转而进行查找。

ignore-jta

布尔值(默认值:false

确定此区域是否参与 JTA (Java Transaction API) 事务。

index-update-type

synchronousasynchronous(默认值:synchronous

确定索引在条目创建时是同步还是异步更新。

initial-capacity

整数(默认值:16)

区域条目数量的初始内存分配。

key-constraint

任何有效、完全限定的 Java 类名。

预期键类型。

load-factor

浮点数(默认值:.75)

设置用于存储区域条目的底层 java.util.ConcurrentHashMap 的初始参数。

name

任何有效的区域名称。

区域的名称。如果未指定,则假定为 id 属性的值(即 bean 名称)。

persistent

*布尔值(默认值:false

确定区域是否将条目持久化到本地磁盘(磁盘存储)。

shortcut

请参阅 https://gemfire-98-javadocs.docs.pivotal.io//org/apache/geode/cache/RegionShortcut.html

此区域的 RegionShortcut。允许根据预定义默认值轻松初始化区域。

statistics

布尔值(默认值:false

确定区域是否报告统计信息。

template

区域模板的名称。

对通过 *region-template 元素之一创建的 bean 的引用。

value-constraint

任何有效、完全限定的 Java 类名。

预期值类型。

CacheListener实例

CacheListener实例注册到Region,以处理Region事件,例如条目创建、更新、销毁等。CacheListener可以是实现CacheListener接口的任何bean。一个Region可以有多个监听器,通过包含在*-region元素中的cache-listener元素声明。

以下示例包含两个声明的CacheListener。第一个引用一个命名的顶层Spring bean。第二个是匿名内部bean定义。

<bean id="myListener" class="org.example.app.geode.cache.SimpleCacheListener"/>

<gfe:replicated-region id="regionWithListeners">
  <gfe:cache-listener>
    <!-- nested CacheListener bean reference -->
    <ref bean="myListener"/>
    <!-- nested CacheListener bean definition -->
    <bean class="org.example.app.geode.cache.AnotherSimpleCacheListener"/>
  </gfe:cache-listener>
</gfe:replicated-region>

以下示例使用带有ref属性的cache-listener元素的替代形式。当定义单个CacheListener时,这样做可以实现更简洁的配置。

注意:XML命名空间只允许一个cache-listener元素,因此必须使用前面示例中所示的样式或以下示例中所示的样式。

<beans>
  <gfe:replicated-region id="exampleReplicateRegionWithCacheListener">
    <gfe:cache-listener ref="myListener"/>
  </gfe:replicated-region>

  <bean id="myListener" class="example.CacheListener"/>
</beans>
cache-listener元素中使用ref和嵌套声明是非法的。这两个选项是互斥的,在同一个元素中使用两者会导致异常。
Bean引用约定

cache-listener元素是XML命名空间中常用模式的一个示例,Pivotal GemFire在任何需要通过实现回调接口来响应缓存或区域事件调用自定义代码的地方都使用该模式。当您使用Spring的IoC容器时,实现是一个标准的Spring bean。为了简化配置,模式允许cache-listener元素出现一次,但如果允许多个实例,它可以包含嵌套的bean引用和内部bean定义,以任何组合形式。约定是使用单数形式(即cache-listener而不是cache-listeners),反映了最常见的情况实际上是单个实例。我们已经在高级缓存配置示例中看到了这种模式的示例。

CacheLoaders和CacheWriters

cache-listener类似,XML命名空间提供了cache-loadercache-writer元素,用于为Region注册这些Pivotal GemFire组件。

当缓存未命中时,会调用CacheLoader,以便从外部数据源(如数据库)加载条目。在创建或更新条目之前,会调用CacheWriter,以允许将条目同步到外部数据源。主要区别在于,Pivotal GemFire每个Region最多支持一个CacheLoaderCacheWriter实例。但是,可以使用任何一种声明样式。

以下示例声明了一个同时具有CacheLoaderCacheWriter的Region

<beans>
  <gfe:replicated-region id="exampleReplicateRegionWithCacheLoaderAndCacheWriter">
    <gfe:cache-loader ref="myLoader"/>
    <gfe:cache-writer>
      <bean class="example.CacheWriter"/>
    </gfe:cache-writer>
  </gfe:replicated-region>

  <bean id="myLoader" class="example.CacheLoader">
    <property name="dataSource" ref="mySqlDataSource"/>
  </bean>

  <!-- DataSource bean definition -->
</beans>

有关更多详细信息,请参阅Pivotal GemFire文档中的CacheLoaderCacheWriter

5.5.4. 压缩

Pivotal GemFire Region也可以被压缩,以减少JVM内存消耗和压力,从而可能避免全局GC。当您为Region启用压缩时,存储在内存中的所有Region值都将被压缩,而键和索引保持未压缩状态。新值在放入Region时被压缩,所有值在从Region读取时自动解压缩。值在持久化到磁盘或通过网络发送到其他对等成员或客户端时不会被压缩。

以下示例显示了一个启用压缩的Region

<beans>
  <gfe:replicated-region id="exampleReplicateRegionWithCompression">
    <gfe:compressor>
      <bean class="org.apache.geode.compression.SnappyCompressor"/>
    </gfe:compressor>
  </gfe:replicated-region>
</beans>

有关Region压缩的更多信息,请参阅Pivotal GemFire的文档。

5.5.5. 堆外内存

Pivotal GemFire Region也可以配置为将Region值存储在堆外内存中,这部分JVM内存不受垃圾回收(GC)的影响。通过避免昂贵的GC周期,您的应用程序可以将更多时间花在重要的事务上,例如处理请求。

使用堆外内存就像声明要使用的内存量,然后启用您的Region使用堆外内存一样简单,如以下配置所示

<util:properties id="gemfireProperties">
    <prop key="off-heap-memory-size">200G</prop>
</util:properties>

<gfe:cache properties-ref="gemfireProperties"/>

<gfe:partitioned-region id="ExampleOffHeapRegion" off-heap="true"/>

您可以通过使用<gfe:cache>元素设置以下Pivotal GemFire配置属性来控制堆外内存管理的其他方面:

<gfe:cache critical-off-heap-percentage="90" eviction-off-heap-percentage"80"/>

Pivotal GemFire的ResourceManager将使用这两个阈值(critical-off-heap-percentageeviction-off-heap-percentage)更有效地管理堆外内存,就像JVM管理堆内存一样。Pivotal GemFire ResourceManager将通过驱逐旧数据来防止缓存消耗过多的堆外内存。如果堆外管理器无法跟上,则ResourceManager会拒绝向缓存添加内容,直到堆外内存管理器释放了足够的内存。

有关管理堆和堆外内存的更多信息,请参阅Pivotal GemFire的文档。

具体来说,请阅读管理堆外内存部分。

5.5.6. 子区域

Spring Data for Pivotal GemFire 还支持子区域,允许区域以分层关系排列。

例如,Pivotal GemFire 允许一个/Customer/Address区域和一个不同的/Employee/Address区域。此外,一个子区域可以拥有自己的子区域和配置。子区域不继承父区域的属性。区域类型可以混合搭配,但受 Pivotal GemFire 约束。子区域自然地声明为区域的子元素。子区域的name属性是简单名称。前面的示例可以配置如下:

<beans>
  <gfe:replicated-region name="Customer">
    <gfe:replicated-region name="Address"/>
  </gfe:replicated-region>

  <gfe:replicated-region name="Employee">
    <gfe:replicated-region name="Address"/>
  </gfe:replicated-region>
</beans>

请注意,子区域不允许使用Monospaced ([id])属性。子区域是使用bean名称创建的(在本例中分别为/Customer/Address和/Employee/Address)。因此,它们可以注入到其他应用程序bean中,例如GemfireTemplate,这些bean需要它们时可以使用区域的完整路径名。区域的完整路径名也应在OQL查询字符串中使用。

5.5.7. 区域模板

Spring Data for Pivotal GemFire 还支持区域模板。

此功能允许开发人员一次性定义通用的区域配置和属性,并在 Spring ApplicationContext 中声明的许多区域 bean 定义中重用该配置。

Spring Data for Pivotal GemFire 在其命名空间中包含五个区域模板标签

表 2. 区域模板标签
标签名 描述

<gfe:region-template>

定义通用的泛型区域属性。扩展XML命名空间中的regionType

<gfe:local-region-template>

定义通用“本地”区域属性。扩展XML命名空间中的localRegionType

<gfe:partitioned-region-template>

定义通用“分区”区域属性。扩展XML命名空间中的partitionedRegionType

<gfe:replicated-region-template>

定义通用“复制”区域属性。扩展XML命名空间中的replicatedRegionType

<gfe:client-region-template>

定义通用“客户端”区域属性。扩展XML命名空间中的clientRegionType

除了标签,具体的<gfe:*-region>元素(以及抽象的<gfe:*-region-template>元素)都有一个template属性,用于定义区域继承其配置的区域模板。区域模板甚至可以继承其他区域模板。

以下示例显示了一种可能的配置

<beans>
  <gfe:async-event-queue id="AEQ" persistent="false" parallel="false" dispatcher-threads="4">
    <gfe:async-event-listener>
      <bean class="example.AeqListener"/>
    </gfe:async-event-listener>
  </gfe:async-event-queue>

  <gfe:region-template id="BaseRegionTemplate" initial-capacity="51" load-factor="0.85" persistent="false" statistics="true"
      key-constraint="java.lang.Long" value-constraint="java.lang.String">
    <gfe:cache-listener>
      <bean class="example.CacheListenerOne"/>
      <bean class="example.CacheListenerTwo"/>
    </gfe:cache-listener>
    <gfe:entry-ttl timeout="600" action="DESTROY"/>
    <gfe:entry-tti timeout="300 action="INVLIDATE"/>
  </gfe:region-template>

  <gfe:region-template id="ExtendedRegionTemplate" template="BaseRegionTemplate" load-factor="0.55">
    <gfe:cache-loader>
      <bean class="example.CacheLoader"/>
    </gfe:cache-loader>
    <gfe:cache-writer>
      <bean class="example.CacheWriter"/>
    </gfe:cache-writer>
    <gfe:async-event-queue-ref bean="AEQ"/>
  </gfe:region-template>

  <gfe:partitioned-region-template id="PartitionRegionTemplate" template="ExtendedRegionTemplate"
      copies="1" load-factor="0.70" local-max-memory="1024" total-max-memory="16384" value-constraint="java.lang.Object">
    <gfe:partition-resolver>
      <bean class="example.PartitionResolver"/>
    </gfe:partition-resolver>
    <gfe:eviction type="ENTRY_COUNT" threshold="8192000" action="OVERFLOW_TO_DISK"/>
  </gfe:partitioned-region-template>

  <gfe:partitioned-region id="TemplateBasedPartitionRegion" template="PartitionRegionTemplate"
      copies="2" local-max-memory="8192" persistent="true" total-buckets="91"/>
</beans>

区域模板也适用于子区域。请注意,'TemplateBasedPartitionRegion' 扩展了 'PartitionRegionTemplate',后者又扩展了 'ExtendedRegionTemplate',而后者又扩展了 'BaseRegionTemplate'。在后续继承的区域 bean 定义中定义的属性和子元素会覆盖父级中的内容。

模板的工作原理

Spring Data for Pivotal GemFire 在解析 Spring ApplicationContext 配置元数据时应用区域模板,因此,区域模板必须按继承顺序声明。换句话说,父模板必须在子模板之前定义。这样做可以确保应用正确的配置,尤其是在元素属性或子元素被覆盖时。

同样重要的是要记住,区域类型只能从其他类似类型的区域继承。例如,<gfe:replicated-region>不可能从<gfe:partitioned-region-template>继承。
区域模板是单一继承的。
关于区域、子区域和查找的注意事项

以前,Spring Data for Pivotal GemFire XML 命名空间中 replicated-regionpartitioned-regionlocal-regionclient-region 元素的一个底层属性是先执行查找,然后再尝试创建区域。这样做是为了防止区域已经存在的情况,如果区域是在导入的 Pivotal GemFire 本机 cache.xml 配置文件中定义的,就会出现这种情况。因此,先执行查找是为了避免任何错误。这是设计使然,可能会发生变化。

此行为已更改,默认行为现在是先创建Region。如果Region已经存在,则创建逻辑会快速失败并抛出适当的异常。但是,就像CREATE TABLE IF NOT EXISTS …​ DDL 语法一样,Spring Data for Pivotal GemFire <gfe:*-region> XML命名空间元素现在包含一个ignore-if-exists属性,该属性通过在尝试创建Region之前,首先根据名称查找现有Region来恢复旧的行为。如果根据名称找到现有Region并且ignore-if-exists设置为true,则Spring配置中定义的Region bean定义将被忽略。

Spring 团队强烈建议,replicated-regionpartitioned-regionlocal-regionclient-region XML 命名空间元素应严格用于仅定义新区域。当这些元素定义的区域已经存在并且区域元素首先执行查找时,可能会出现一个问题是,如果您在应用程序配置中为驱逐、过期、订阅等定义了不同的区域语义和行为,那么区域定义可能不匹配,并可能表现出与应用程序要求相反的行为。更糟糕的是,您可能希望将区域定义为分布式区域(例如,PARTITION),而实际上,现有区域定义仅限于本地。
推荐实践 - 仅使用replicated-regionpartitioned-regionlocal-regionclient-region XML 命名空间元素来定义新区域。

考虑以下 Pivotal GemFire 本机 cache.xml 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<cache xmlns="https://geode.apache.org/schema/cache"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://geode.apache.org/schema/cache https://geode.apache.org/schema/cache/cache-1.0.xsd"
       version="1.0">

  <region name="Customers" refid="REPLICATE">
    <region name="Accounts" refid="REPLICATE">
      <region name="Orders" refid="REPLICATE">
        <region name="Items" refid="REPLICATE"/>
      </region>
    </region>
  </region>

</cache>

此外,考虑您可能已将应用程序 DAO 定义如下:

public class CustomerAccountDao extends GemDaoSupport {

    @Resource(name = "Customers/Accounts")
    private Region customersAccounts;

    ...
}

在这里,我们将对Customers/Accounts Region的引用注入到我们的应用程序DAO中。因此,开发人员在Spring XML配置元数据中为部分或所有这些Region定义bean的情况并不少见,如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:gfe="https://www.springframework.org/schema/gemfire"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
    http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
    https://www.springframework.org/schema/gemfire https://www.springframework.org/schema/gemfire/spring-gemfire.xsd
">

  <gfe:cache cache-xml-location="classpath:cache.xml"/>

  <gfe:lookup-region name="Customers/Accounts"/>
  <gfe:lookup-region name="Customers/Accounts/Orders"/>

</beans>

Customers/AccountsCustomers/Accounts/Orders区域在Spring容器中分别作为Customers/AccountsCustomers/Accounts/Orders引用。使用lookup-region元素和相应的语法(前面描述的)的好处是,它允许您直接引用子区域,而无需不必要地为父区域(在本例中为Customers)定义bean。

考虑以下糟糕的示例,它将配置元数据语法更改为使用嵌套格式

<gfe:lookup-region name="Customers">
  <gfe:lookup-region name="Accounts">
    <gfe:lookup-region name="Orders"/>
  </gfe:lookup-region>
</gfe:lookup-region>

现在考虑另一个糟糕的示例,它使用顶级replicated-region元素以及设置为首先执行查找的ignore-if-exists属性。

<gfe:replicated-region name="Customers" persistent="true" ignore-if-exists="true">
  <gfe:replicated-region name="Accounts" persistent="true" ignore-if-exists="true">
    <gfe:replicated-region name="Orders" persistent="true" ignore-if-exists="true"/>
  </gfe:replicated-region>
</gfe:replicated-region>

在Spring ApplicationContext中定义的Region bean包括:{ "Customers", "/Customers/Accounts", "/Customers/Accounts/Orders" }。这意味着前面示例中显示的依赖注入引用(即@Resource(name = "Customers/Accounts"))现在已损坏,因为实际上没有定义名为Customers/Accounts的bean。因此,您不应按照前面两个示例所示配置Region。

Pivotal GemFire在引用父区域和子区域时非常灵活,无论是否带有前导斜杠。例如,父级可以引用为/CustomersCustomers,子级可以引用为/Customers/AccountsCustomers/Accounts。但是,Spring Data for Pivotal GemFire在根据区域命名bean时非常具体。它始终使用斜杠 (/) 来表示子区域(例如,/Customers/Accounts)。

因此,您应该使用前面所示的非嵌套lookup-region语法,或者定义带前导斜杠 (/) 的直接引用,如下所示:

<gfe:lookup-region name="/Customers/Accounts"/>
<gfe:lookup-region name="/Customers/Accounts/Orders"/>

前面的示例,其中使用了嵌套的replicated-region元素来引用子区域,显示了前面所述的问题。Customers、Accounts和Orders区域和子区域是否持久?它们不是持久的,因为区域是在本机 Pivotal GemFire cache.xml 配置文件中定义为REPLICATE,并且在缓存bean初始化之前(一旦<gfe:cache>元素被处理)就已存在。

5.5.8. 数据驱逐(带溢出)

基于各种约束,每个Region都可以有一个逐出策略,用于从内存中逐出数据。目前,在Pivotal GemFire中,逐出适用于最近最少使用的条目(也称为LRU)。被逐出的条目要么被销毁,要么被分页到磁盘(称为“溢出到磁盘”)。

Spring Data for Pivotal GemFire 通过使用嵌套的 eviction 元素支持 PARTITION 区域、REPLICATE 区域以及客户端、本地区域的所有逐出策略(条目计数、内存和堆使用情况)。

例如,要配置一个PARTITION Region,使其在内存大小超过512 MB时溢出到磁盘,您可以指定以下配置

<gfe:partitioned-region id="examplePartitionRegionWithEviction">
  <gfe:eviction type="MEMORY_SIZE" threshold="512" action="OVERFLOW_TO_DISK"/>
</gfe:partitioned-region>
副本不能使用local destroy驱逐,因为那会使它们失效。有关更多信息,请参阅Pivotal GemFire文档。

配置溢出区域时,应通过disk-store元素配置存储以实现最大效率。

有关驱逐策略的详细描述,请参阅Pivotal GemFire文档中关于驱逐的部分。

5.5.9. 数据过期

Pivotal GemFire 允许您控制条目在缓存中存在多长时间。过期是由经过时间驱动的,而逐出是由条目计数或堆或内存使用情况驱动的。一旦条目过期,就无法再从缓存中访问它。

Pivotal GemFire 支持以下过期类型

  • 存活时间 (TTL):对象在上次创建或更新后可以在缓存中保留的秒数。对于条目,创建和put操作将计数器设置为零。区域计数器在区域创建时和条目计数器重置时重置。

  • 空闲超时 (TTI):对象在上次访问后可以在缓存中保留的秒数。对象的空闲超时计数器在其TTL计数器重置的任何时候都会重置。此外,条目的空闲超时计数器在通过get操作或netSearch访问条目的任何时候都会重置。区域的空闲超时计数器在其任何条目的空闲超时重置时都会重置。

这些都可以应用于Region本身或Region中的条目。Spring Data for Pivotal GemFire 提供了<region-ttl><region-tti><entry-ttl><entry-tti> Region子元素来指定超时值和过期操作。

以下示例显示了一个设置了过期值的PARTITION Region

<gfe:partitioned-region id="examplePartitionRegionWithExpiration">
  <gfe:region-ttl timeout="30000" action="INVALIDATE"/>
  <gfe:entry-tti timeout="600" action="LOCAL_DESTROY"/>
</gfe:replicated-region>

有关过期策略的详细描述,请参阅Pivotal GemFire文档中关于过期的部分。

基于注解的数据过期

使用 Spring Data for Pivotal GemFire,您可以在单个 Region 条目值(或者换句话说,直接在应用程序域对象上)定义过期策略和设置。例如,您可以在基于 Session 的应用程序域对象上定义过期策略,如下所示:

@Expiration(timeout = "1800", action = "INVALIDATE")
public class SessionBasedApplicationDomainObject {
  ...
}

您还可以通过使用@IdleTimeoutExpiration@TimeToLiveExpiration注解分别指定针对空闲超时(TTI)和存活时间(TTL)过期的特定设置,如以下示例所示

@TimeToLiveExpiration(timeout = "3600", action = "LOCAL_DESTROY")
@IdleTimeoutExpiration(timeout = "1800", action = "LOCAL_INVALIDATE")
@Expiration(timeout = "1800", action = "INVALIDATE")
public class AnotherSessionBasedApplicationDomainObject {
  ...
}

当指定多个过期注解类型时,@IdleTimeoutExpiration@TimeToLiveExpiration都优先于通用@Expiration注解,如前面示例所示。@IdleTimeoutExpiration@TimeToLiveExpiration都不会覆盖另一个。相反,当配置不同的Region条目过期策略(例如TTL和TTI)时,它们是相互补充的。

所有基于@Expiration的注解仅适用于Region条目值。Region的过期不在Spring Data for Pivotal GemFire的过期注解支持范围内。但是,Pivotal GemFire和Spring Data for Pivotal GemFire确实允许您使用SDG XML命名空间设置Region过期,如下所示:

<gfe:*-region id="Example" persistent="false">
  <gfe:region-ttl timeout="600" action="DESTROY"/>
  <gfe:region-tti timeout="300" action="INVALIDATE"/>
</gfe:*-region>

Spring Data for Pivotal GemFire的@Expiration注解支持是使用Pivotal GemFire的CustomExpiry接口实现的。有关更多详细信息,请参阅Pivotal GemFire文档中关于配置数据过期的部分。

Spring Data for Pivotal GemFire 的 AnnotationBasedExpiration 类(和 CustomExpiry 实现)负责处理 SDG @Expiration 注解,并根据请求适当应用区域条目过期的过期策略配置。

要使用 Spring Data for Pivotal GemFire 配置特定的 Pivotal GemFire 区域,以将过期策略适当地应用于使用基于 @Expiration 注解的应用程序域对象,您必须:

  1. 通过使用适当的构造函数或便捷的工厂方法之一,在 Spring ApplicationContext 中定义一个类型为 AnnotationBasedExpiration 的 bean。当配置特定过期类型(例如空闲超时 (TTI) 或存活时间 (TTL))的过期时,您应该使用 AnnotationBasedExpiration 类中的工厂方法之一,如下所示:

    <bean id="ttlExpiration" class="org.springframework.data.gemfire.expiration.AnnotationBasedExpiration"
          factory-method="forTimeToLive"/>
    
    <gfe:partitioned-region id="Example" persistent="false">
        <gfe:custom-entry-ttl ref="ttlExpiration"/>
    </gfe:partitioned-region>

    要配置空闲超时(TTI)过期,请使用forIdleTimeout工厂方法和<gfe:custom-entry-tti ref="ttiExpiration"/>元素来设置TTI。

  2. (可选)使用 Spring Data for Pivotal GemFire 的 @Expiration 注解之一:@Expiration@IdleTimeoutExpiration@TimeToLiveExpiration,为存储在区域中的应用程序域对象添加带有过期策略和自定义设置的注解。

  3. (可选)在某些情况下,如果特定的应用程序域对象根本没有用 Spring Data for Pivotal GemFire 的 @Expiration 注解进行注解,但 Pivotal GemFire 区域被配置为使用 SDG 的自定义 AnnotationBasedExpiration 类来确定存储在区域中的对象的过期策略和设置,您可以通过执行以下操作来设置 AnnotationBasedExpiration bean 上的“默认”过期属性:

<bean id="defaultExpirationAttributes" class="org.apache.geode.cache.ExpirationAttributes">
    <constructor-arg value="600"/>
    <constructor-arg value="#{T(org.apache.geode.cache.ExpirationAction).DESTROY}"/>
</bean>

<bean id="ttiExpiration" class="org.springframework.data.gemfire.expiration.AnnotationBasedExpiration"
      factory-method="forIdleTimeout">
    <constructor-arg ref="defaultExpirationAttributes"/>
</bean>

<gfe:partitioned-region id="Example" persistent="false">
    <gfe:custom-entry-tti ref="ttiExpiration"/>
</gfe:partitioned-region>

您可能已经注意到,Spring Data for Pivotal GemFire 的 @Expiration 注解使用 String 作为属性类型,而不是(可能更合适地)强类型——例如,'timeout' 为 int,'action' 为 SDG 的 ExpirationActionType。为什么会这样呢?

好了,我们来看 Spring Data for Pivotal GemFire 的另一个特性,它利用 Spring 的核心基础设施来方便配置:属性占位符和 Spring Expression Language (SpEL) 表达式。

例如,开发人员可以使用@Expiration注解属性中的属性占位符来指定过期“timeout”和“action”,如下例所示:

@TimeToLiveExpiration(timeout = "${geode.region.entry.expiration.ttl.timeout}"
    action = "${geode.region.entry.expiration.ttl.action}")
public class ExampleApplicationDomainObject {
  ...
}

然后,在您的 Spring XML 配置或 JavaConfig 中,您可以声明以下 bean:

<util:properties id="expirationSettings">
  <prop key="geode.region.entry.expiration.ttl.timeout">600</prop>
  <prop key="geode.region.entry.expiration.ttl.action">INVALIDATE</prop>
  ...
</util:properties>

<context:property-placeholder properties-ref="expirationProperties"/>

这在多个应用程序域对象可能共享相似过期策略以及希望外部化配置时都非常方便。

但是,您可能需要更动态的过期配置,由运行系统的状态决定。这时 SpEL 的强大功能就发挥作用了,实际上,这也是推荐的方法。您不仅可以引用 Spring 容器中的 bean 并访问 bean 属性、调用方法等,而且过期“timeout”和“action”的值可以是强类型的。考虑以下示例(它建立在前面的示例之上):

<util:properties id="expirationSettings">
  <prop key="geode.region.entry.expiration.ttl.timeout">600</prop>
  <prop key="geode.region.entry.expiration.ttl.action">#{T(org.springframework.data.gemfire.expiration.ExpirationActionType).DESTROY}</prop>
  <prop key="geode.region.entry.expiration.tti.action">#{T(org.apache.geode.cache.ExpirationAction).INVALIDATE}</prop>
  ...
</util:properties>

<context:property-placeholder properties-ref="expirationProperties"/>

然后,在您的应用程序域对象上,您可以定义超时和操作,如下所示:

@TimeToLiveExpiration(timeout = "@expirationSettings['geode.region.entry.expiration.ttl.timeout']"
    action = "@expirationSetting['geode.region.entry.expiration.ttl.action']")
public class ExampleApplicationDomainObject {
  ...
}

您可以想象,'expirationSettings' bean 可以是一个比简单的java.util.Properties实例更有趣和有用的对象。在前面的示例中,properties元素(expirationSettings)使用SpEL将操作值基于实际的ExpirationAction枚举类型,如果枚举类型发生变化,可以迅速发现故障。

举例来说,所有这些都在Spring Data for Pivotal GemFire测试套件中得到演示和测试。有关更多详细信息,请参阅源代码

5.5.10. 数据持久性

区域可以是持久的。Pivotal GemFire 确保您放入配置为持久性的区域中的所有数据都以可恢复的方式写入磁盘,以便您下次重新创建区域时可以恢复数据。这样做允许在机器或进程故障后,甚至在有序关闭和随后重新启动 Pivotal GemFire 数据节点后恢复数据。

要使用Spring Data for Pivotal GemFire启用持久性,请在任何<*-region>元素上将persistent属性设置为true,如以下示例所示:

<gfe:partitioned-region id="examplePersitentPartitionRegion" persistent="true"/>

持久性也可以通过设置data-policy属性来配置。为此,请将属性的值设置为Pivotal GemFire的DataPolicy设置之一,如以下示例所示:

<gfe:partitioned-region id="anotherExamplePersistentPartitionRegion" data-policy="PERSISTENT_PARTITION"/>

DataPolicy必须与Region类型匹配,并且如果persistent属性也明确设置,则必须与persistent属性一致。如果persistent属性设置为false但指定了持久性DataPolicy(例如PERSISTENT_REPLICATEPERSISTENT_PARTITION),则会抛出初始化异常。

为了在持久化区域时达到最大效率,您应该通过disk-store元素配置存储。DiskStore通过disk-store-ref属性引用。此外,区域可以同步或异步执行磁盘写入。以下示例显示了一个同步DiskStore

<gfe:partitioned-region id="yetAnotherExamplePersistentPartitionRegion" persistent="true"
    disk-store-ref="myDiskStore" disk-synchronous="true"/>

这在配置DiskStore中进一步讨论。

5.5.11. 订阅策略

Pivotal GemFire 允许配置点对点(P2P)事件消息来控制区域接收的条目事件。Spring Data for Pivotal GemFire 提供<gfe:subscription/>子元素来将REPLICATEPARTITION区域的订阅策略设置为ALLCACHE_CONTENT。以下示例显示了一个区域,其订阅策略设置为CACHE_CONTENT

<gfe:partitioned-region id="examplePartitionRegionWithCustomSubscription">
  <gfe:subscription type="CACHE_CONTENT"/>
</gfe:partitioned-region>

5.5.12. 本地区域

Spring Data for Pivotal GemFire 提供了一个专门的local-region元素用于创建本地Region。顾名思义,本地Region是独立的,这意味着它们不与其他分布式系统成员共享数据。除此之外,所有常见的Region配置选项都适用。

以下示例显示了一个最小声明(同样,该示例依赖于Spring Data for Pivotal GemFire XML命名约定来连接缓存)

<gfe:local-region id="exampleLocalRegion"/>

在前面的示例中,创建了一个本地区域(如果不存在同名区域)。该区域的名称与bean ID(exampleLocalRegion)相同,并且该bean假定存在一个名为gemfireCache的Pivotal GemFire缓存。

5.5.13. 复制区域

一种常见的区域类型是REPLICATE区域或“副本”。简而言之,当一个区域被配置为REPLICATE时,每个托管该区域的成员都在本地存储该区域条目的副本。对REPLICATE区域的任何更新都会分发到该区域的所有副本。当创建副本时,它会经历一个初始化阶段,在该阶段它会发现其他副本并自动复制所有条目。在一个副本初始化时,您仍然可以继续使用其他副本。

所有常见的配置选项都适用于REPLICATE区域。Spring Data for Pivotal GemFire 提供了一个replicated-region元素。以下示例显示了一个最小声明:

<gfe:replicated-region id="exampleReplica"/>

有关更多详细信息,请参阅Pivotal GemFire文档中关于分布式和复制区域的部分。

5.5.14. 分区区域

Spring Data for Pivotal GemFire XML 命名空间还支持PARTITION区域。

引用Pivotal GemFire文档:

“分区区域是指数据在托管该区域的对等服务器之间进行划分,以便每个对等服务器存储数据的一个子集。使用分区区域时,应用程序会看到一个逻辑视图,该视图看起来像一个包含该区域所有数据的单个映射。对此映射的读写操作会透明地路由到托管操作目标条目的对等服务器。Pivotal GemFire 将哈希码域划分为桶。每个桶分配给一个特定的对等服务器,但可以随时重新定位到另一个对等服务器,以提高整个集群的资源利用率。”

使用partitioned-region元素创建一个PARTITION区域。其配置选项类似于replicated-region,并增加了分区特定的功能,例如冗余副本的数量、总最大内存、桶的数量、分区解析器等。

以下示例显示了如何设置具有两个冗余副本的PARTITION区域

<gfe:partitioned-region id="examplePartitionRegion" copies="2" total-buckets="17">
  <gfe:partition-resolver>
    <bean class="example.PartitionResolver"/>
  </gfe:partition-resolver>
</gfe:partitioned-region>

有关更多详细信息,请参阅Pivotal GemFire文档中关于分区区域的部分。

分区区域属性

下表快速概述了PARTITION区域特有的配置选项。这些选项是对前面描述的通用区域配置选项的补充。

表 3. partitioned-region 属性
名称 描述

副本

0..4

每个分区用于高可用性的副本数量。默认情况下,不创建副本,这意味着没有冗余。每个副本都以额外存储为代价提供额外备份。

colocated-with

有效区域名称

此新创建的PARTITION区域与哪个PARTITION区域并置的名称。

local-max-memory

正整数

此进程中区域使用的最大内存量(以兆字节为单位)。

total-max-memory

任意整数值

所有进程中区域使用的最大内存量(以兆字节为单位)。

partition-listener

bean 名称

此区域用于处理分区事件的PartitionListener的名称。

partition-resolver

bean 名称

此区域用于自定义分区的PartitionResolver的名称。

recovery-delay

任意长整型值

现有成员在另一个成员崩溃后等待满足冗余的延迟(以毫秒为单位)。-1(默认值)表示故障后不恢复冗余。

startup-recovery-delay

任意长整型值

新成员在满足冗余之前等待的毫秒数。-1 表示添加新成员不会触发冗余恢复。默认是在添加新成员时立即恢复冗余。

5.5.15. 客户端区域

Pivotal GemFire 支持各种部署拓扑来管理和分发数据。Pivotal GemFire 拓扑的主题超出了本文档的范围。但是,快速回顾一下,Pivotal GemFire 支持的拓扑可以分为:点对点 (p2p)、客户端-服务器和广域网 (WAN)。在后两种配置中,通常会声明连接到缓存服务器的客户端区域。

Spring Data for Pivotal GemFire 通过其客户端缓存元素:client-regionpool,为每种配置提供专门支持。顾名思义,client-region定义了一个客户端区域,而pool定义了一个由各种客户端区域使用和共享的连接池。

以下示例显示了一个典型的客户端区域配置

<bean id="myListener" class="example.CacheListener"/>

<!-- client Region using the default SDG gemfirePool Pool -->
<gfe:client-region id="Example">
  <gfe:cache-listener ref="myListener"/>
</gfe:client-region>

<!-- client Region using its own dedicated Pool -->
<gfe:client-region id="AnotherExample" pool-name="myPool">
  <gfe:cache-listener ref="myListener"/>
</gfe:client-region>

<!-- Pool definition -->
<gfe:pool id="myPool" subscription-enabled="true">
  <gfe:locator host="remoteHost" port="12345"/>
</gfe:pool>

与其他区域类型一样,client-region支持CacheListener实例以及CacheLoaderCacheWriter。它还需要一个连接Pool才能连接到一组定位器或服务器。每个客户端区域都可以有自己的Pool,或者它们可以共享同一个Pool。如果未指定Pool,则将使用“DEFAULT”Pool

在前面的示例中,Pool配置了一个Locator。Locator是一个单独的进程,用于发现分布式系统中的缓存服务器和对等数据成员,建议用于生产系统。还可以配置Pool以使用server元素直接连接到一个或多个缓存服务器。

有关客户端上(特别是Pool上)可设置的选项的完整列表,请参阅 Spring Data for Pivotal GemFire 模式(“Spring Data for Pivotal GemFire 模式”)和 Pivotal GemFire 文档中关于客户端-服务器配置的部分。

客户兴趣

为了最大限度地减少网络流量,每个客户端可以单独定义自己的“兴趣”策略,向Pivotal GemFire指示它实际需要的数据。在Spring Data for Pivotal GemFire中,可以为每个客户端Region单独定义“兴趣”。支持基于键和基于正则表达式的兴趣类型。

以下示例显示了基于键和基于正则表达式的interest类型

<gfe:client-region id="Example" pool-name="myPool">
    <gfe:key-interest durable="true" result-policy="KEYS">
        <bean id="key" class="java.lang.String">
             <constructor-arg value="someKey"/>
        </bean>
    </gfe:key-interest>
    <gfe:regex-interest pattern=".*" receive-values="false"/>
</gfe:client-region>

特殊键ALL_KEYS表示已注册所有键的“兴趣”。使用正则表达式".*"可以实现相同目的。

<gfe:*-interest>键和正则表达式元素支持三个属性:durablereceive-valuesresult-policy

durable指示当客户端连接到集群中的一个或多个服务器时为客户端创建的“兴趣”策略和订阅队列是否在客户端会话之间保持。如果客户端断开连接并重新连接,则在客户端断开连接期间,服务器上为客户端维护一个durable订阅队列。当客户端重新连接时,客户端会收到在客户端与集群中的服务器断开连接期间发生的任何事件。

集群中的服务器上为客户端定义的每个连接Pool都会维护一个订阅队列,该Pool也已“启用”订阅。订阅队列用于存储(并可能合并)发送到客户端的事件。如果订阅队列是durable的,它会在客户端会话(即连接)之间持久化,可能直到指定的超时。如果客户端未在给定时间范围内返回,则客户端Pool订阅队列将被销毁,以减少集群中服务器的资源消耗。如果订阅队列不是durable的,它会在客户端断开连接时立即销毁。您需要决定客户端是应该接收在断开连接期间发生的事件,还是只需要在重新连接后接收最新事件。

receive-values属性指示是否为创建和更新事件接收条目值。如果为true,则接收值。如果为false,则仅接收失效事件。

最后,`result-policy`是枚举类型:`KEYS`、`KEYS_VALUE`和`NONE`。默认值为`KEYS_VALUES`。`result-policy`控制客户端首次连接以初始化本地缓存时的初始转储,实质上是为客户端播种与兴趣策略匹配的所有条目的事件。

如前所述,如果没有启用Pool上的订阅,客户端兴趣注册作用不大。实际上,在没有启用订阅的情况下尝试兴趣注册是错误的。以下示例显示了如何进行操作:

<gfe:pool ... subscription-enabled="true">
  ...
</gfe:pool>

除了subscription-enabled,您还可以设置subscription-ack-intervalsubscription-message-tracking-timeoutsubscription-redundancysubscription-redundancy用于控制集群中的服务器应维护多少个订阅队列副本。如果冗余大于1,并且“主”订阅队列(即服务器)宕机,则“辅助”订阅队列将接管,防止客户端在HA场景中丢失事件。

除了Pool设置外,服务器端区域还使用一个附加属性enable-subscription-conflation来控制发送到客户端的事件的合并。这也可以进一步最小化网络流量,并且在应用程序只关心条目最新值的情况下非常有用。但是,如果应用程序保留了发生的事件时间序列,合并将阻碍该用例。默认值为false。以下示例显示了服务器上的区域配置,客户端包含相应的客户端[CACHING_]PROXY区域,其中包含对此服务器区域中键的兴趣:

<gfe:partitioned-region name="ServerSideRegion" enable-subscription-conflation="true">
  ...
</gfe:partitioned-region>

要控制在客户端与集群中的服务器断开连接后,“持久”订阅队列维护的时间量(以秒为单位),请按如下方式设置<gfe:client-cache>元素上的durable-client-timeout属性:

<gfe:client-cache durable-client-timeout="600">
  ...
</gfe:client-cache>

关于客户端兴趣工作原理和功能的全面深入讨论超出了本文档的范围。

有关更多详细信息,请参阅 Pivotal GemFire 文档中关于客户端到服务器事件分发的部分。

5.5.16. JSON 支持

Pivotal GemFire 支持在区域中缓存 JSON 文档,并能够使用 Pivotal GemFire OQL(对象查询语言)查询存储的 JSON 文档。JSON 文档内部以 PdxInstance 类型存储,使用 JSONFormatter 类执行 JSON 文档(作为 String)的转换。

Spring Data for Pivotal GemFire 提供了<gfe-data:json-region-autoproxy/>元素,以启用一个AOP组件来建议适当的代理区域操作,这有效地封装了JSONFormatter,从而允许您的应用程序直接使用JSON字符串。

此外,写入 JSON 配置区域的 Java 对象将使用 Jackson 的 ObjectMapper 自动转换为 JSON。当这些值被读回时,它们将作为 JSON 字符串返回。

默认情况下,<gfe-data:json-region-autoproxy/>为所有区域执行转换。要将此功能应用于选定的区域,请在region-refs属性中提供逗号分隔的区域 bean ID 列表。其他属性包括pretty-print标志(默认为false)和convert-returned-collections

此外,默认情况下,getAll()values()区域操作的结果会针对已配置的区域进行转换。这是通过在本地内存中创建并行数据结构来完成的。这对于大型集合可能会产生显著的开销,因此如果您希望禁用这些区域操作的自动转换,请将convert-returned-collections设置为false

某些区域操作(特别是那些使用 Pivotal GemFire 专有 Region.Entry 的操作,例如:entries(boolean)entrySet(boolean)getEntry() 类型)不作为 AOP 建议的目标。此外,entrySet() 方法(返回 Set<java.util.Map.Entry<?, ?>>)也不受影响。

以下示例配置显示了如何设置pretty-printconvert-returned-collections属性

<gfe-data:json-region-autoproxy region-refs="myJsonRegion" pretty-print="true" convert-returned-collections="false"/>

此功能也与GemfireTemplate操作无缝衔接,前提是模板声明为Spring bean。目前,不支持原生QueryService操作。

5.6. 配置索引

Pivotal GemFire 允许在区域数据上创建索引(有时也复数为“索引”)以提高 OQL(对象查询语言)查询的性能。

在 Spring Data for Pivotal GemFire 中,索引使用 index 元素声明,如以下示例所示:

<gfe:index id="myIndex" expression="someField" from="/SomeRegion" type="HASH"/>

在 Spring Data for Pivotal GemFire 的 XML 模式(也称为 SDG XML 命名空间)中,index bean 声明不绑定到 Region,这与 Pivotal GemFire 的原生 cache.xml 不同。相反,它们是顶层元素,类似于 <gfe:cache> 元素。这允许您在任何 Region 上声明任意数量的索引,无论它们是刚刚创建还是已经存在——这相对于 Pivotal GemFire 的原生 cache.xml 格式是一个显著的改进。

Index必须有一个名称。您可以使用name属性为Index指定一个显式名称。否则,将使用index bean定义的bean名称(即id属性的值)作为Index名称。

expressionfrom子句构成Index的主要组成部分,用于标识要索引的数据(即from子句中标识的Region)以及用于索引数据的条件(即expression)。expression应基于应用程序域对象字段,这些字段用于应用程序定义的OQL查询的谓词中,这些查询用于查询和查找存储在Region中的对象。

考虑以下示例,它有一个lastName属性:

@Region("Customers")
class Customer {

  @Id
  Long id;

  String lastName;
  String firstName;

  ...
}

现在考虑以下示例,它有一个应用程序定义的SDG Repository来查询Customer对象

interface CustomerRepository extends GemfireRepository<Customer, Long> {

  Customer findByLastName(String lastName);

  ...
}

SDG Repository finder/query 方法会生成并运行以下 OQL 语句:

SELECT * FROM /Customers c WHERE c.lastName = '$1'

因此,您可能希望创建一个类似于以下内容的Index

<gfe:index id="myIndex" name="CustomersLastNameIndex" expression="lastName" from="/Customers" type="HASH"/>

from子句必须引用一个有效且存在的区域,这是将Index应用于区域的方式。这并非Spring Data for Pivotal GemFire特有。这是Pivotal GemFire的一项功能。

Index type 可以是 Spring Data for Pivotal GemFire 的 IndexType 枚举定义的三个枚举值之一:FUNCTIONALHASHPRIMARY_KEY

每个枚举值对应于实际创建Index(或“定义”——您可以在下一节中找到更多关于“定义”索引的信息)时调用的QueryService create[|Key|Hash]Index方法之一。例如,如果IndexTypePRIMARY_KEY,则调用QueryService.createKeyIndex(..)来创建KEY Index

默认值为FUNCTIONAL,并导致调用QueryService.createIndex(..)方法之一。有关完整的选项集,请参阅Spring Data for Pivotal GemFire XML模式。

有关 Pivotal GemFire 中索引的更多信息,请参阅 Pivotal GemFire 用户指南中的“使用索引”。

5.6.1. 定义索引

除了在Spring容器初始化时,Spring Data for Pivotal GemFire处理Index bean定义时预先创建索引外,您还可以通过使用define属性在创建索引之前定义所有应用程序索引,如下所示:

<gfe:index id="myDefinedIndex" expression="someField" from="/SomeRegion" define="true"/>

define设置为true(默认为false)时,它实际上不会立即创建Index。所有“定义”的Index都会在Spring ApplicationContext“刷新”时,或者换句话说,当Spring容器发布ContextRefreshedEvent时,一次性创建。Spring Data for Pivotal GemFire 将自己注册为ApplicationListener,监听ContextRefreshedEvent。当事件触发时,Spring Data for Pivotal GemFire 调用QueryService.createDefinedIndexes()

一次性定义和创建索引可以提高创建索引的速度和效率。

有关更多详细信息,请参阅“一次创建多个索引”。

5.6.2. IgnoreIfExistsOverride

Spring Data for Pivotal GemFire Index配置选项中有两个需要特别提及:ignoreIfExistsoverride

这些选项分别对应于 Spring Data for Pivotal GemFire 的 XML 命名空间中 <gfe:index> 元素上的 ignore-if-existsoverride 属性。

在使用这些选项之前,请务必完全理解您正在做什么。这些选项可能会影响您的应用程序在运行时消耗的性能和资源(例如内存)。因此,在 SDG 中,这两个选项默认都已禁用(设置为false)。
这些选项仅在 Spring Data for Pivotal GemFire 中可用,用于解决 Pivotal GemFire 的已知限制。Pivotal GemFire 没有等效的选项或功能。

每个选项在行为上都有显著差异,并且完全取决于抛出的 Pivotal GemFire Index 异常类型。这也意味着,如果没有抛出 Pivotal GemFire Index 类型异常,则这两个选项都不会产生任何影响。这些选项旨在专门处理 Pivotal GemFire IndexExistsExceptionIndexNameConflictException 实例,这些实例可能由于各种(有时是模糊的)原因发生。异常的原因如下:

  • 当存在另一个定义相同但名称不同的Index时,尝试创建Index会抛出IndexExistsException

  • 当存在另一个名称相同但定义可能不同的Index时,尝试创建Index会抛出IndexNameConflictException

Spring Data for Pivotal GemFire 的默认行为始终是快速失败。因此,默认情况下不会“处理”任何Index Exception。这些Index异常被包装在SDG GemfireIndexException中并重新抛出。如果您希望Spring Data for Pivotal GemFire为您处理它们,可以将这些Index bean定义选项之一设置为true

IgnoreIfExists 总是优先于Override,主要是因为它使用的资源更少,仅仅因为它在两种异常情况下都返回“existing”Index

IgnoreIfExists 行为

当抛出IndexExistsException并且ignoreIfExists设置为true(或<gfe:index ignore-if-exists="true">)时,本index bean定义或声明本应创建的Index将简单地被忽略,并返回现有Index

返回现有Index几乎没有影响,因为index bean定义是相同的,这由Pivotal GemFire本身而不是SDG决定。

然而,这也意味着从 Pivotal GemFire 的角度来看(即使用 QueryService.getIndexes()),实际上不存在您的 index bean 定义或声明中指定的“名称”的 Index。因此,在使用查询提示时,您应该小心编写 OQL 查询语句,尤其是那些引用被忽略的应用程序 Index 的查询提示。这些查询提示需要更改。

当抛出IndexNameConflictException并且ignoreIfExists设置为true(或<gfe:index ignore-if-exists="true">)时,本index bean定义或声明本应创建的Index也会被忽略,并再次返回“现有”Index,就像抛出IndexExistsException时一样。

但是,当抛出IndexNameConflictException时,返回现有Index并忽略应用程序对Index的定义风险更大。对于IndexNameConflictException,虽然冲突索引的名称相同,但定义可能不同。这种情况可能会对应用程序特定的OQL查询产生影响,您可能会假设索引是专门针对应用程序数据访问模式和查询定义的。然而,如果同名索引的定义不同,情况可能并非如此。因此,您应该验证您的Index名称。

当被忽略的Index的定义与现有Index的定义显著不同时,SDG 会尽最大努力通知用户。但是,为了让 SDG 完成此操作,它必须能够找到现有Index,这是通过使用 Pivotal GemFire API(唯一可用的方式)查找的。
Override 行为

当抛出IndexExistsException并且override设置为true(或<gfe:index override="true">)时,Index实际上被重命名。请记住,当存在多个定义相同但名称不同的索引时,会抛出IndexExistsExceptions

Spring Data for Pivotal GemFire 只能通过使用 Pivotal GemFire 的 API 来实现这一点,即首先移除现有 Index,然后使用新名称重新创建 Index。移除或后续创建调用都可能失败。无法原子地执行这两个操作并在其中任何一个失败时回滚此联合操作。

然而,如果成功,那么您就会遇到与使用ignoreIfExists选项之前相同的问题。任何使用查询提示引用旧Index名称的现有OQL查询语句都必须更改。

当抛出IndexNameConflictException并且override设置为true(或<gfe:index override="true">)时,现有Index可能会被重新定义。我们之所以说“可能”,是因为在抛出IndexNameConflictException时,同名且现有Index可能具有完全相同的定义和名称。

如果是这样,SDG 会很聪明,即使在override情况下也会按原样返回现有Index。这种行为没有害处,因为名称和定义完全相同。当然,SDG 只有在能够找到现有Index(这取决于 Pivotal GemFire 的 API)时才能做到这一点。如果找不到,则什么也不会发生,并抛出包装了IndexNameConflictException的 SDG GemfireIndexException

但是,当现有Index的定义不同时,SDG 会尝试使用index bean定义中指定的Index定义重新创建Index。请确保这是您想要做的,并确保index bean定义符合您的期望和应用程序要求。

IndexNameConflictExceptions 是如何发生的?

IndexExistsExceptions的抛出可能并不少见,特别是在使用多个配置源配置 Pivotal GemFire 时(Spring Data for Pivotal GemFire、Pivotal GemFire 集群配置、Pivotal GemFire 原生 cache.xml、API 等)。您应该明确偏爱一种配置方法并坚持使用它。

但是,IndexNameConflictException何时抛出?

一个特殊情况是在PARTITION区域 (PR) 上定义的Index。当在PARTITION区域(例如X)上定义Index时,Pivotal GemFire 会将Index定义(和名称)分发到集群中也托管相同PARTITION区域(即“X”)的其他对等成员。此Index定义到对等成员的分发以及随后由对等成员创建此Index是按需进行的(即由托管相同 PR 的对等成员),并且是异步执行的。

在此时间窗口内,Pivotal GemFire 可能无法识别这些待处理的 PR Indexes——例如,通过调用 QueryService.getIndexes()QueryService.getIndexes(:Region),甚至 QueryService.getIndex(:Region, indexName:String)

因此,SDG 或其他 Pivotal GemFire 缓存客户端应用程序(不涉及 Spring)唯一确定知道的方法是尝试创建Index。如果创建失败并抛出IndexNameConflictException甚至IndexExistsException,则应用程序就知道存在问题。这是因为QueryService Index 创建会等待待处理的Index定义,而其他 Pivotal GemFire API 调用则不会。

无论如何,SDG 都会尽最大努力通知您发生了什么或正在发生什么,并告诉您纠正措施。鉴于所有 Pivotal GemFire QueryService.createIndex(..) 方法都是同步阻塞操作,Pivotal GemFire 的状态在抛出这些索引类型异常后应该是一致且可访问的。因此,SDG 可以根据您的配置检查系统状态并采取相应行动。

在所有其他情况下,SDG 采用快速失败策略。

5.7. 配置 DiskStore

Spring Data for Pivotal GemFire 通过disk-store元素支持DiskStore配置和创建,如以下示例所示:

<gfe:disk-store id="Example" auto-compact="true" max-oplog-size="10"
                queue-size="50" time-interval="9999">
    <gfe:disk-dir location="/disk/location/one" max-size="20"/>
    <gfe:disk-dir location="/disk/location/two" max-size="20"/>
</gfe:disk-store>

DiskStore实例用于区域的文件系统持久备份和逐出条目的溢出,以及WAN网关的持久备份。多个Pivotal GemFire组件可以共享同一个DiskStore。此外,一个DiskStore可以定义多个文件系统目录,如前面的示例所示。

有关持久性和溢出以及DiskStore实例配置选项的完整解释,请参阅Pivotal GemFire的文档。

5.8. 配置快照服务

Spring Data for Pivotal GemFire 通过使用Pivotal GemFire 的快照服务来支持缓存和区域快照。开箱即用的快照服务支持提供了一些便捷功能,以简化 Pivotal GemFire 的 缓存区域 快照服务 API 的使用。

正如Pivotal GemFire 文档所解释的,快照允许您保存并随后重新加载缓存数据,这对于在环境之间移动数据(例如从生产环境到暂存或测试环境)以在受控上下文中重现数据相关问题非常有用。您可以将 Spring Data for Pivotal GemFire 的快照服务支持与Spring 的 bean 定义配置文件结合使用,以根据需要加载特定于环境的快照数据。

Spring Data for Pivotal GemFire 对 Pivotal GemFire 快照服务的支持始于<gfe-data> XML 命名空间中的<gfe-data:snapshot-service>元素。

例如,您可以通过使用几个快照导入和一个数据导出定义来定义要加载和保存的缓存范围快照,如下所示:

<gfe-data:snapshot-service id="gemfireCacheSnapshotService">
  <gfe-data:snapshot-import location="/absolute/filesystem/path/to/import/fileOne.snapshot"/>
  <gfe-data:snapshot-import location="relative/filesystem/path/to/import/fileTwo.snapshot"/>
  <gfe-data:snapshot-export
      location="/absolute/or/relative/filesystem/path/to/export/directory"/>
</gfe-data:snapshot-service>

您可以定义任意数量的导入和导出。您可以只定义导入或只定义导出。文件位置和目录路径可以是绝对的,也可以是相对于 Spring Data for Pivotal GemFire 应用程序(即 JVM 进程的工作目录)的。

前面的示例非常简单,在这种情况下定义的快照服务引用了默认名称为gemfireCache的Pivotal GemFire缓存实例(如配置缓存中所述)。如果您将缓存bean定义命名为非默认名称,则可以使用cache-ref属性按名称引用缓存bean,如下所示:

<gfe:cache id="myCache"/>
...
<gfe-data:snapshot-service id="mySnapshotService" cache-ref="myCache">
  ...
</gfe-data:snapshot-service>

您还可以通过指定region-ref属性来为特定区域定义快照服务,如下所示:

<gfe:partitioned-region id="Example" persistent="false" .../>
...
<gfe-data:snapshot-service id="gemfireCacheRegionSnapshotService" region-ref="Example">
  <gfe-data:snapshot-import location="relative/path/to/import/example.snapshot/>
  <gfe-data:snapshot-export location="/absolute/path/to/export/example.snapshot/>
</gfe-data:snapshot-service>

当指定region-ref属性时,Spring Data for Pivotal GemFire的SnapshotServiceFactoryBeanregion-ref属性值解析为Spring容器中定义的Region bean,并创建一个RegionSnapshotService。快照导入和导出定义的运作方式相同。但是,location必须引用导出上的文件。

Pivotal GemFire 严格要求导入的快照文件在被引用之前必须实际存在。对于导出,Pivotal GemFire 会创建快照文件。如果导出的快照文件已存在,则数据将被覆盖。
Spring Data for Pivotal GemFire 在<gfe-data:snapshot-service>元素上包含一个suppress-import-on-init属性,用于禁止配置的快照服务在初始化时尝试将数据导入缓存或区域。例如,当从一个区域导出的数据用于另一个区域的导入时,这非常有用。

5.8.1. 快照位置

对于基于缓存的快照服务(即CacheSnapshotService),您通常会传递一个包含所有快照文件的目录来加载,而不是单个快照文件,正如CacheSnapshotService API中重载的load方法所示。

当然,您可以使用重载的load(:File[], :SnapshotFormat, :SnapshotOptions)方法来具体指定要加载到 Pivotal GemFire 缓存中的快照文件。

然而,Spring Data for Pivotal GemFire 认识到,典型的开发人员工作流程可能是从一个环境提取数据并导出到多个快照文件,将所有这些文件压缩,然后方便地将 zip 文件移动到另一个环境进行导入。

因此,Spring Data for Pivotal GemFire 允许您在导入时为基于cache的快照服务指定一个 jar 或 zip 文件,如下所示:

  <gfe-data:snapshot-service id="cacheBasedSnapshotService" cache-ref="gemfireCache">
    <gfe-data:snapshot-import location="/path/to/snapshots.zip"/>
  </gfe-data:snapshot-service>

Spring Data for Pivotal GemFire 方便地解压提供的 zip 文件并将其视为目录导入(加载)。

5.8.2. 快照过滤器

定义多个快照导入和导出的真正强大之处在于使用快照过滤器。快照过滤器实现了 Pivotal GemFire 的 SnapshotFilter 接口,用于过滤区域条目,以便在导入时包含到区域中,并在导出时包含到快照中。

Spring Data for Pivotal GemFire 允许您通过使用filter-ref属性或匿名嵌套 bean 定义来在导入和导出时使用快照过滤器,如以下示例所示:

<gfe:cache/>

<gfe:partitioned-region id="Admins" persistent="false"/>
<gfe:partitioned-region id="Guests" persistent="false"/>

<bean id="activeUsersFilter" class="example.gemfire.snapshot.filter.ActiveUsersFilter/>

<gfe-data:snapshot-service id="adminsSnapshotService" region-ref="Admins">
  <gfe-data:snapshot-import location="/path/to/import/users.snapshot">
    <bean class="example.gemfire.snapshot.filter.AdminsFilter/>
  </gfe-data:snapshot-import>
  <gfe-data:snapshot-export location="/path/to/export/active/admins.snapshot" filter-ref="activeUsersFilter"/>
</gfe-data:snapshot-service>

<gfe-data:snapshot-service id="guestsSnapshotService" region-ref="Guests">
  <gfe-data:snapshot-import location="/path/to/import/users.snapshot">
    <bean class="example.gemfire.snapshot.filter.GuestsFilter/>
  </gfe-data:snapshot-import>
  <gfe-data:snapshot-export location="/path/to/export/active/guests.snapshot" filter-ref="activeUsersFilter"/>
</gfe-data:snapshot-service>

此外,您可以使用ComposableSnapshotFilter类表达更复杂的快照过滤器。该类实现了Pivotal GemFire的SnapshotFilter接口以及复合软件设计模式。

简而言之,复合软件设计模式允许您组合多个相同类型的对象,并将聚合视为该对象类型的单个实例——这是一种强大而有用的抽象。

ComposableSnapshotFilter有两个工厂方法,andor。它们允许您分别使用 AND 和 OR 逻辑运算符逻辑组合单个快照过滤器。工厂方法接受SnapshotFilters列表。

以下示例显示了ComposableSnapshotFilter的定义

<bean id="activeUsersSinceFilter" class="org.springframework.data.gemfire.snapshot.filter.ComposableSnapshotFilter"
      factory-method="and">
  <constructor-arg index="0">
    <list>
      <bean class="org.example.app.gemfire.snapshot.filter.ActiveUsersFilter"/>
      <bean class="org.example.app.gemfire.snapshot.filter.UsersSinceFilter"
            p:since="2015-01-01"/>
    </list>
  </constructor-arg>
</bean>

然后,您可以继续使用oractivesUsersSinceFilter与其他过滤器组合,如下所示:

<bean id="covertOrActiveUsersSinceFilter" class="org.springframework.data.gemfire.snapshot.filter.ComposableSnapshotFilter"
      factory-method="or">
  <constructor-arg index="0">
    <list>
      <ref bean="activeUsersSinceFilter"/>
      <bean class="example.gemfire.snapshot.filter.CovertUsersFilter"/>
    </list>
  </constructor-arg>
</bean>

5.8.3. 快照事件

默认情况下,Spring Data for Pivotal GemFire 在启动时使用 Pivotal GemFire 的快照服务导入数据,并在关闭时导出数据。但是,您可能希望在 Spring 应用程序中触发定期的、基于事件的快照,用于导入或导出。

为此,Spring Data for Pivotal GemFire 定义了两个额外的 Spring 应用程序事件,分别扩展了 Spring 的 ApplicationEvent 类,用于导入和导出:ImportSnapshotApplicationEventExportSnapshotApplicationEvent

这两个应用程序事件可以针对整个 Pivotal GemFire 缓存或单个 Pivotal GemFire 区域。这些类中的构造函数接受一个可选的区域路径名(例如/Example)以及零个或多个SnapshotMetadata实例。

SnapshotMetadata数组会覆盖由<gfe-data:snapshot-import><gfe-data:snapshot-export>子元素定义的快照元数据,这些子元素在快照应用程序事件未明确提供SnapshotMetadata的情况下使用。每个独立的SnapshotMetadata实例都可以定义自己的locationfilters属性。

Spring ApplicationContext中定义的所有快照服务bean都会接收导入和导出快照应用程序事件。但是,只有匹配的快照服务bean才会处理导入和导出事件。

如果定义的快照服务bean是RegionSnapshotService并且其区域引用(由region-ref属性确定)与快照应用程序事件指定的区域路径名匹配,则基于区域的[Import|Export]SnapshotApplicationEvent匹配。

基于缓存的[Import|Export]SnapshotApplicationEvent(即没有区域路径名的快照应用程序事件)会触发所有快照服务 bean(包括任何RegionSnapshotService bean)分别执行导入或导出。

您可以使用 Spring 的 ApplicationEventPublisher 接口从应用程序中触发导入和导出快照应用程序事件,如下所示:

@Component
public class ExampleApplicationComponent {

  @Autowired
  private ApplicationEventPublisher eventPublisher;

  @Resource(name = "Example")
  private Region<?, ?> example;

  public void someMethod() {

    ...

    File dataSnapshot = new File(System.getProperty("user.dir"), "/path/to/export/data.snapshot");

    SnapshotFilter myFilter = ...;

    SnapshotMetadata exportSnapshotMetadata =
        new SnapshotMetadata(dataSnapshot, myFilter, null);

    ExportSnapshotApplicationEvent exportSnapshotEvent =
        new ExportSnapshotApplicationEvent(this, example.getFullPath(), exportSnapshotMetadata)

    eventPublisher.publishEvent(exportSnapshotEvent);

    ...
  }
}

在前面的示例中,只有/Example区域的快照服务bean会捕获并处理导出事件,将过滤后的“/Example”区域数据保存到应用程序工作目录子目录中的data.snapshot文件。

使用 Spring 应用程序事件和消息子系统是保持应用程序松散耦合的好方法。您还可以使用 Spring 的调度服务定期触发快照应用程序事件。

5.9. 配置函数服务

Spring Data for Pivotal GemFire 提供了注解支持,用于实现、注册和执行 Pivotal GemFire 函数。

Spring Data for Pivotal GemFire 还提供 XML 命名空间支持,用于注册 Pivotal GemFire 函数 以进行远程函数执行。

有关函数执行框架的更多信息,请参阅 Pivotal GemFire 的文档

Pivotal GemFire 函数被声明为 Spring bean,并且必须实现org.apache.geode.cache.execute.Function接口或扩展org.apache.geode.cache.execute.FunctionAdapter

命名空间使用熟悉的模式来声明函数,如以下示例所示:

<gfe:function-service>
  <gfe:function>
      <bean class="example.FunctionOne"/>
      <ref bean="function2"/>
  </gfe:function>
</gfe:function-service>

<bean id="function2" class="example.FunctionTwo"/>

5.10. 配置广域网网关

WAN 网关提供了一种跨地理位置同步 Pivotal GemFire 分布式系统的方式。Spring Data for Pivotal GemFire 提供 XML 命名空间支持,用于配置 WAN 网关,如以下示例所示。

5.10.1. Pivotal GemFire 7.0 中的 WAN 配置

在下面的示例中,通过向 Region 添加子元素(gateway-sendergateway-sender-ref)来为PARTITION Region 配置GatewaySendersGatewaySender可以注册EventFiltersTransportFilters

以下示例还显示了AsyncEventQueue的示例配置,该配置也必须自动连接到Region(未显示)

<gfe:partitioned-region id="region-with-inner-gateway-sender" >
    <gfe:gateway-sender remote-distributed-system-id="1">
        <gfe:event-filter>
	        <bean class="org.springframework.data.gemfire.example.SomeEventFilter"/>
        </gfe:event-filter>
        <gfe:transport-filter>
	        <bean class="org.springframework.data.gemfire.example.SomeTransportFilter"/>
        </gfe:transport-filter>
    </gfe:gateway-sender>
    <gfe:gateway-sender-ref bean="gateway-sender"/>
</gfe:partitioned-region>

<gfe:async-event-queue id="async-event-queue" batch-size="10" persistent="true" disk-store-ref="diskstore"
        maximum-queue-memory="50">
    <gfe:async-event-listener>
        <bean class="example.AsyncEventListener"/>
    </gfe:async-event-listener>
</gfe:async-event-queue>

<gfe:gateway-sender id="gateway-sender" remote-distributed-system-id="2">
    <gfe:event-filter>
        <ref bean="event-filter"/>
        <bean class="org.springframework.data.gemfire.example.SomeEventFilter"/>
    </gfe:event-filter>
    <gfe:transport-filter>
        <ref bean="transport-filter"/>
        <bean class="org.springframework.data.gemfire.example.SomeTransportFilter"/>
    </gfe:transport-filter>
</gfe:gateway-sender>

<bean id="event-filter" class="org.springframework.data.gemfire.example.AnotherEventFilter"/>
<bean id="transport-filter" class="org.springframework.data.gemfire.example.AnotherTransportFilter"/>

GatewaySender的另一端是相应的GatewayReceiver,用于接收网关事件。GatewayReceiver也可以配置EventFiltersTransportFilters,如下所示:

<gfe:gateway-receiver id="gateway-receiver" start-port="12345" end-port="23456" bind-address="192.168.0.1">
    <gfe:transport-filter>
        <bean class="org.springframework.data.gemfire.example.SomeTransportFilter"/>
    </gfe:transport-filter>
</gfe:gateway-receiver>

有关所有配置选项的详细说明,请参阅 Pivotal GemFire 文档

6. 使用注解通过Spring容器启动Pivotal GemFire

Spring Data for Pivotal GemFire (SDG) 2.0 引入了一种新的基于注解的配置模型,用于使用 Spring 容器配置和启动 Pivotal GemFire。

引入基于注解的方法来在 Spring 上下文中配置 Pivotal GemFire 的主要动机是使 Spring 应用程序开发人员能够尽可能快速轻松开始使用

我们开始吧!

如果您想更快地开始,请参阅快速入门部分。

6.1. 介绍

考虑到所有配置属性和不同的配置选项,Pivotal GemFire 可能难以正确设置和使用

不同的支持拓扑结构进一步增加了复杂性

基于注解的配置模型旨在简化所有这些及更多。

基于注解的配置模型是使用 Spring Data for Pivotal GemFire 的 XML 命名空间进行 XML 配置的替代方案。使用 XML,您可以使用 gfe XML 模式进行配置,并使用 gfe-data XML 模式进行数据访问。有关更多详细信息,请参阅“使用 Spring 容器启动 Pivotal GemFire”。

截至 SDG 2.0,基于注解的配置模型尚不支持 Pivotal GemFire WAN 组件和拓扑的配置。

与 Spring Boot 类似,Spring Data for Pivotal GemFire 的基于注解的配置模型被设计为一种主观的、约定优于配置的方法,用于使用 Pivotal GemFire。实际上,这种基于注解的配置模型是受 Spring Boot 以及其他几个 Spring 和 Spring Data 项目的集体启发。

遵循约定,所有注解都为所有配置属性提供合理且明智的默认值。给定注解属性的默认值直接对应于 Pivotal GemFire 为相同配置属性提供的默认值。

其目的是让您通过在 Spring @Configuration@SpringBootApplication 类上声明适当的注解来启用 Pivotal GemFire 功能或嵌入式服务,而无需不必要地配置大量属性即可使用该功能或服务。

再次强调,快速轻松上手 是主要目标。

但是,如果您需要,可以自定义 Pivotal GemFire 的配置元数据和行为,Spring Data for Pivotal GemFire 的基于注解的配置会悄然退让。您只需指定要调整的配置属性。此外,正如我们将在本文档后面看到的那样,有多种方法可以通过使用注解来配置 Pivotal GemFire 功能或嵌入式服务。

您可以在 org.springframework.data.gemfire.config.annotation 包中找到所有新的 SDG Java Annotations

6.2. 使用 Spring 配置 Pivotal GemFire 应用程序

与所有通过 @SpringBootApplication 注解应用程序类来启动的 Spring Boot 应用程序一样,Spring Boot 应用程序可以通过声明以下三个主要注解中的任何一个,轻松地成为 Pivotal GemFire 缓存应用程序

  • @ClientCacheApplication

  • @PeerCacheApplication

  • @CacheServerApplication

这三个注解是 Spring 应用程序开发人员使用 Pivotal GemFire 时的起点。

为了实现这些注解背后的意图,您必须理解可以使用 Pivotal GemFire 创建两种类型的缓存实例:客户端缓存或对等缓存。

您可以将 Spring Boot 应用程序配置为 Pivotal GemFire 缓存客户端,并使用 ClientCache 实例,该实例可以与现有 Pivotal GemFire 服务器集群通信,用于管理应用程序的数据。客户端-服务器拓扑是使用 Pivotal GemFire 时最常见的系统架构,您可以通过简单地使用 @ClientCacheApplication 注解您的 Spring Boot 应用程序,使其成为具有 ClientCache 实例的缓存客户端。

或者,Spring Boot 应用程序可能是 Pivotal GemFire 集群的对等成员。也就是说,应用程序本身只是管理数据的服务器集群中的另一个服务器。当您使用 @PeerCacheApplication 注解您的应用程序类时,Spring Boot 应用程序会创建一个“嵌入式”对等 Cache 实例。

通过扩展,对等缓存应用程序也可以充当 CacheServer,允许缓存客户端连接并对服务器执行数据访问操作。这可以通过使用 @CacheServerApplication 而不是 @PeerCacheApplication 注解应用程序类来实现,它会创建一个对等 Cache 实例以及允许缓存客户端连接的 CacheServer

Pivotal GemFire 服务器默认情况下不一定是缓存服务器。也就是说,服务器不一定仅仅因为它是一个服务器就被设置为服务缓存客户端。Pivotal GemFire 服务器可以是管理数据的集群的对等成员(数据节点),而不服务任何客户端,而集群中的其他对等成员除了管理数据之外,确实被设置为服务客户端。还可以将集群中的某些对等成员设置为非数据节点,称为数据访问器,它们不存储数据,但作为代理为客户端提供 CacheServers 服务。Pivotal GemFire 支持许多不同的拓扑和集群配置,但这些超出了本文档的范围。

例如,如果您想创建一个 Spring Boot 缓存客户端应用程序,请从以下内容开始

基于 Spring 的 Pivotal GemFire ClientCache 应用程序
@SpringBootApplication
@ClientCacheApplication
class ClientApplication { .. }

或者,如果您想创建一个带有嵌入式对等 Cache 实例的 Spring Boot 应用程序,其中您的应用程序将是 Pivotal GemFire 形成的集群(分布式系统)的服务器和对等成员,请从以下内容开始

基于 Spring 的 Pivotal GemFire 嵌入式对等 Cache 应用程序
@SpringBootApplication
@PeerCacheApplication
class ServerApplication { .. }

或者,您可以使用 @CacheServerApplication 注解代替 @PeerCacheApplication 来创建嵌入式对等 Cache 实例以及在 localhost 上运行的 CacheServer,监听默认缓存服务器端口 40404,如下所示

带有 CacheServer 的基于 Spring 的 Pivotal GemFire 嵌入式对等 Cache 应用程序
@SpringBootApplication
@CacheServerApplication
class ServerApplication { .. }

6.3. 客户端/服务器应用程序详细信息

客户端可以通过多种方式连接并与 Pivotal GemFire 集群中的服务器通信。最常见和推荐的方法是使用 Pivotal GemFire 定位器。

缓存客户端可以连接到 Pivotal GemFire 集群中的一个或多个定位器,而不是直接连接到 CacheServer。使用定位器而不是直接 CacheServer 连接的优点是,定位器向客户端提供有关其连接的集群的元数据。此元数据包括哪些服务器包含感兴趣的数据或哪些服务器负载最少等信息。客户端 Pool 与定位器结合使用,还提供了 CacheServer 崩溃时的故障转移功能。通过在客户端 Pool 中启用 PARTITION 区域 (PR) 单跳功能,客户端被直接路由到包含客户端请求和所需数据的服务器。
定位器也是集群中的对等成员。定位器实际上构成了 Pivotal GemFire 节点集群。也就是说,所有由定位器连接的节点都是集群中的对等体,新成员使用定位器加入集群并查找其他成员。

默认情况下,当创建 ClientCache 实例时,Pivotal GemFire 会设置一个连接到在 localhost 上运行并监听端口 40404CacheServer 的“DEFAULT”PoolCacheServer 监听端口 40404,接受所有系统 NIC 上的连接。您无需执行任何特殊操作即可使用客户端-服务器拓扑。只需使用 @CacheServerApplication 注解您的服务器端 Spring Boot 应用程序,并使用 @ClientCacheApplication 注解您的客户端 Spring Boot 应用程序,即可开始使用。

如果您愿意,甚至可以使用 Gfsh 的 start server 命令启动服务器。您的 Spring Boot @ClientCacheApplication 仍然可以连接到服务器,无论它是如何启动的。但是,您可能更喜欢使用 Spring Data for Pivotal GemFire 的方法配置和启动服务器,因为正确注解的 Spring Boot 应用程序类更直观且更易于调试。

作为应用程序开发人员,您无疑会希望自定义 Pivotal GemFire 设置的“DEFAULT”Pool,以便可能连接到一个或多个定位器,如下例所示

使用定位器的基于 Spring 的 Pivotal GemFire ClientCache 应用程序
@SpringBootApplication
@ClientCacheApplication(locators = {
    @Locator(host = "boombox" port = 11235),
    @Locator(host = "skullbox", port = 12480)
})
class ClientApplication { .. }

除了 locators 属性之外,@ClientCacheApplication 注解还具有 servers 属性。servers 属性可用于指定一个或多个嵌套的 @Server 注解,如果需要,允许缓存客户端直接连接到一个或多个服务器。

您可以使用 locatorsservers 属性,但不能同时使用两者(Pivotal GemFire 强制执行此规则)。

您还可以通过使用 @EnablePool@EnablePools 注解来配置额外的 Pool 实例(除了当使用 @ClientCacheApplication 注解创建 ClientCache 实例时 Pivotal GemFire 提供的“DEFAULT”Pool)。

@EnablePools 是一个复合注解,它在单个类上聚合了多个嵌套的 @EnablePool 注解。Java 8 及更早版本不允许在单个类上声明多个相同类型的注解。

以下示例使用 @EnablePool@EnablePools 注解

使用多个命名 Pools 的基于 Spring 的 Pivotal GemFire ClientCache 应用程序
@SpringBootApplication
@ClientCacheApplication(logLevel = "info")
@EnablePool(name = "VenusPool", servers = @Server(host = "venus", port = 48484),
    min-connections = 50, max-connections = 200, ping-internal = 15000,
    prSingleHopEnabled = true, readTimeout = 20000, retryAttempts = 1,
    subscription-enable = true)
@EnablePools(pools = {
    @EnablePool(name = "SaturnPool", locators = @Locator(host="skullbox", port=20668),
        subsription-enabled = true),
    @EnablePool(name = "NeptunePool", severs = {
            @Server(host = "saturn", port = 41414),
            @Server(host = "neptune", port = 42424)
        }, min-connections = 25))
})
class ClientApplication { .. }

name 属性是 @EnablePool 注解唯一必需的属性。正如我们稍后将看到的,name 属性的值对应于在 Spring 容器中创建的 Pool bean 的名称以及用于引用相应配置属性的名称。它也是 Pivotal GemFire 注册和使用的 Pool 的名称。

类似地,在服务器上,您可以配置多个 CacheServers,客户端可以连接到它们,如下所示

使用多个命名 CacheServers 的基于 Spring 的 Pivotal GemFire CacheServer 应用程序
@SpringBootApplication
@CacheSeverApplication(logLevel = "info", autoStartup = true, maxConnections = 100)
@EnableCacheServer(name = "Venus", autoStartup = true,
    hostnameForClients = "venus", port = 48484)
@EnableCacheServers(servers = {
    @EnableCacheServer(name = "Saturn", hostnameForClients = "saturn", port = 41414),
    @EnableCacheServer(name = "Neptune", hostnameForClients = "neptune", port = 42424)
})
class ServerApplication { .. }
@EnablePools 类似,@EnableCacheServers 是一个复合注解,用于在单个类上聚合多个 @EnableCacheServer 注解。同样,Java 8 及更早版本不允许在单个类上声明多个相同类型的注解。

细心的读者可能已经注意到,在所有情况下,您都为所有主机名、端口和面向配置的注解属性指定了硬编码值。当应用程序被推广并部署到不同的环境(例如从 DEV 到 QA 到 STAGING 到 PROD)时,这并不理想。

下一节将介绍如何处理在运行时确定的动态配置。

6.4. 配置和启动定位器

除了 Pivotal GemFire 缓存应用程序,您还可以创建 Pivotal GemFire 定位器应用程序。

Pivotal GemFire 定位器是一个 JVM 进程,允许节点作为对等成员加入 Pivotal GemFire 集群。定位器还使客户端能够发现集群中的服务器。定位器向客户端提供元数据,以在集群成员之间均匀地平衡负载,启用单跳数据访问操作,以及其他功能。

关于定位器的完整讨论超出了本文档的范围。建议读者阅读 Pivotal GemFire 用户指南,了解有关定位器及其在集群中作用的更多详细信息。

要配置和启动独立定位器进程,请执行以下操作

Spring Boot, Pivotal GemFire 定位器应用程序
@SpringBootApplication
@LocatorApplication(port = 12345)
class LocatorApplication { ... }

您可以在集群中启动多个定位器。唯一的要求是成员名称在集群中必须是唯一的。使用 @LocatorApplication 注解的 name 属性来相应地命名集群中的成员定位器。或者,您可以在 Spring Boot 的 application.properties 中设置 spring.data.gemfire.locator.name 属性。

此外,如果您在同一台机器上 fork 多个定位器,则必须确保每个定位器都在唯一的端口上启动。设置 port 注解属性或 spring.data.gemfire.locator.port 属性。

然后,您可以在由定位器或定位器加入的集群中启动 1 个或多个 Pivotal GemFire 对等缓存成员,这些成员也使用 Spring 配置和引导,如下所示

Spring Boot,Pivotal GemFire CacheServer 应用程序,由 localhost 上的定位器,端口 12345 加入
@SpringBootApplication
@CacheServerApplication(locators = "localhost[12345]")
class ServerApplication { ... }

同样,您可以根据需要启动任意数量的 ServerApplication 类,它们都由我们上面的定位器加入。您只需确保成员名称唯一即可。

@LocatorApplication 用于配置和启动独立的 Pivotal GemFire Locator 应用程序进程。此进程只能是 Locator,不能是其他任何东西。如果您尝试使用缓存实例启动 Locator,SDG 将抛出错误。

如果您想同时启动一个缓存实例和一个嵌入式定位器,那么您应该使用 @EnableLocator 注解。

在开发过程中启动嵌入式定位器很方便。但是,强烈建议您在生产环境中运行独立的定位器进程以实现高可用性。如果集群中所有定位器都宕机,那么集群将保持完整,但是,无法加入新成员,这对于线性横向扩展以满足需求非常重要。

有关更多详细信息,请参阅“配置嵌入式定位器”一节。

6.5. 使用 Configurers 进行运行时配置

设计基于注解的配置模型的另一个目标是保留注解属性中的类型安全。例如,如果配置属性可以表示为 int(例如端口号),那么该属性的类型应该是 int

不幸的是,这不利于运行时动态和可解析的配置。

Spring 的一个更精细的特性是能够在 Spring 容器中配置 bean 时,在配置元数据的属性或属性中使用属性占位符和 SpEL 表达式。但是,这将要求所有注解属性都是 String 类型,从而放弃了类型安全,这是不可取的。

因此,Spring Data for Pivotal GemFire 借鉴了 Spring 中另一个常用模式,即 Configurers。Spring Web MVC 中提供了许多不同的 Configurer 接口,包括 org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer

Configurers 设计模式使应用程序开发人员能够在启动时收到回调,以自定义组件或 bean 的配置。框架回调用户提供的代码以在运行时调整配置。此模式的更常见用途之一是根据应用程序的运行时环境提供条件配置。

Spring Data for Pivotal GemFire 提供了多个 Configurer 回调接口,用于在运行时,在 Spring 管理的由注解创建的 bean 初始化之前,自定义基于注解的配置元数据的不同方面

  • CacheServerConfigurer

  • ClientCacheConfigurer

  • ContinuousQueryListenerContainerConfigurer

  • DiskStoreConfigurer

  • IndexConfigurer

  • PeerCacheConfigurer

  • PoolConfigurer

  • RegionConfigurer

  • GatewayReceiverConfigurer

  • GatewaySenderConfigurer

例如,您可以使用 CacheServerConfigurerClientCacheConfigurer 分别自定义您的 Spring Boot CacheServerClientCache 应用程序使用的端口号。

考虑以下服务器应用程序示例

使用 CacheServerConfigurer 自定义 Spring Boot CacheServer 应用程序
@SpringBootApplication
@CacheServerApplication(name = "SpringServerApplication")
class ServerApplication {

  @Bean
  CacheServerConfigurer cacheServerPortConfigurer(
          @Value("${gemfire.cache.server.host:localhost}") String cacheServerHost
          @Value("${gemfire.cache.server.port:40404}") int cacheServerPort) {

      return (beanName, cacheServerFactoryBean) -> {
          cacheServerFactoryBean.setBindAddress(cacheServerHost);
          cacheServerFactoryBean.setHostnameForClients(cacheServerHost);
          cacheServerFactoryBean.setPort(cacheServerPort);
      };
  }
}

接下来,考虑以下客户端应用程序示例

使用 ClientCacheConfigurer 自定义 Spring Boot ClientCache 应用程序
@SpringBootApplication
@ClientCacheApplication
class ClientApplication {

  @Bean
  ClientCacheConfigurer clientCachePoolPortConfigurer(
          @Value("${gemfire.cache.server.host:localhost}") String cacheServerHost
          @Value("${gemfire.cache.server.port:40404}") int cacheServerPort) {

      return (beanName, clientCacheFactoryBean) ->
          clientCacheFactoryBean.setServers(Collections.singletonList(
              new ConnectionEndpoint(cacheServerHost, cacheServerPort)));
  }
}

通过使用提供的 Configurers,您可以在启动期间接收回调,以进一步自定义由相关注解在运行时启用的配置。

此外,当 Configurer 在 Spring 容器中声明为 bean 时,bean 定义可以利用其他 Spring 容器特性,例如属性占位符、通过在工厂方法参数上使用 @Value 注解的 SpEL 表达式等。

Spring Data for Pivotal GemFire 提供的所有 Configurer 在回调中都会接收两部分信息:由注解在 Spring 容器中创建的 bean 的名称,以及注解用于创建和配置 Pivotal GemFire 组件的 FactoryBean 的引用(例如,ClientCache 实例由 ClientCacheFactoryBean 创建和配置)。

SDG FactoryBeans 是 SDG 公共 API 的一部分,如果您没有提供这种新的基于注解的配置模型,您将在 Spring 的基于 Java 的容器配置中使用它们。事实上,注解本身也在使用这些相同的 FactoryBeans 进行配置。因此,本质上,注解是一个外观,提供了一个额外的抽象层,以方便使用。

鉴于 Configurer 可以像任何其他 POJO 一样声明为常规 bean 定义,您可以结合不同的 Spring 配置选项,例如将 Spring Profiles 与使用属性占位符和 SpEL 表达式的 Conditions 结合使用。这些和其他巧妙的功能让您可以创建更复杂和灵活的配置。

但是,Configurers 并不是唯一的选择。

6.6. 使用 Properties 进行运行时配置

除了 Configurers,基于注解的配置模型中的每个注解属性都关联一个相应的配置属性(前缀为 spring.data.gemfire.),该属性可以在 Spring Boot 的 application.properties 文件中声明。

在前述示例的基础上,客户端的 application.properties 文件将定义以下属性集

客户端 application.properties
spring.data.gemfire.cache.log-level=info
spring.data.gemfire.pool.Venus.servers=venus[48484]
spring.data.gemfire.pool.Venus.max-connections=200
spring.data.gemfire.pool.Venus.min-connections=50
spring.data.gemfire.pool.Venus.ping-interval=15000
spring.data.gemfire.pool.Venus.pr-single-hop-enabled=true
spring.data.gemfire.pool.Venus.read-timeout=20000
spring.data.gemfire.pool.Venus.subscription-enabled=true
spring.data.gemfire.pool.Saturn.locators=skullbox[20668]
spring.data.gemfire.pool.Saturn.subscription-enabled=true
spring.data.gemfire.pool.Neptune.servers=saturn[41414],neptune[42424]
spring.data.gemfire.pool.Neptune.min-connections=25

相应的服务器的 application.properties 文件将定义以下属性

服务器 application.properties
spring.data.gemfire.cache.log-level=info
spring.data.gemfire.cache.server.port=40404
spring.data.gemfire.cache.server.Venus.port=43434
spring.data.gemfire.cache.server.Saturn.port=41414
spring.data.gemfire.cache.server.Neptune.port=41414

然后您可以将 @ClientCacheApplication 类简化为以下内容

Spring @ClientCacheApplication
@SpringBootApplication
@ClientCacheApplication
@EnablePools(pools = {
    @EnablePool(name = "Venus"),
    @EnablePool(name = "Saturn"),
    @EnablePool(name = "Neptune")
})
class ClientApplication { .. }

此外,@CacheServerApplication 类变为以下内容

Spring @CacheServerApplication
@SpringBootApplication
@CacheServerApplication(name = "SpringServerApplication")
@EnableCacheServers(servers = {
    @EnableCacheServer(name = "Venus"),
    @EnableCacheServer(name = "Saturn"),
    @EnableCacheServer(name = "Neptune")
})
class ServerApplication { .. }

前面的示例说明了为什么“命名”基于注解的 bean 很重要(除了在某些情况下是必需的)。这样做可以从 XML、属性和 Java 中引用 Spring 容器中的 bean。甚至可以将注解定义的 bean 注入到应用程序类中,无论出于何种目的,如下例所示

@Component
class MyApplicationComponent {

  @Resource(name = "Saturn")
  CacheServer saturnCacheServer;

  ...
}

同样,命名一个注解定义的 bean 允许您编写一个 Configurer 来自定义一个特定的“命名”bean,因为 beanName 是传递给回调的两个参数之一。

通常,关联的注解属性属性有两种形式:“命名”属性和“未命名”属性。

以下示例展示了这种安排

spring.data.gemfire.cache.server.bind-address=10.105.20.1
spring.data.gemfire.cache.server.Venus.bind-address=10.105.20.2
spring.data.gemfire.cache.server.Saturn...
spring.data.gemfire.cache.server.Neptune...

虽然上面有三个命名的 CacheServers,但也有一个未命名的 CacheServer 属性,它为任何未指定的属性值提供默认值,即使对于“命名”的 CacheServers 也是如此。因此,虽然“Venus”设置并覆盖了自己的 bind-address,但“Saturn”和“Neptune”继承了“未命名”的 spring.data.gemfire.cache.server.bind-address 属性。

请参阅注解的 Javadoc,了解哪些注解属性支持基于属性的配置,以及它们是否支持“命名”属性而不是默认的“未命名”属性。

6.6.1. Properties 中的 Properties

按照 Spring 的惯例,您甚至可以用其他 Properties 来表达 Properties,无论是通过以下示例展示了如何在 application.properties 文件中设置嵌套属性

属性中的属性
spring.data.gemfire.cache.server.port=${gemfire.cache.server.port:40404}

以下示例展示了如何在 Java 中设置嵌套属性

属性占位符嵌套
@Bean
CacheServerConfigurer cacheServerPortConfigurer(
    @Value("${gemfire.cache.server.port:${some.other.property:40404}}")
    int cacheServerPort) {
  ...
}
属性占位符嵌套可以任意深入。

6.7. 配置嵌入式服务

Pivotal GemFire 提供了根据用例启动应用程序所需的许多不同嵌入式服务的能力。

6.7.1. 配置嵌入式定位器

如前所述,Pivotal GemFire 定位器被客户端用于连接和查找集群中的服务器。此外,加入现有集群的新成员使用定位器来查找其对等体。

对于应用程序开发人员来说,在开发 Spring Boot 和 Spring Data for Pivotal GemFire 应用程序时,启动一个由两三个 Pivotal GemFire 服务器组成的小型集群通常很方便。您无需启动单独的定位器进程,而是可以使用 @EnableLocator 注解您的 Spring Boot @CacheServerApplication 类,如下所示

Spring, 运行嵌入式定位器的 Pivotal GemFire CacheServer 应用程序
@SpringBootApplication
@CacheServerApplication
@EnableLocator
class ServerApplication { .. }

@EnableLocator 注解在运行于 localhost 并监听默认 Locator 端口 10334 的 Spring Pivotal GemFire CacheServer 应用程序中启动一个嵌入式 Locator。您可以通过使用相应的注解属性自定义嵌入式 Locator 绑定的 host(绑定地址)和 port

或者,您可以通过在 application.properties 中设置相应的 spring.data.gemfire.locator.hostspring.data.gemfire.locator.port 属性来设置 @EnableLocator 属性。

然后,您可以通过以下方式连接到此定位器,启动其他支持 Spring Boot @CacheServerApplication 的应用程序

Spring, 连接到定位器的 Pivotal GemFire CacheServer 应用程序
@SpringBootApplication
@CacheServerApplication(locators = "localhost[10334]")
class ServerApplication { .. }

您甚至可以将前面显示的两个应用程序类合并到一个类中,并使用您的 IDE 创建不同的运行配置文件配置,通过使用 Java 系统属性启动相同类的不同实例,并进行稍微修改的配置,如下所示

运行嵌入式定位器并连接到定位器的 Spring CacheServer 应用程序
@SpringBootApplication
@CacheServerApplication(locators = "localhost[10334]")
public class ServerApplication {

  public static void main(String[] args) {
    SpringApplication.run(ServerApplication.class);
  }

  @EnableLocator
  @Profile("embedded-locator")
  static class Configuration { }

}

然后,对于每个运行配置文件,您可以设置和更改以下系统属性

IDE 运行配置文件配置
spring.data.gemfire.name=SpringCacheServerOne
spring.data.gemfire.cache.server.port=41414
spring.profiles.active=embedded-locator

ServerApplication 类的运行配置文件中只有 1 个应设置 -Dspring.profiles.active=embedded-locator Java 系统属性。然后,您可以更改其他运行配置文件的 ..name..cache.server.port,并在本地系统上运行一个由 Pivotal GemFire 服务器组成的小型集群(分布式系统)。

@EnableLocator 注解仅用于开发时注解,而不是应用程序开发人员在生产环境中使用。我们强烈建议将定位器作为独立的进程在集群中运行,以实现高可用性。

有关 Pivotal GemFire Locator 如何工作的更多详细信息,请参见此处

6.7.2. 配置嵌入式管理器

Pivotal GemFire Manager 是集群中的另一个对等成员或节点,负责集群“管理”。管理包括创建 RegionsIndexesDiskStores 等,以及监控集群组件的运行时操作和行为。

管理器允许启用 JMX 的客户端(例如 Gfsh shell 工具)连接到管理器以管理集群。也可以使用 JDK 提供的工具(如 JConsole 或 JVisualVM)连接到管理器,因为它们也是启用 JMX 的客户端。

也许您也希望将前面显示的 Spring @CacheServerApplication 也启用为 Manager。为此,请使用 @EnableManager 注解您的 Spring @Configuration@SpringBootApplication 类。

默认情况下,Manager 绑定到 localhost,监听默认的 Manager 端口 1099。Manager 的几个方面可以通过注解属性或相应的属性进行配置。

以下示例展示了如何在 Java 中创建嵌入式管理器

运行嵌入式管理器的 Spring CacheServer 应用程序
@SpringBootApplication
@CacheServerApplication(locators = "localhost[10334]")
public class ServerApplication {

  public static void main(String[] args) {
    SpringApplication.run(ServerApplication.class);
  }

  @EnableLocator
  @EnableManager
  @Profile("embedded-locator-manager")
  static class Configuration { }

}

使用上述类,您甚至可以使用 Gfsh 连接到小型集群并对其进行管理,如下所示

$ gfsh
    _________________________     __
   / _____/ ______/ ______/ /____/ /
  / /  __/ /___  /_____  / _____  /
 / /__/ / ____/  _____/ / /    / /
/______/_/      /______/_/    /_/    1.2.1

Monitor and Manage {data-store-name}

gfsh>connect
Connecting to Locator at [host=localhost, port=10334] ..
Connecting to Manager at [host=10.99.199.5, port=1099] ..
Successfully connected to: [host=10.99.199.5, port=1099]

gfsh>list members
         Name          | Id
---------------------- | ----------------------------------------------------
SpringCacheServerOne   | 10.99.199.5(SpringCacheServerOne:14842)<ec><v0>:1024
SpringCacheServerTwo   | 10.99.199.5(SpringCacheServerTwo:14844)<v1>:1025
SpringCacheServerThree | 10.99.199.5(SpringCacheServerThree:14846)<v2>:1026

因为我们还启用了嵌入式 Locator,所以我们可以通过 Locator 间接连接到 Manager。Locator 允许 JMX 客户端连接并找到集群中的 Manager。如果不存在,Locator 会承担 Manager 的角色。但是,如果不存在 Locator,我们将需要通过以下方式直接连接到 Manager

Gfsh connect 命令直接连接到管理器
gfsh>connect --jmx-manager=localhost[1099]
@EnableLocator 注解类似,@EnableManager 注解也仅用于开发时注解,而不是应用程序开发人员在生产环境中使用。我们强烈建议 Manager,像 Locator 一样,作为独立的、独立的和专用进程在集群中运行。

有关 Pivotal GemFire 管理和监控的更多详细信息,请参见此处

6.7.3. 配置嵌入式 HTTP 服务器

Pivotal GemFire 也能够运行嵌入式 HTTP 服务器。当前的实现由 Eclipse Jetty 支持。

嵌入式 HTTP 服务器用于托管 Pivotal GemFire 的管理 (Admin) REST API (非公开宣传的 API)、开发者 REST APIPulse 监控 Web 应用程序

但是,要使用任何这些 Pivotal GemFire 提供的 Web 应用程序,您的系统必须安装完整的 Pivotal GemFire,并且必须将 GEODE_HOME 环境变量设置为您的安装目录。

要启用嵌入式 HTTP 服务器,请将 @EnableHttpService 注解添加到任何 @PeerCacheApplication@CacheServerApplication 注解类中,如下所示

运行嵌入式 HTTP 服务器的 Spring CacheServer 应用程序
@SpringBootApplication
@CacheServerApplication
@EnableHttpService
public class ServerApplication { .. }

默认情况下,嵌入式 HTTP 服务器监听端口 7070 以处理 HTTP 客户端请求。当然,您可以根据需要使用注解属性或相应的配置属性来调整端口。

有关 HTTP 支持和所提供服务的更多详细信息,请参阅前面的链接。

6.7.4. 配置嵌入式 Memcached 服务器(Gemcached)

Pivotal GemFire 还实现了 Memcached 协议,能够为 Memcached 客户端提供服务。也就是说,Memcached 客户端可以连接到 Pivotal GemFire 集群并执行 Memcached 操作,就好像集群中的 Pivotal GemFire 服务器是真正的 Memcached 服务器一样。

要启用嵌入式 Memcached 服务,请将 @EnableMemcachedServer 注解添加到任何 @PeerCacheApplication@CacheServerApplication 注解类中,如下所示

运行嵌入式 Memcached 服务器的 Spring CacheServer 应用程序
@SpringBootApplication
@CacheServerApplication
@EnabledMemcachedServer
public class ServerApplication { .. }

有关 Pivotal GemFire 的 Memcached 服务(称为“Gemcached”)的更多详细信息,请参见此处

6.7.5. 配置嵌入式 Redis 服务器

Pivotal GemFire 还实现了 Redis 服务器协议,该协议使 Redis 客户端能够连接并与 Pivotal GemFire 服务器集群通信以发出 Redis 命令。截至本文撰写之时,Pivotal GemFire 中的 Redis 服务器协议支持仍处于实验阶段。

要启用嵌入式 Redis 服务,请将 @EnableRedisServer 注解添加到任何 @PeerCacheApplication@CacheServerApplication 注解类中,如下所示

运行嵌入式 Redis 服务器的 Spring CacheServer 应用程序
@SpringBootApplication
@CacheServerApplication
@EnableRedisServer
public class ServerApplication { .. }
您必须在 Spring [Boot] 应用程序的类路径中明确声明 org.apache.geode:geode-redis 模块。

有关 Pivotal GemFire 的 Redis 适配器的更多详细信息,请参见此处

6.8. 配置日志

通常,需要开启日志记录以准确了解 Pivotal GemFire 正在做什么以及何时进行。

要启用日志记录,请使用 @EnableLogging 注解您的应用程序类,并设置适当的属性或关联属性,如下所示

已启用日志记录的 Spring ClientCache 应用程序
@SpringBootApplication
@ClientCacheApplication
@EnableLogging(logLevel="info", logFile="/absolute/file/system/path/to/application.log)
public class ClientApplication { .. }

虽然 logLevel 属性可以与所有基于缓存的应用程序注解一起指定(例如,@ClientCacheApplication(logLevel="info")),但使用 @EnableLogging 注解自定义日志行为更容易。

此外,您可以通过在 application.properties 中设置 spring.data.gemfire.logging.level 属性来配置 log-level

有关更多详细信息,请参阅 @EnableLogging 注解 Javadoc

6.9. 配置统计信息

为了更深入地了解 Pivotal GemFire 的运行时情况,您可以启用统计信息。收集统计数据有助于在发生复杂问题时进行系统分析和故障排除,这些问题通常具有分布式特性,并且时间是一个关键因素。

启用统计信息后,您可以使用 Pivotal GemFire 的 VSD (Visual Statistics Display) 工具来分析收集到的统计数据。

要启用统计信息,请使用 @EnableStatistics 注解您的应用程序类,如下所示

已启用统计信息的 Spring ClientCache 应用程序
@SpringBootApplication
@ClientCacheApplication
@EnableStatistics
public class ClientApplication { .. }

在服务器上启用统计信息在评估性能时特别有价值。为此,请使用 @EnableStatistics 注解您的 @PeerCacheApplication@CacheServerApplication 类。

您可以使用 @EnableStatistics 注解属性或相关属性来自定义统计数据收集过程。

有关更多详细信息,请参阅 @EnableStatistics 注解 Javadoc

有关 Pivotal GemFire 统计信息的更多详细信息,请参见此处

6.10. 配置 PDX

Pivotal GemFire 最强大的功能之一是 PDX 序列化。虽然 PDX 的完整讨论超出了本文档的范围,但使用 PDX 进行序列化是 Java 序列化的更好替代方案,具有以下优点

  • PDX 使用集中式类型注册表,使对象的序列化字节更加紧凑。

  • PDX 是一种中立的序列化格式,允许 Java 和 Native 客户端对相同的数据集进行操作。

  • PDX 支持版本控制,允许添加或删除对象字段,而不会影响使用更改后的 PDX 序列化对象的旧版本或新版本的现有应用程序,且不会丢失数据。

  • PDX 允许在 OQL 查询投影和谓词中单独访问对象字段,而无需先反序列化对象。

通常,Pivotal GemFire 中需要序列化,无论何时数据在客户端和服务器之间或集群中的对等体之间在正常分发和复制过程中传输,以及当数据溢出或持久化到磁盘时。

启用 PDX 序列化比修改所有应用程序域对象类型以实现 java.io.Serializable 简单得多,尤其是在不希望对应用程序域模型施加此类限制,或者无法控制正在序列化的对象时,在使用第三方库(例如,考虑带有 Coordinate 类型的地理空间 API)时尤其如此。

要启用 PDX,请使用 @EnablePdx 注解您的应用程序类,如下所示

已启用 PDX 的 Spring ClientCache 应用程序
@SpringBootApplication
@ClientCacheApplication
@EnablePdx
public class ClientApplication { .. }

通常,应用程序的域对象类型要么实现 org.apache.geode.pdx.PdxSerializable 接口,要么您可以实现并注册 org.apache.geode.pdx.PdxSerializer 接口的非侵入式实现来处理所有需要序列化的应用程序域对象类型。

不幸的是,Pivotal GemFire 只允许注册一个 PdxSerializer,这意味着所有应用程序域对象类型都需要由单个 PdxSerializer 实例处理。然而,这是一个严重的反模式和不可维护的做法。

尽管 Pivotal GemFire 只能注册单个 PdxSerializer 实例,但为每个应用程序域对象类型创建一个 PdxSerializer 实现是合理的。

通过使用组合软件设计模式,您可以提供一个 PdxSerializer 接口的实现,该实现聚合了所有特定于应用程序域对象类型的 PdxSerializer 实例,但作为一个单一的 PdxSerializer 实例并注册它。

您可以在 Spring 容器中将此组合 PdxSerializer 声明为托管 bean,并使用 @EnablePdx 注解中的 serializerBeanName 属性通过其 bean 名称引用此组合 PdxSerializer。Spring Data for Pivotal GemFire 会为您处理向 Pivotal GemFire 注册它的事宜。

以下示例展示了如何创建自定义组合 PdxSerializer

启用 PDX 并使用自定义复合 PdxSerializer 的 Spring ClientCache 应用程序
@SpringBootApplication
@ClientCacheApplication
@EnablePdx(serializerBeanName = "compositePdxSerializer")
public class ClientApplication {

  @Bean
  PdxSerializer compositePdxSerializer() {
      return new CompositePdxSerializerBuilder()...
  }
}

也可以将 Pivotal GemFire 的 org.apache.geode.pdx.ReflectionBasedAutoSerializer 声明为 Spring 上下文中的 bean 定义。

或者,您应该使用 Spring Data for Pivotal GemFire 更强大的 org.springframework.data.gemfire.mapping.MappingPdxSerializer,它使用 Spring Data 映射元数据和基础设施应用于序列化过程,以实现比单独反射更高效的处理。

PDX 的许多其他方面和功能可以通过 @EnablePdx 注解属性或关联的配置属性进行调整。

有关更多详细信息,请参阅 @EnablePdx 注解 Javadoc

6.11. 配置 Pivotal GemFire 属性

虽然许多 gemfire.properties 已方便地封装并在 SDG 基于注解的配置模型中通过注解进行抽象,但不太常用的 Pivotal GemFire 属性仍然可以通过 @EnableGemFireProperties 注解访问。

使用 @EnableGemFireProperties 注解您的应用程序类很方便,是创建 gemfire.properties 文件或在启动应用程序时在命令行上将 Pivotal GemFire 属性设置为 Java 系统属性的不错替代方案。

我们建议在将应用程序部署到生产环境时,将这些 Pivotal GemFire 属性设置在 gemfire.properties 文件中。但是,在开发阶段,为了原型设计、调试和测试目的,单独设置这些属性可能会很方便。

一些不常用的 Pivotal GemFire 属性示例,您通常不需要担心,包括但不限于:ack-wait-thresholddisable-tcpsocket-buffer-size 等。

要单独设置任何 Pivotal GemFire 属性,请使用 @EnableGemFireProperties 注解您的应用程序类,并使用相应属性设置您希望更改的 Pivotal GemFire 属性(与 Pivotal GemFire 设置的默认值不同),如下所示

已设置特定 Pivotal GemFire 属性的 Spring ClientCache 应用程序
@SpringBootApplication
@ClientCacheApplication
@EnableGemFireProperties(conflateEvents = true, socketBufferSize = 16384)
public class ClientApplication { .. }

请记住,一些 Pivotal GemFire 属性是客户端特定的(例如 conflateEvents),而另一些是服务器特定的(例如 distributedSystemIdenableNetworkPartitionDetectionenforceUniqueHostmemberTimeoutredundancyZone 等)。

有关 Pivotal GemFire 属性的更多详细信息,请参见此处

6.12. 配置区域

到目前为止,除了 PDX 之外,我们的讨论都集中在配置 Pivotal GemFire 的更多管理功能上:创建缓存实例、启动嵌入式服务、启用日志和统计、配置 PDX,以及使用 gemfire.properties 来影响低级配置和行为。虽然所有这些配置选项都很重要,但它们都与您的应用程序没有直接关系。换句话说,我们仍然需要一个地方来存储应用程序数据并使其普遍可用和可访问。

Pivotal GemFire 将缓存中的数据组织成区域(Regions)。您可以将区域视为关系数据库中的一个表。通常,一个区域应该只存储单一类型的对象,这更有利于构建有效的索引和编写查询。我们稍后会介绍索引

以前,Spring Data for Pivotal GemFire 用户需要通过编写非常详细的 Spring 配置元数据来显式定义和声明应用程序用于存储数据的区域,无论是使用 SDG API 中的 FactoryBeans 结合 Spring 的基于 Java 的容器配置还是使用XML

以下示例演示了如何在 Java 中配置区域 bean

使用 Spring 基于 Java 的容器配置的区域 bean 定义示例
@Configuration
class GemFireConfiguration {

  @Bean("Example")
  PartitionedRegionFactoryBean exampleRegion(GemFireCache gemfireCache) {

      PartitionedRegionFactoryBean<Long, Example> exampleRegion =
          new PartitionedRegionFactoryBean<>();

      exampleRegion.setCache(gemfireCache);
      exampleRegion.setClose(false);
      exampleRegion.setPersistent(true);

      return exampleRegion;
  }

  ...
}

以下示例演示了如何在 XML 中配置相同的区域 bean

使用 SDG XML 命名空间的区域 bean 定义示例
<gfe:partitioned-region id="exampleRegion" name="Example" persistent="true">
    ...
</gfe:partitioned-region>

虽然 Java 或 XML 配置都不算太难指定,但两者都可能很麻烦,特别是当应用程序需要大量区域时。许多基于关系数据库的应用程序可能有数百甚至数千个表。

手动定义和声明所有这些区域将是繁琐且容易出错的。现在,有更好的方法。

现在,您可以根据应用程序域对象(实体)本身来定义和配置区域。除非您需要更精细的控制,否则不再需要显式定义 Spring 配置元数据中的 Region bean 定义。

为了简化区域创建,Spring Data for Pivotal GemFire 将 Spring Data Repositories 的使用与基于注解的配置的表达能力结合起来,使用新的 @EnableEntityDefinedRegions 注解。

大多数 Spring Data 应用程序开发人员应该已经熟悉Spring Data Repository 抽象和 Spring Data for Pivotal GemFire 的实现/扩展,该实现/扩展已专门定制以优化 Pivotal GemFire 的数据访问操作。

首先,应用程序开发人员通过定义应用程序的域对象(实体)开始,如下所示

建模书籍的应用程序域对象类型
@Region("Books")
class Book {

  @Id
  private ISBN isbn;

  private Author author;

  private Category category;

  private LocalDate releaseDate;

  private Publisher publisher;

  private String title;

}

接下来,您通过扩展 Spring Data Commons org.springframework.data.repository.CrudRepository 接口来定义 Books 的基本存储库,如下所示

书籍存储库
interface BookRepository extends CrudRepository<Book, ISBN> { .. }

org.springframe.data.repository.CrudRepository 是一个数据访问对象 (DAO),它提供基本数据访问操作 (CRUD) 以及对简单查询的支持(例如 findById(..))。您可以通过在存储库接口上声明查询方法来定义其他更复杂的查询(例如,List<BooK> findByAuthor(Author author);)。

在底层,当 Spring 容器启动时,Spring Data for Pivotal GemFire 会提供应用程序存储库接口的实现。只要您遵循约定,SDG 甚至会实现您定义的查询方法。

现在,当您定义 Book 类时,您还通过在实体类型上声明 Spring Data for Pivotal GemFire 映射注解 @Region 来指定 Book 实例映射(存储)的区域。当然,如果存储库接口的类型参数中引用的实体类型(本例中为 Book)没有用 @Region 注解,则名称将从实体类型的简单类名(本例中也为 Book)派生。

Spring Data for Pivotal GemFire 使用映射上下文,该上下文包含应用程序中定义的所有实体的映射元数据,以确定运行时所需的所有区域。

要启用和使用此功能,请使用 @EnableEntityDefinedRegions 注解应用程序类,如下所示

实体定义的区域配置
@SpringBootApplication
@ClientCacheApplication
@EnableEntityDefinedRegions(basePackages = "example.app.domain")
@EnableGemfireRepositories(basePackages = "example.app.repo")
class ClientApplication { .. }
当在应用程序中使用 Spring Data Repositories 时,从实体类创建区域是最有用的。Spring Data for Pivotal GemFire 的 Repository 支持通过 @EnableGemfireRepositories 注解启用,如前一个示例所示。
目前,只有明确使用 @Region 注解的实体类才会被扫描到并创建区域。如果实体类未明确映射 @Region,则不会创建区域。

默认情况下,@EnableEntityDefinedRegions 注解递归扫描实体类,从声明 @EnableEntityDefinedRegions 注解的配置类包开始。

但是,通常通过使用包含应用程序实体类的包名设置 basePackages 属性来限制扫描期间的搜索。

或者,您可以使用更类型安全的 basePackageClasses 属性来指定要扫描的包,方法是将该属性设置为包中包含实体类的实体类型,或者使用专门为识别要扫描的包而创建的非实体占位符类。

以下示例展示了如何指定要扫描的实体类型

使用实体类类型的实体定义区域配置
@SpringBootApplication
@ClientCacheApplication
@EnableGemfireRepositories
@EnableEntityDefinedRegions(basePackageClasses = {
    example.app.books.domain.Book.class,
    example.app.customers.domain.Customer.class
})
class ClientApplication { .. }

除了指定扫描的起始位置外,像 Spring 的 @ComponentScan 注解一样,您还可以指定 includeexclude 过滤器,其语义与 org.springframework.context.annotation.ComponentScan.Filter 注解完全相同。

有关更多详细信息,请参阅 @EnableEntityDefinedRegions 注解 Javadoc

6.12.1. 配置特定类型区域

Pivotal GemFire 支持许多不同类型的区域。每种类型都对应于区域的DataPolicy,它决定了区域中的数据将如何管理(即分布式、复制等)。

其他配置设置(例如区域的 scope)也会影响数据管理方式。有关更多详细信息,请参阅 Pivotal GemFire 用户指南中的“存储和分发选项”

当您使用通用 @Region 映射注解注释您的应用程序域对象类型时,Spring Data for Pivotal GemFire 会决定创建哪种类型的区域。SDG 的默认策略在确定要创建的区域类型时会考虑缓存类型。

例如,如果您使用 @ClientCacheApplication 注解将应用程序声明为 ClientCache,SDG 默认会创建一个客户端 PROXY Region。或者,如果您使用 @PeerCacheApplication@CacheServerApplication 注解将应用程序声明为对等 Cache,SDG 默认会创建一个服务器 PARTITION Region

当然,在必要时,您总是可以覆盖默认值。为了覆盖 Spring Data for Pivotal GemFire 应用的默认值,引入了四个新的 Region 映射注解

  • @ClientRegion

  • @LocalRegion

  • @PartitionRegion

  • @ReplicateRegion

@ClientRegion 映射注解是客户端应用程序特有的。上面列出的所有其他区域映射注解只能用于具有嵌入式对等 Cache 的服务器应用程序。

客户端应用程序有时需要创建和使用仅本地区域,也许是为了聚合来自其他区域的数据,以便在本地分析数据并执行应用程序代表用户执行的某些功能。在这种情况下,数据不需要分发回服务器,除非其他应用程序需要访问结果。此区域甚至可以是临时的,使用后即被丢弃,这可以通过区域本身的空闲超时 (TTI) 和生存时间 (TTL) 过期策略来实现。(有关过期策略的更多信息,请参阅“配置过期”。)

区域级别的空闲超时(TTI)和生存时间(TTL)过期策略与条目级别的 TTI 和 TTL 过期策略是独立的且不同的。

无论如何,如果您想创建一个仅本地的客户端区域,其中数据不会分发回服务器上具有相同名称的相应区域,您可以声明 @ClientRegion 映射注解并将 shortcut 属性设置为 ClientRegionShortcut.LOCAL,如下所示

具有仅本地客户端区域的 Spring ClientCache 应用程序
@ClientRegion(shortcut = ClientRegionShortcut.LOCAL)
class ClientLocalEntityType { .. }

所有区域类型特定注解都提供额外的属性,这些属性既在区域类型之间是通用的,也特定于该区域类型。例如,PartitionRegion 注解中的 collocatedWithredundantCopies 属性仅适用于服务器端 PARTITION 区域。

有关 Pivotal GemFire 区域类型的更多详细信息,请参见此处

6.12.2. 配置集群定义区域

除了 @EnableEntityDefinedRegions 注解之外,Spring Data for Pivotal GemFire 还提供了反向注解 @EnableClusterDefinedRegions。与其根据应用程序用例 (UC) 和需求定义和驱动实体类来构建区域(最常见和逻辑的方法),您可以选择从集群中已定义的区域声明您的区域,您的 ClientCache 应用程序将连接到该集群。

这允许您将配置集中化,使用服务器集群作为数据定义的主要来源,并确保集群的所有客户端应用程序都具有一致的配置。这在快速扩展大量相同客户端应用程序以处理云管理环境中增加的负载时特别有用。

这个想法是,不是由客户端应用程序驱动数据字典,而是用户使用 Pivotal GemFire 的 Gfsh CLI shell 工具定义区域。这有一个额外的好处,即当向集群添加额外的对等体时,它们也将拥有并共享相同的配置,因为 Pivotal GemFire 的 Cluster Configuration Service 会记住它。

例如,用户可以在 Gfsh 中定义一个区域,如下所示

使用 Gfsh 定义区域
gfsh>create region --name=Books --type=PARTITION
 Member   | Status
--------- | --------------------------------------
ServerOne | Region "/Books" created on "ServerOne"
ServerTwo | Region "/Books" created on "ServerTwo"

gfsh>list regions
List of regions
---------------
Books

gfsh>describe region --name=/Books
..........................................................
Name            : Books
Data Policy     : partition
Hosting Members : ServerTwo
                  ServerOne

Non-Default Attributes Shared By Hosting Members

 Type  |    Name     | Value
------ | ----------- | ---------
Region | size        | 0
       | data-policy | PARTITION

借助 Pivotal GemFire 的 Cluster Configuration Service,添加到服务器集群以处理增加的负载(在后端)的任何其他对等成员也将具有相同的配置,例如

向集群添加额外的对等成员
gfsh>list members
  Name    | Id
--------- | ----------------------------------------------
Locator   | 10.0.0.121(Locator:68173:locator)<ec><v0>:1024
ServerOne | 10.0.0.121(ServerOne:68242)<v3>:1025
ServerTwo | 10.0.0.121(ServerTwo:68372)<v4>:1026

gfsh>start server --name=ServerThree --log-level=config --server-port=41414
Starting a Geode Server in /Users/you/geode/cluster/ServerThree...
...
Server in /Users/you/geode/cluster/ServerThree... on 10.0.0.121[41414] as ServerThree is currently online.
Process ID: 68467
Uptime: 3 seconds
Geode Version: 1.2.1
Java Version: 1.8.0_152
Log File: /Users/you/geode/cluster/ServerThree/ServerThree.log
JVM Arguments: -Dgemfire.default.locators=10.0.0.121[10334]
  -Dgemfire.use-cluster-configuration=true
  -Dgemfire.start-dev-rest-api=false
  -Dgemfire.log-level=config
  -XX:OnOutOfMemoryError=kill -KILL %p
  -Dgemfire.launcher.registerSignalHandlers=true
  -Djava.awt.headless=true
  -Dsun.rmi.dgc.server.gcInterval=9223372036854775806
Class-Path: /Users/you/geode/cluster/apache-geode-1.2.1/lib/geode-core-1.2.1.jar
  :/Users/you/geode/cluster/apache-geode-1.2.1/lib/geode-dependencies.jar

gfsh>list members
   Name     | Id
----------- | ----------------------------------------------
Locator     | 10.0.0.121(Locator:68173:locator)<ec><v0>:1024
ServerOne   | 10.0.0.121(ServerOne:68242)<v3>:1025
ServerTwo   | 10.0.0.121(ServerTwo:68372)<v4>:1026
ServerThree | 10.0.0.121(ServerThree:68467)<v5>:1027

gfsh>describe member --name=ServerThree
Name        : ServerThree
Id          : 10.0.0.121(ServerThree:68467)<v5>:1027
Host        : 10.0.0.121
Regions     : Books
PID         : 68467
Groups      :
Used Heap   : 37M
Max Heap    : 3641M
Working Dir : /Users/you/geode/cluster/ServerThree
Log file    : /Users/you/geode/cluster/ServerThree/ServerThree.log
Locators    : 10.0.0.121[10334]

Cache Server Information
Server Bind              :
Server Port              : 41414
Running                  : true
Client Connections       : 0

如您所见,“ServerThree”现在拥有“Books”区域。如果任何或所有服务器宕机,它们在重新启动时将拥有相同的配置以及“Books”区域。

在客户端,可能会启动许多书店客户端应用程序实例来处理书店在线服务中的书籍。 “Books”区域可能是实现书店应用程序服务所需的许多不同区域之一。Spring Data for GemFire 允许从集群定义客户端应用程序区域,而不是必须单独创建和配置每个区域,如下所示

使用 @EnableClusterDefinedRegions 从集群定义客户端区域
@ClientCacheApplication
@EnableClusterDefinedRegions
class BookStoreClientApplication {

    public static void main(String[] args) {
        ....
    }

    ...
}
@EnableClusterDefinedRegions 只能在客户端使用。
您可以使用 clientRegionShortcut 注解属性来控制在客户端创建的区域类型。默认情况下,会创建一个客户端 PROXY 区域。将 clientRegionShortcut 设置为 ClientRegionShortcut.CACHING_PROXY 以实现“近缓存”。此设置适用于从集群定义区域创建的所有客户端区域。如果您想控制从集群上定义的区域创建的客户端区域的单个设置(如数据策略),那么您可以实现一个 RegionConfigurer,其中包含基于区域名称的自定义逻辑。

然后,在您的应用程序中使用“Books”区域变得很简单。您可以直接注入“Books”区域,如下所示

使用“图书”区域
@org.springframework.stereotype.Repository
class BooksDataAccessObject {

    @Resource(name = "Books")
    private Region<ISBN, Book> books;

    // implement CRUD and queries with the "Books" Region
}

或者,甚至可以根据应用程序域类型(实体)Book 定义 Spring Data Repository 定义,该定义映射到“Books”区域,如下所示

使用带有 SD Repository 的“图书”区域
interface BookRepository extends CrudRepository<Book, ISBN> {
    ...
}

然后,您可以将您的自定义 BooksDataAccessObjectBookRepository 注入到您的应用程序服务组件中,以执行所需的任何业务功能。

6.12.3. 配置驱逐

使用 Pivotal GemFire 管理数据是一项活跃的任务。通常需要进行调优,并且您必须结合使用多种功能(例如,驱逐和过期)才能有效地管理 Pivotal GemFire 中的内存数据。

鉴于 Pivotal GemFire 是一个内存数据网格 (IMDG),数据在内存中管理并分发到参与集群的其他节点,以最大限度地减少延迟,最大化吞吐量并确保数据高度可用。由于并非所有应用程序的数据通常都能完全放入内存(即使在整个节点集群中,更不用说单个节点上),您可以通过向集群添加新节点来增加容量。这通常被称为线性横向扩展(而不是纵向扩展,后者意味着增加更多内存、更多 CPU、更多磁盘或更多网络带宽——基本上是增加所有系统资源以处理负载)。

尽管如此,即使拥有一个节点集群,通常也必须只在内存中保留最重要的数据。内存不足,甚至接近满容量,很少是好事(如果曾发生过的话)。停止世界的 GC 或更糟糕的 OutOfMemoryErrors 会使您的应用程序戛然而止。

因此,为了帮助管理内存并保留最重要的数据,Pivotal GemFire 支持最近最少使用 (LRU) 驱逐。也就是说,Pivotal GemFire 根据区域条目上次访问的时间使用最近最少使用算法驱逐区域条目。

要启用驱逐,请使用 @EnableEviction 注解应用程序类,如下所示

已启用驱逐的 Spring 应用程序
@SpringBootApplication
@PeerCacheApplication
@EnableEviction(policies = {
    @EvictionPolicy(regionNames = "Books", action = EvictionActionType.INVALIDATE),
    @EvictionPolicy(regionNames = { "Customers", "Orders" }, maximum = 90,
        action = EvictionActionType.OVERFLOW_TO_DISK,
        type = EvictonPolicyType.HEAP_PERCENTAGE)
})
class ServerApplication { .. }

驱逐策略通常设置在服务器中的区域上。

如前所示,policies 属性可以指定一个或多个嵌套的 @EvictionPolicy 注解,每个注解都单独适用于一个或多个需要应用驱逐策略的区域。

此外,您可以引用 Pivotal GemFire 的 org.apache.geode.cache.util.ObjectSizer 接口的自定义实现,该实现可以在 Spring 容器中定义为 bean,并通过使用 objectSizerName 属性按名称引用。

ObjectSizer 允许您定义用于评估和确定存储在区域中的对象大小的标准。

有关驱逐配置选项的完整列表,请参阅 @EnableEviction 注解 Javadoc

有关 Pivotal GemFire 驱逐的更多详细信息,请参见此处

6.12.4. 配置过期

除了驱逐,过期也可以通过允许存储在区域中的条目过期来管理内存。Pivotal GemFire 支持生存时间 (TTL) 和空闲超时 (TTI) 条目过期策略。

Spring Data for Pivotal GemFire 基于注解的过期配置基于 Spring Data for Pivotal GemFire 1.5 版中添加的早期和现有条目过期注解支持

本质上,Spring Data for Pivotal GemFire 的过期注解支持基于 Pivotal GemFire org.apache.geode.cache.CustomExpiry 接口的自定义实现。此 o.a.g.cache.CustomExpiry 实现检查存储在区域中的用户应用程序域对象是否存在类型级别的过期注解。

Spring Data for Pivotal GemFire 提供以下过期注解

  • 过期

  • IdleTimeoutExpiration

  • TimeToLiveExpiration

一个应用程序域对象类型可以被一个或多个过期注解注释,如下所示

应用程序域对象特定的过期策略
@Region("Books")
@TimeToLiveExpiration(timeout = 30000, action = "INVALIDATE")
class Book { .. }

要启用过期,请使用 @EnableExpiration 注解应用程序类,如下所示

已启用过期的 Spring 应用程序
@SpringBootApplication
@PeerCacheApplication
@EnableExpiration
class ServerApplication { .. }

除了应用程序域对象类型级别的过期策略之外,您还可以使用 @EnableExpiration 注解直接和单独配置基于区域的过期策略,如下所示

具有区域特定过期策略的 Spring 应用程序
@SpringBootApplication
@PeerCacheApplication
@EnableExpiration(policies = {
    @ExpirationPolicy(regionNames = "Books", types = ExpirationType.TIME_TO_LIVE),
    @ExpirationPolicy(regionNames = { "Customers", "Orders" }, timeout = 30000,
        action = ExpirationActionType.LOCAL_DESTROY)
})
class ServerApplication { .. }

前面的示例设置了 BooksCustomersOrders 区域的过期策略。

过期策略通常设置在服务器中的区域上。

有关过期配置选项的完整列表,请参阅 @EnableExpiration 注解 Javadoc

有关 Pivotal GemFire 过期的更多详细信息,请参见此处

6.12.5. 配置压缩

除了驱逐过期之外,您还可以为数据区域配置压缩以减少内存消耗。

Pivotal GemFire 允许您通过使用可插拔的 Compressors 或不同的压缩编解码器来压缩内存中的 Region 值。Pivotal GemFire 默认使用 Google 的 Snappy 压缩库。

要启用压缩,请使用 @EnableCompression 注解应用程序类,如下所示

启用 Region 压缩的 Spring 应用程序
@SpringBootApplication
@ClientCacheApplication
@EnableCompression(compressorBeanName = "MyCompressor", regionNames = { "Customers", "Orders" })
class ClientApplication { .. }
compressorBeanNameregionNames 属性都不是必需的。

compressorBeanName 默认为 SnappyCompressor,启用 Pivotal GemFire 的 SnappyCompressor

regionNames 属性是一个 Region 名称数组,指定了启用压缩的 Region。默认情况下,如果未显式设置 regionNames 属性,所有 Region 都会压缩值。

或者,您可以在 application.properties 文件中使用 spring.data.gemfire.cache.compression.compressor-bean-namespring.data.gemfire.cache.compression.region-names 属性来设置和配置这些 @EnableCompression 注解属性的值。
要使用 Pivotal GemFire 的 Region 压缩功能,您必须在应用程序的 pom.xml 文件(对于 Maven)或 build.gradle 文件(对于 Gradle)中包含 org.iq80.snappy:snappy 依赖项。这仅在您使用 Pivotal GemFire 对 Region 压缩的默认支持时才需要,该支持默认使用 SnappyCompressor。当然,如果您使用其他压缩库,则需要在应用程序的类路径中包含该压缩库的依赖项。此外,您需要实现 Pivotal GemFire 的 Compressor 接口来适应您选择的压缩库,将其定义为 Spring 压缩器中的 bean,并将 compressorBeanName 设置为此自定义 bean 定义。

有关更多详细信息,请参见 @EnableCompression 注解 Javadoc

有关 Pivotal GemFire 压缩的更多详细信息,请参见此处

6.12.6. 配置堆外内存

减少 JVM 堆内存压力并最小化 GC 活动的另一种有效方法是使用 Pivotal GemFire 的堆外内存支持。

Region 条目不存储在 JVM 堆上,而是存储在系统的主内存中。正如 Pivotal GemFire 用户指南中所述,当存储的对象大小统一、大多小于 128K 且不需要频繁反序列化时,堆外内存通常效果最好。

要启用堆外,请使用 @EnableOffHeap 注解应用程序类,如下所示

启用堆外的 Spring 应用程序
@SpringBootApplication
@PeerCacheApplication
@EnableOffHeap(memorySize = 8192m regionNames = { "Customers", "Orders" })
class ServerApplication { .. }

memorySize 属性是必需的。memorySize 属性的值指定 Region 可以使用的主内存量,以兆字节 (m) 或千兆字节 (g) 为单位。

regionNames 属性是一个 Region 名称数组,指定了在主内存中存储条目的 Region。默认情况下,如果未显式设置 regionNames 属性,所有 Region 都使用主内存。

或者,您可以在 application.properties 文件中使用 spring.data.gemfire.cache.off-heap.memory-sizespring.data.gemfire.cache.off-heap.region-names 属性来设置和配置这些 @EnableOffHeap 注解属性的值。

有关更多详细信息,请参见 @EnableOffHeap 注解 Javadoc

6.12.7. 配置磁盘存储

或者,您可以将 Region 配置为将数据持久化到磁盘。您还可以将 Region 配置为在逐出 Region 条目时将数据溢出到磁盘。在这两种情况下,都需要 DiskStore 来持久化和/或溢出数据。当没有为具有持久性或溢出的 Region 配置显式 DiskStore 时,Pivotal GemFire 使用 DEFAULT DiskStore

我们建议在将数据持久化和/或溢出到磁盘时定义特定于 Region 的 DiskStores

Spring Data for Pivotal GemFire 提供注解支持,通过使用 @EnableDiskStore@EnableDiskStores 注解应用程序类来定义和创建应用程序 Region DiskStores

@EnableDiskStores 是一个复合注解,用于聚合一个或多个 @EnableDiskStore 注解。

例如,虽然 Book 信息可能主要由来自某些外部数据源(如 Amazon)的引用数据组成,但 Order 数据很可能具有事务性,并且是应用程序需要保留的数据(如果事务量足够高,甚至可能溢出到磁盘)——至少任何图书出版商和作者都希望如此。

使用 @EnableDiskStore 注解,您可以如下定义和创建 DiskStore

定义 DiskStore 的 Spring 应用程序
@SpringBootApplication
@PeerCacheApplication
@EnableDiskStore(name = "OrdersDiskStore", autoCompact = true, compactionThreshold = 70,
    maxOplogSize = 512, diskDirectories = @DiskDiretory(location = "/absolute/path/to/order/disk/files"))
class ServerApplication { .. }

同样,可以使用复合注解 @EnableDiskStores 定义多个 DiskStore

与 Spring Data for Pivotal GemFire 基于注解的配置模型中的其他注解一样,@EnableDiskStore@EnableDiskStores 都有许多属性以及相关的配置属性,用于在运行时自定义创建的 DiskStores

此外,@EnableDiskStores 注解定义了适用于所有从 @EnableDiskStore 注解创建的 DiskStore 的某些常见 DiskStore 属性,这些注解与 @EnableDiskStores 注解本身组合。单个 DiskStore 配置会覆盖特定的全局设置,但 @EnableDiskStores 注解方便地定义了适用于该注解聚合的所有 DiskStore 的常见配置属性。

Spring Data for Pivotal GemFire 还提供了 DiskStoreConfigurer 回调接口,可以在 Java 配置中声明并用于代替配置属性在运行时自定义 DiskStore,如下例所示

具有自定义 DiskStore 配置的 Spring 应用程序
@SpringBootApplication
@PeerCacheApplication
@EnableDiskStore(name = "OrdersDiskStore", autoCompact = true, compactionThreshold = 70,
    maxOplogSize = 512, diskDirectories = @DiskDiretory(location = "/absolute/path/to/order/disk/files"))
class ServerApplication {

  @Bean
  DiskStoreConfigurer ordersDiskStoreDiretoryConfigurer(
          @Value("${orders.disk.store.location}") String location) {

      return (beanName, diskStoreFactoryBean) -> {

          if ("OrdersDiskStore".equals(beanName) {
              diskStoreFactoryBean.setDiskDirs(Collections.singletonList(new DiskDir(location));
          }
      }
  }
}

有关可用属性以及相关配置属性的更多详细信息,请参见 @EnableDiskStore@EnableDiskStores 注解 Javadoc。

有关 Pivotal GemFire Region 持久性和溢出(使用 DiskStores)的更多详细信息,请参见此处

6.12.8. 配置索引

除非数据可以访问,否则将数据存储在 Region 中没有多大用处。

除了 Region.get(key) 操作(特别是在预先知道键的情况下),数据通常通过对包含数据的 Region 执行查询来检索。使用 Pivotal GemFire,查询是使用对象查询语言 (OQL) 编写的,客户端希望访问的特定数据集在查询的谓词中表达(例如,SELECT * FROM /Books b WHERE b.author.name = 'Jon Doe')。

通常,没有索引的查询效率低下。在没有索引的情况下执行查询时,Pivotal GemFire 执行相当于全表扫描的操作。

索引是为查询谓词中使用的对象上的字段创建和维护的,以匹配查询投影所表达的感兴趣的数据。可以创建不同类型的索引,例如索引和哈希索引。

Spring Data for Pivotal GemFire 使得在存储和访问数据的 Region 上创建索引变得容易。我们不再像以前那样通过使用 Spring 配置显式声明 Index bean 定义,而是可以在 Java 中创建 Index bean 定义,如下所示

使用 Java 配置的索引 bean 定义
@Bean("BooksIsbnIndex")
IndexFactoryBean bookIsbnIndex(GemFireCache gemfireCache) {

    IndexFactoryBean bookIsbnIndex = new IndexFactoryBean();

    bookIsbnIndex.setCache(gemfireCache);
    bookIsbnIndex.setName("BookIsbnIndex");
    bookIsbnIndex.setExpression("isbn");
    bookIsbnIndex.setFrom("/Books"));
    bookIsbnIndex.setType(IndexType.KEY);

    return bookIsbnIndex;
}

或者,我们可以使用 XML 创建 Index bean 定义,如下所示

使用 XML 的索引 bean 定义
<gfe:index id="BooksIsbnIndex" expression="isbn" from="/Books" type="KEY"/>

但是,现在您可以直接在应用程序域对象类型的字段上定义索引,您知道这些字段将用于查询谓词以加快这些查询的速度。您甚至可以为应用程序存储库接口上用户定义查询方法生成的 OQL 查询应用索引。

重新使用前面示例中的 Book 实体类,我们可以使用 @Indexed 注解 Book 上我们知道在 BookRepository 接口中用查询方法定义的查询中使用的字段,如下所示

使用索引建模书籍的应用程序域对象类型
@Region("Books")
class Book {

  @Id
  private ISBN isbn;

  @Indexed
  private Author author;

  private Category category;

  private LocalDate releaseDate;

  private Publisher publisher;

  @LuceneIndexed
  private String title;

}

在我们的新 Book 类定义中,我们使用 @Indexed 注解了 author 字段,并使用 @LuceneIndexed 注解了 title 字段。此外,isbn 字段之前已用 Spring Data 的 @Id 注解,该注解标识了包含 Book 实例唯一标识符的字段,并且在 Spring Data for Pivotal GemFire 中,@Id 注解字段或属性在存储条目时用作 Region 中的键。

  • @Id 注解字段或属性会导致创建 Pivotal GemFire KEY 索引。

  • @Indexed 注解字段或属性会导致创建 Pivotal GemFire HASH 索引(默认)。

  • @LuceneIndexed 注解字段或属性会导致创建 Pivotal GemFire Lucene 索引,用于使用 Pivotal GemFire 的 Lucene 集成和支持进行基于文本的搜索。

@Indexed 注解在未设置任何属性的情况下使用时,索引的 nameexpressionfromClause 派生自添加了 @Indexed 注解的类的字段或属性。expression 就是字段或属性的名称。fromClause 派生自域对象类上的 @Region 注解,如果未指定 @Region 注解,则派生自域对象类的简单名称。

当然,您可以显式设置任何 @Indexed 注解属性以覆盖 Spring Data for Pivotal GemFire 提供的默认值。

使用自定义索引建模书籍的应用程序域对象类型
@Region("Books")
class Book {

  @Id
  private ISBN isbn;

  @Indexed(name = "BookAuthorNameIndex", expression = "author.name", type = "FUNCTIONAL")
  private Author author;

  private Category category;

  private LocalDate releaseDate;

  private Publisher publisher;

  @LuceneIndexed(name = "BookTitleIndex", destory = true)
  private String title;

}

索引的 name(未显式设置时自动生成)也用作在 Spring 容器中为索引注册的 bean 的名称。如有必要,此索引 bean 甚至可以通过名称注入到另一个应用程序组件中。

生成的索引名称遵循此模式:<Region Name><Field/Property Name><Index Type>Idx。例如,author 索引的名称将是 BooksAuthorHashIdx

要启用索引,请使用 @EnableIndexing 注解应用程序类,如下所示

启用索引的 Spring 应用程序
@SpringBootApplication
@PeerCacheApplication
@EnableEntityDefinedRegions
@EnableIndexing
class ServerApplication { .. }
除非同时声明 @EnableEntityDefinedRegions,否则 @EnablingIndexing 注解无效。实质上,索引是从实体类类型上的字段或属性定义的,并且必须扫描实体类以检查实体字段和属性是否存在索引注解。如果没有此扫描,则无法找到索引注解。我们还强烈建议您限制扫描范围。

虽然 Spring Data for Pivotal GemFire 存储库尚不支持 Lucene 查询,但 SDG 通过使用熟悉的 Spring 模板设计模式为 Pivotal GemFire Lucene 查询提供了全面的支持

最后,我们以一些额外的提示来结束本节,在使用索引时请记住这些提示

  • 虽然执行 OQL 查询不需要 OQL 索引,但执行 Lucene 基于文本的搜索需要 Lucene 索引。

  • OQL 索引不会持久化到磁盘。它们只在内存中维护。因此,当 Pivotal GemFire 节点重新启动时,必须重建索引。

  • 您还需要注意维护索引相关的开销,特别是因为索引完全存储在内存中,尤其是在 Region 条目更新时。索引“维护”可以配置为异步任务。

当您的 Spring 应用程序重新启动时,索引必须重建,您可以使用的另一个优化是首先预先定义所有索引,然后一次性创建所有索引,这在 Spring Data for Pivotal GemFire 中发生在 Spring 容器刷新时。

您可以通过将 @EnableIndexing 注解上的 define 属性设置为 true 来预先定义索引,然后一次性创建它们。

有关更多详细信息,请参见 Pivotal GemFire 用户指南中的 “一次创建多个索引”

创建合理的索引是一项重要的任务,因为设计不当的索引可能弊大于利。

有关配置选项的完整列表,请参见 @Indexed 注解和 @LuceneIndexed 注解 Javadoc。

有关 Pivotal GemFire OQL 查询的更多详细信息,请参见此处

有关 Pivotal GemFire 索引的更多详细信息,请参见此处

有关 Pivotal GemFire Lucene 查询的更多详细信息,请参见此处

6.13. 配置连续查询

Pivotal GemFire 的另一个非常重要和有用的功能是 连续查询

在支持互联网的万物互联世界中,事件和数据流无处不在。能够实时处理大量数据流并对事件作出反应是许多应用程序日益重要的需求。一个例子是自动驾驶汽车。能够实时接收、过滤、转换、分析和处理数据是实时应用程序的关键区别和特征。

幸运的是,Pivotal GemFire 在这方面超前于时代。通过使用连续查询 (CQ),客户端应用程序可以表达它感兴趣的数据或事件,并注册监听器以在事件发生时处理和处理这些事件。客户端应用程序可能感兴趣的数据以 OQL 查询的形式表示,其中查询谓词用于过滤或识别感兴趣的数据。当数据更改或添加并与注册的 CQ 的查询谓词中定义的条件匹配时,客户端应用程序将收到通知。

Spring Data for Pivotal GemFire 使得定义和注册 CQ 以及相关的监听器以处理和处理 CQ 事件变得容易,而无需 Pivotal GemFire 的所有冗余管道。SDG 新的基于注解的 CQ 配置建立在 连续查询监听器容器中现有连续查询支持的基础上。

例如,假设图书出版商希望注册兴趣并在任何时候 Book 的订单(需求)超过当前库存(供应)时收到通知。然后出版商的打印应用程序可能会注册以下 CQ

带有已注册 CQ 和监听器的 Spring ClientCache 应用程序。
@SpringBootApplication
@ClientCacheApplication(subcriptionEnabled = true)
@EnableContinuousQueries
class PublisherPrintApplication {

    @ContinuousQuery(name = "DemandExceedsSupply", query =
       "SELECT book.* FROM /Books book, /Inventory inventory
        WHERE book.title = 'How to crush it in the Book business like Amazon"
        AND inventory.isbn = book.isbn
        AND inventory.available < (
            SELECT sum(order.lineItems.quantity)
            FROM /Orders order
            WHERE order.status = 'pending'
            AND order.lineItems.isbn = book.isbn
        )
    ")
    void handleSupplyProblem(CqEvent event) {
        // start printing more books, fast!
    }
}

要启用连续查询,请使用 @EnableContinuousQueries 注解您的应用程序类。

定义连续查询包括使用 @ContinuousQuery 注解任何 Spring @Component 注解的 POJO 类方法(与 SDG 的 Function 注解的 POJO 方法类似)。使用 @ContinuousQuery 注解定义的带有 CQ 的 POJO 方法在任何时候添加或更改与查询谓词匹配的数据时都会被调用。

此外,POJO 方法签名应遵循 ContinuousQueryListenerContinuousQueryListenerAdapter 部分中概述的要求。

有关可用属性和配置设置的更多详细信息,请参见 @EnableContinuousQueries@ContinuousQuery 注解 Javadoc。

有关 Spring Data for Pivotal GemFire 连续查询支持的更多详细信息,请参见此处

有关 Pivotal GemFire 连续查询的更多详细信息,请参见此处

6.14. 配置 Spring 的缓存抽象

通过 Spring Data for Pivotal GemFire,Pivotal GemFire 可以用作 Spring 缓存抽象中的缓存提供程序。

在 Spring 的缓存抽象中,缓存注解(例如 @Cacheable)标识在调用可能昂贵的操作之前执行缓存查找的缓存。应用程序服务方法的结果在操作调用后被缓存。

在 Spring Data for Pivotal GemFire 中,Spring Cache 直接对应于 Pivotal GemFire Region。在调用任何缓存注解的应用程序服务方法之前,Region 必须存在。这适用于 Spring 的任何缓存注解(即 @Cacheable@CachePut@CacheEvict),这些注解标识在服务操作中使用的缓存。

例如,我们的出版商的销售点 (PoS) 应用程序可能具有一项功能,用于在销售交易期间确定或查找 BookPrice,如下例所示

@Service
class PointOfSaleService

  @Cacheable("BookPrices")
  Price runPriceCheckFor(Book book) {
      ...
  }

  @Transactional
  Receipt checkout(Order order) {
      ...
  }

  ...
}

为了让您在使用 Spring Data for Pivotal GemFire 和 Spring 的缓存抽象时更轻松,基于注解的配置模型中添加了两个新功能。

考虑以下 Spring 缓存配置

使用 Pivotal GemFire 作为缓存提供程序启用缓存
@EnableCaching
class CachingConfiguration {

  @Bean
  GemfireCacheManager cacheManager(GemFireCache gemfireCache) {

      GemfireCacheManager cacheManager = new GemfireCacheManager();

      cacheManager.setCache(gemfireCache);

      return cacheManager;
  }

  @Bean("BookPricesCache")
  ReplicatedRegionFactoryBean<Book, Price> bookPricesRegion(GemFireCache gemfireCache) {

    ReplicatedRegionFactoryBean<Book, Price> bookPricesRegion =
        new ReplicatedRegionFactoryBean<>();

    bookPricesRegion.setCache(gemfireCache);
    bookPricesRegion.setClose(false);
    bookPricesRegion.setPersistent(false);

    return bookPricesRegion;
  }

  @Bean("PointOfSaleService")
  PointOfSaleService pointOfSaleService(..) {
      return new PointOfSaleService(..);
  }
}

使用 Spring Data for Pivotal GemFire 的新功能,您可以将相同的缓存配置简化为以下内容

启用 Pivotal GemFire 缓存
@EnableGemfireCaching
@EnableCachingDefinedRegions
class CachingConfiguration {

  @Bean("PointOfSaleService")
  PointOfSaleService pointOfSaleService(..) {
      return new PointOfSaleService(..);
  }
}

首先,@EnableGemfireCaching 注解取代了 Spring @EnableCaching 注解以及在 Spring 配置中声明显式 CacheManager bean 定义(名为“cacheManager”)的需要。

其次,@EnableCachingDefinedRegions 注解,就像“配置 Region”中描述的 @EnableEntityDefinedRegions 注解一样,检查整个 Spring 应用程序,缓存注解的服务组件以识别应用程序在运行时需要的所有缓存,并在应用程序启动时为这些缓存在 Pivotal GemFire 中创建 Region。

创建的 Region 是创建 Region 的应用程序进程的本地 Region。如果应用程序是对等 Cache,则 Region 仅存在于应用程序节点上。如果应用程序是 ClientCache,则 SDG 会创建客户端 PROXY Region,并期望那些同名的 Region 已经存在于集群中的服务器上。

SDG 无法使用 Spring CacheResolver 在运行时解析操作中使用的缓存来确定服务方法所需的缓存。
SDG 还支持应用程序服务组件上的 JCache (JSR-107) 缓存注解。有关代替 JCache 缓存注解使用的等效 Spring 缓存注解,请参见核心 Spring Framework 参考指南

有关在 Spring 的缓存抽象中使用 Pivotal GemFire 作为缓存提供程序的更多详细信息,请参阅 “Spring 缓存抽象支持” 部分。

有关 Spring 的缓存抽象的更多详细信息,请参见此处

6.15. 配置集群配置推送

这可能是 Spring Data for Pivotal GemFire 中最令人兴奋的新功能。

当客户端应用程序类用 @EnableClusterConfiguration 注解时,客户端应用程序在 Spring 容器中定义和声明为 bean 的任何 Region 或索引都会“推送”到客户端连接到的服务器集群。不仅如此,这个“推送”是以 Pivotal GemFire 在使用 HTTP 时记住客户端推送的配置的方式执行的。如果集群中的所有节点都宕机了,它们会以与之前相同的配置重新启动。如果集群中添加了新服务器,它将获取相同的配置。

从某种意义上说,此功能与您使用 Gfsh 手动在集群中的所有服务器上创建 Region 和索引并没有太大区别。只是现在,使用 Spring Data for Pivotal GemFire,您不再需要使用 Gfsh 来创建 Region 和索引。您的 Spring Boot 应用程序,借助 Spring Data for Pivotal GemFire 的强大功能,已经包含创建 Region 和索引所需的所有配置元数据。

当您使用 Spring Data Repository 抽象时,我们知道您的应用程序需要的所有 Region(例如由 @Region 注解的实体类定义的 Region)和索引(例如由 @Indexed 注解的实体字段和属性定义的索引)。

当您使用 Spring 的缓存抽象时,我们还知道您的应用程序服务组件所需的所有缓存的所有 Region。

本质上,您只需通过使用 Spring Framework 的所有 API 和功能来开发您的应用程序,无论是在注解元数据、Java、XML 还是其他方式中表达,也无论是用于配置、映射还是其他目的,您就已经告诉了我们所有需要知道的信息。

关键是,您可以专注于应用程序的业务逻辑,同时使用框架的功能和支持基础设施(例如 Spring 的缓存抽象、Spring Data Repositories、Spring 的事务管理等),而 Spring Data for Pivotal GemFire 会为您处理这些框架功能所需的所有 Pivotal GemFire 管道。

将配置从客户端推送到集群中的服务器并让集群记住它,部分得益于 Pivotal GemFire 集群配置服务的使用。Pivotal GemFire 的集群配置服务也是 Gfsh 用于记录用户从 shell 发送到集群的与模式相关的更改(例如,gfsh> create region --name=Example --type=PARTITION)的相同服务。

当然,由于集群可能会“记住”客户端从之前运行中推送的先前配置,因此 Spring Data for Pivotal GemFire 会小心,不要覆盖服务器中已定义的任何现有 Region 和索引。例如,当 Region 已经包含数据时,这尤其重要!

目前,没有选项可以覆盖任何现有的 Region 或 Index 定义。要重新创建 Region 或 Index,您必须使用 Gfsh 先销毁 Region 或 Index,然后重新启动客户端应用程序,以便将配置再次推送到服务器。或者,您可以使用 Gfsh 手动(重新)定义 Region 和 Index。
Gfsh 不同,Spring Data for Pivotal GemFire 仅支持从客户端在服务器上创建 Region 和索引。对于高级配置和用例,您应该使用 Gfsh 来管理(服务器端)集群。
要使用此功能,您必须在 Spring、Pivotal GemFire ClientCache 应用程序的类路径中显式声明 org.springframework:spring-web 依赖项。

考虑以下配置中表达的强大功能

Spring ClientCache 应用程序
@SpringBootApplication
@ClientCacheApplication
@EnableCachingDefinedRegions
@EnableEntityDefinedRegions
@EnableIndexing
@EnableGemfireCaching
@EnableGemfireRepositories
@EnableClusterConfiguration
class ClientApplication { .. }

您可以立即获得一个 Spring Boot 应用程序,其中包含 Pivotal GemFire ClientCache 实例、Spring Data Repositories、使用 Pivotal GemFire 作为缓存提供程序的 Spring 缓存抽象(其中 Region 和索引不仅在客户端创建,而且推送到集群中的服务器)。

从那里,您只需要执行以下操作

  • 定义应用程序的域模型对象,并使用映射和索引注解进行注解。

  • 为每个实体类型定义存储库接口以支持基本数据访问操作和简单查询。

  • 定义包含事务处理实体的业务逻辑的服务组件。

  • 在需要缓存、事务行为等的服务方法上声明适当的注解。

在这种情况下,没有任何内容与应用程序后端服务(例如 Pivotal GemFire)所需的基础设施和管道有关。数据库用户具有类似的功能。现在 Spring 和 Pivotal GemFire 开发人员也具有了。

当与以下 Spring Data for Pivotal GemFire 注解结合使用时,此应用程序将以很少的努力真正开始腾飞

  • @EnableContinuousQueries

  • @EnableGemfireFunctionExecutions

  • @EnableGemfireCacheTransactions

有关更多详细信息,请参见 @EnableClusterConfiguration 注解 Javadoc

6.16. 配置 SSL

与通过网络传输数据同样重要的是保护传输中的数据。当然,在 Java 中实现此目的的常用方法是使用安全套接字扩展 (SSE) 和传输层安全 (TLS)。

要启用 SSL,请使用 @EnableSsl 注解您的应用程序类,如下所示

启用 SSL 的 Spring ClientCache 应用程序
@SpringBootApplication
@ClientCacheApplication
@EnableSsl
public class ClientApplication { .. }

然后,您需要设置必要的 SSL 配置属性或属性:密钥库、用户名/密码等。

您可以单独为不同的 Pivotal GemFire 组件(GATEWAYHTTPJMXLOCATORSERVER)配置 SSL,也可以通过使用 CLUSTER 枚举值来集体配置它们以使用 SSL。

您可以通过使用嵌套的 @EnableSsl 注解的 components 属性和 Component 枚举中的枚举值来指定 SSL 配置设置应应用于哪些 Pivotal GemFire 组件,如下所示

按组件启用 SSL 的 Spring ClientCache 应用程序
@SpringBootApplication
@ClientCacheApplication
@EnableSsl(components = { GATEWAY, LOCATOR, SERVER })
public class ClientApplication { .. }

此外,您还可以通过使用相应的注解属性或相关配置属性来指定组件级别的 SSL 配置(ciphersprotocolskeystore/truststore 信息)。

有关更多详细信息,请参见 @EnableSsl 注解 Javadoc

有关 Pivotal GemFire SSL 支持的更多详细信息,请参见此处

6.17. 配置安全性

毫无疑问,应用程序安全性极其重要,Spring Data for Pivotal GemFire 为保护 Pivotal GemFire 客户端和服务器提供了全面的支持。

最近,Pivotal GemFire 引入了一个新的 集成安全 框架(取代了其旧的身份验证和授权安全模型),用于处理身份验证和授权。这个新安全框架的主要功能和优点之一是它与 Apache Shiro 集成,因此可以委托身份验证和授权请求给 Apache Shiro 来强制执行安全性。

本节的其余部分演示了 Spring Data for Pivotal GemFire 如何进一步简化 Pivotal GemFire 的安全故事。

6.17.1. 配置服务器安全性

您可以通过几种不同的方式配置 Pivotal GemFire 集群中服务器的安全性。

  • 实现 Pivotal GemFire org.apache.geode.security.SecurityManager 接口,并将 Pivotal GemFire 的 security-manager 属性设置为使用完全限定类名引用您的应用程序 SecurityManager 实现。或者,用户可以构造并初始化其 SecurityManager 实现的实例,并在创建 Pivotal GemFire 对等 Cache 时使用 CacheFactory.setSecurityManager(:SecurityManager) 方法进行设置。

  • 创建一个 Apache Shiro shiro.ini 文件,其中包含为您的应用程序定义的用户、角色和权限,然后将 Pivotal GemFire security-shiro-init 属性设置为引用此 shiro.ini 文件,该文件必须在 CLASSPATH 中可用。

  • 仅使用 Apache Shiro,使用 Spring Data for Pivotal GemFire 的新 @EnableSecurity 注解您的 Spring Boot 应用程序类,并在 Spring 容器中将一个或多个 Apache Shiro Realm 定义为 bean,以访问您的应用程序的安全元数据(即授权用户、角色和权限)。

第一种方法的问题是您必须实现自己的 SecurityManager,这可能非常繁琐且容易出错。实现自定义 SecurityManager 提供了一些灵活性,可以从存储元数据的任何数据源(例如 LDAP 甚至专有内部数据源)访问安全元数据。但是,这个问题已经通过配置和使用 Apache Shiro Realms 解决了,后者更普遍为人所知且不特定于 Pivotal GemFire。

请参阅 Pivotal GemFire 的身份验证授权安全示例,作为实现您自己的自定义应用程序特定 SecurityManager 的一种可能方法。但是,我们强烈**不**建议这样做。

第二种方法,使用 Apache Shiro INI 文件,略好一些,但您仍然需要首先熟悉 INI 文件格式。此外,INI 文件是静态的,并且在运行时不易更新。

第三种方法是最理想的,因为它遵循广为人知和行业接受的概念(即 Apache Shiro 的安全框架),并且易于设置,如下例所示

使用 Apache Shiro 的 Spring 服务器应用程序
@SpringBootApplication
@CacheServerApplication
@EnableSecurity
class ServerApplication {

  @Bean
  PropertiesRealm shiroRealm() {

      PropertiesRealm propertiesRealm = new PropertiesRealm();

      propertiesRealm.setResourcePath("classpath:shiro.properties");
      propertiesRealm.setPermissionResolver(new GemFirePermissionResolver());

      return propertiesRealm;
  }
}
上面示例中配置的 Realm 很容易是 Apache Shiro 支持的任何 Realms

您甚至可以创建 Apache Shiro Realm 的自定义实现。

有关更多详细信息,请参见 Apache Shiro 的Realm 文档

当 Apache Shiro 在集群服务器的 CLASSPATH 上,并且一个或多个 Apache Shiro Realms 已在 Spring 容器中定义为 bean 时,Spring Data for Pivotal GemFire 会检测此配置并在使用 @EnableSecurity 注解时使用 Apache Shiro 作为安全提供程序来保护您的 Pivotal GemFire 服务器。

有关 Spring Data for Pivotal GemFire 对使用 Apache Shiro 的 Pivotal GemFire 新集成安全框架的支持的更多信息,请参阅此spring.io 博客文章

有关可用属性和相关配置属性的更多详细信息,请参见 @EnableSecurity 注解 Javadoc。

有关 Pivotal GemFire 安全性的更多详细信息,请参见此处

6.17.2. 配置客户端安全性

如果不讨论如何保护基于 Spring 的 Pivotal GemFire 缓存客户端应用程序,安全故事将不完整。

Pivotal GemFire 保护客户端应用程序的过程坦率地说相当复杂。简而言之,您需要

  1. 提供 org.apache.geode.security.AuthInitialize 接口的实现。

  2. 将 Pivotal GemFire security-client-auth-init(系统)属性设置为引用自定义的、应用程序提供的 AuthInitialize 接口。

  3. 在专有的 Pivotal GemFire gfsecurity.properties 文件中指定用户凭据。

Spring Data for Pivotal GemFire 通过使用与服务器应用程序中相同的 @EnableSecurity 注解来简化所有这些步骤。换句话说,相同的 @EnableSecurity 注解处理客户端和服务器应用程序的安全性。此功能使用户更容易在决定将其应用程序从嵌入式对等 Cache 应用程序切换到 ClientCache 应用程序时,例如。只需将 SDG 注解从 @PeerCacheApplication@CacheServerApplication 更改为 @ClientCacheApplication,您就完成了。

实际上,您需要在客户端执行的操作如下

使用 @EnableSecurity 的 Spring 客户端应用程序
@SpringBootApplication
@ClientCacheApplication
@EnableSecurity
class ClientApplication { .. }

然后您可以定义包含所需用户名和密码的熟悉的 Spring Boot application.properties 文件,如下例所示,您就全部设置好了

带有所需安全凭据的 Spring Boot application.properties 文件
spring.data.gemfire.security.username=jackBlack
spring.data.gemfire.security.password=b@cK!nB1@cK
默认情况下,当 application.properties 文件放置在应用程序 CLASSPATH 的根目录中时,Spring Boot 可以找到它。当然,Spring 支持使用其 Resource 抽象来定位资源的多种方式。

有关可用属性和相关配置属性的更多详细信息,请参见 @EnableSecurity 注解 Javadoc。

有关 Pivotal GemFire 安全性的更多详细信息,请参见此处

6.18. 配置技巧

以下技巧可以帮助您充分利用新的基于注解的配置模型

6.18.1. 配置组织

正如我们在 “配置集群配置推送” 一节中看到的,当通过注解启用许多 Pivotal GemFire 或 Spring Data for Pivotal GemFire 功能时,我们开始在 Spring @Configuration@SpringBootApplication 类上堆叠大量注解。在这种情况下,开始将配置进行一些模块化是合理的。

例如,考虑以下声明

具有所有功能的 Spring ClientCache 应用程序
@SpringBootApplication
@ClientCacheApplication
@EnableContinuousQueries
@EnableCachingDefinedRegions
@EnableEntityDefinedRegions
@EnableIndexing
@EnableGemfireCacheTransactions
@EnableGemfireCaching
@EnableGemfireFunctionExecutions
@EnableGemfireRepositories
@EnableClusterConfiguration
class ClientApplication { .. }

我们可以按关注点将此配置分解,如下所示

具有所有功能的 Spring ClientCache 应用程序
@SpringBootApplication
@Import({ GemFireConfiguration.class, CachingConfiguration.class,
    FunctionsConfiguration.class, QueriesConfiguration.class,
    RepositoriesConfiguration.class })
class ClientApplication { .. }

@ClientCacheApplication
@EnableClusterConfiguration
@EnableGemfireCacheTransactions
class GemFireConfiguration { .. }

@EnableGemfireCaching
@EnableCachingDefinedRegions
class CachingConfiguration { .. }

@EnableGemfireFunctionExecutions
class FunctionsConfiguration { .. }

@EnableContinuousQueries
class QueriesConfiguration {

   @ContinuousQuery(..)
   void processCqEvent(CqEvent event) {
       ...
   }
}

@EnableEntityDefinedRegions
@EnableGemfireRepositories
@EnableIndexing
class RepositoriesConfiguration { .. }

虽然这对于 Spring Framework 来说无关紧要,但我们通常建议以可读性为目标,以方便下一个必须维护代码的人(这可能在未来的某个时候是您)。

6.18.2. 其他基于配置的注解

以下 SDG 注解未在本参考文档中讨论,原因要么是该注解支持 Pivotal GemFire 的已弃用功能,要么是有更好的替代方法来完成该注解提供的功能

  • @EnableAuth:启用 Pivotal GemFire 的旧身份验证和授权安全模型。(已弃用。Pivotal GemFire 的新集成安全框架可以通过使用 SDG 的 @EnableSecurity 注解在客户端和服务器上启用,如“配置安全性”中所述。)

  • @EnableAutoRegionLookup:不推荐。本质上,此注解支持查找外部配置元数据(例如 cache.xml 或应用于服务器时的集群配置)中定义的 Region,并自动将这些 Region 注册为 Spring 容器中的 bean。此注解对应于 SDG XML 命名空间中的 <gfe:auto-region-lookup> 元素。更多详细信息可以在此处找到。用户在使用 Spring 和 Spring Data for Pivotal GemFire 时通常应优先使用 Spring 配置。请改参见“配置 Region”和“配置集群配置推送”。

  • @EnableBeanFactoryLocator:启用 SDG GemfireBeanFactoryLocator 功能,仅在使用外部配置元数据(例如 cache.xml)时有用。例如,如果您在 cache.xml 中定义的 Region 上定义了 CacheLoader,您仍然可以使用 Spring 配置中定义的(例如)关系数据库 DataSource bean 自动装配此 CacheLoader。此注解利用了此 SDG 功能,如果您有大量遗留配置元数据,例如 cache.xml 文件,则可能有用。

  • @EnableGemFireAsLastResource:在 全局 - JTA 事务管理与 Pivotal GemFire 中讨论。

  • @EnableMcast:启用 Pivotal GemFire 的旧对等发现机制,该机制使用基于 UDP 的多播网络。(已弃用。请改用 Pivotal GemFire 定位器。请参见“配置嵌入式定位器”。

  • @EnableRegionDataAccessTracing:用于调试目的。此注解通过注册 AOP 切面来启用对 Region 上执行的所有数据访问操作的跟踪,该切面代理 Spring 容器中声明为 bean 的所有 Region,拦截 Region 操作并记录事件。

6.19. 结论

正如我们在前面的章节中所了解到的,Spring Data for Pivotal GemFire 的新基于注解的配置模型提供了巨大的功能。希望它能实现其目标,让您在使用 Pivotal GemFire 和 Spring 时更容易**快速轻松地入门**。

请记住,当您使用新注解时,您仍然可以使用 Java 配置或 XML 配置。您甚至可以通过在 Spring @Configuration@SpringBootApplication 类上使用 Spring 的 @Import@ImportResource 注解来组合所有三种方法。当您明确提供一个否则将由 Spring Data for Pivotal GemFire 使用其中一个注解提供的 bean 定义时,基于注解的配置就会退让。

在某些情况下,您甚至可能需要回退到 Java 配置,就像在 Configurers 情况中一样,以处理更复杂或条件配置逻辑,这些逻辑不容易用注解表达或无法单独通过注解完成。请不要惊慌。这种行为是意料之中的。

例如,需要 Java 或 XML 配置的另一个情况是配置 Pivotal GemFire WAN 组件,目前这些组件没有任何注解配置支持。但是,定义和注册 WAN 组件只需要在 Spring @Configuration@SpringBootApplication 类的 Java 配置中使用 org.springframework.data.gemfire.wan.GatewayReceiverFactoryBeanorg.springframework.data.gemfire.wan.GatewaySenderFactoryBean API 类(推荐)。

注解并非旨在处理所有情况。注解旨在帮助您**尽可能快速轻松地启动和运行**,尤其是在开发期间。

我们希望您会喜欢这些新功能!

6.20. 基于注解的配置快速入门

以下部分概述了 SDG 注解,以便快速入门。

所有注解都提供了额外的配置属性以及相关的属性,以便在运行时方便地自定义 Pivotal GemFire 的配置和行为。但是,通常,使用特定的 Pivotal GemFire 功能不需要任何属性或相关属性。只需声明注解即可启用该功能,然后就完成了。有关更多详细信息,请参阅每个注解的单独 Javadoc。

6.20.1. 配置 ClientCache 应用程序

要配置和引导 Pivotal GemFire ClientCache 应用程序,请使用以下内容

@SpringBootApplication
@ClientCacheApplication
public class ClientApplication {

  public static void main(String[] args) {
    SpringApplication.run(ClientApplication.class, args);
  }
}

有关更多详细信息,请参阅 使用 Spring 配置 Pivotal GemFire 应用程序

6.20.2. 配置对等 Cache 应用程序

要配置和引导 Pivotal GemFire 对等 Cache 应用程序,请使用以下内容

@SpringBootApplication
@PeerCacheApplication
public class ServerApplication {

  public static void main(String[] args) {
    SpringApplication.run(ServerApplication.class, args);
  }
}
如果您想启用一个允许 ClientCache 应用程序连接到此服务器的 CacheServer,那么只需将 @PeerCacheApplication 注解替换为 @CacheServerApplication 注解。这将启动一个在“localhost”上运行的 CacheServer,侦听默认的 CacheServer 端口 40404

有关更多详细信息,请参阅 使用 Spring 配置 Pivotal GemFire 应用程序

6.20.3. 配置嵌入式定位器

使用 @EnableLocator 注解您的 Spring @PeerCacheApplication@CacheServerApplication 类,以启动一个绑定到所有 NIC 的嵌入式定位器,侦听默认的定位器端口 10334,如下所示

@SpringBootApplication
@CacheServerApplication
@EnableLocator
public class ServerApplication {

  public static void main(String[] args) {
    SpringApplication.run(ServerApplication.class, args);
  }
}
@EnableLocator 只能与 Pivotal GemFire 服务器应用程序一起使用。

请参阅 @EnableLocator Javadoc

有关更多详细信息,请参阅 配置嵌入式定位器

6.20.4. 配置嵌入式管理器

使用 @EnableManager 注解您的 Spring @PeerCacheApplication@CacheServerApplication 类,以启动一个绑定到所有 NIC 的嵌入式管理器,侦听默认的管理器端口 1099,如下所示

@SpringBootApplication
@CacheServerApplication
@EnableManager
public class ServerApplication {

  public static void main(String[] args) {
    SpringApplication.run(ServerApplication.class, args);
  }
}
@EnableManager 只能与 Pivotal GemFire 服务器应用程序一起使用。

请参阅 @EnableManager Javadoc

有关更多详细信息,请参阅 配置嵌入式管理器

6.20.5. 配置嵌入式 HTTP 服务器

使用 @EnableHttpService 注解您的 Spring @PeerCacheApplication@CacheServerApplication 类,以启动嵌入式 HTTP 服务器 (Jetty),侦听端口 7070,如下所示

@SpringBootApplication
@CacheServerApplication
@EnableHttpService
public class ServerApplication {

  public static void main(String[] args) {
    SpringApplication.run(ServerApplication.class, args);
  }
}
@EnableHttpService 只能与 Pivotal GemFire 服务器应用程序一起使用。

有关更多详细信息,请参阅 配置嵌入式 HTTP 服务器

6.20.6. 配置嵌入式 Memcached 服务器

使用 @EnableMemcachedServer 注解您的 Spring @PeerCacheApplication@CacheServerApplication 类,以启动嵌入式 Memcached 服务器 (Gemcached),侦听端口 11211,如下所示

@SpringBootApplication
@CacheServerApplication
@EnableMemcachedServer
public class ServerApplication {

  public static void main(String[] args) {
    SpringApplication.run(ServerApplication.class, args);
  }
}
@EnableMemcachedServer 只能与 Pivotal GemFire 服务器应用程序一起使用。

有关更多详细信息,请参阅 配置嵌入式 Memcached 服务器 (Gemcached)

6.20.7. 配置嵌入式 Redis 服务器

使用 @EnableRedisServer 注解您的 Spring @PeerCacheApplication@CacheServerApplication 类,以启动嵌入式 Redis 服务器,侦听端口 6379,如下所示

@SpringBootApplication
@CacheServerApplication
@EnableRedisServer
public class ServerApplication {

  public static void main(String[] args) {
    SpringApplication.run(ServerApplication.class, args);
  }
}
@EnableRedisServer 只能与 Pivotal GemFire 服务器应用程序一起使用。
您必须在 Spring [Boot] 应用程序的类路径中明确声明 org.apache.geode:geode-redis 模块。

有关更多详细信息,请参阅 配置嵌入式 Redis 服务器

6.20.8. 配置日志记录

要配置或调整 Pivotal GemFire 日志记录,请使用 @EnableLogging 注解您的 Spring、Pivotal GemFire 客户端或服务器应用程序类,如下所示

@SpringBootApplication
@ClientCacheApplication
@EnableLogging(logLevel="trace")
public class ClientApplication {

  public static void main(String[] args) {
    SpringApplication.run(ClientApplication.class, args);
  }
}
默认的 log-level 是“config”。此外,此注解不会调整应用程序中的日志级别,只会调整 Pivotal GemFire 的日志级别。

请参阅 @EnableLogging Javadoc

有关更多详细信息,请参阅 配置日志记录

6.20.9. 配置统计信息

要在运行时收集 Pivotal GemFire 统计信息,请使用 @EnableStatistics 注解您的 Spring、Pivotal GemFire 客户端或服务器应用程序类,如下所示

@SpringBootApplication
@ClientCacheApplication
@EnableStatistics
public class ClientApplication {

  public static void main(String[] args) {
    SpringApplication.run(ClientApplication.class, args);
  }
}

有关更多详细信息,请参阅 配置统计信息

6.20.10. 配置 PDX

要启用 Pivotal GemFire PDX 序列化,请使用 @EnablePdx 注解您的 Spring、Pivotal GemFire 客户端或服务器应用程序类,如下所示

@SpringBootApplication
@ClientCacheApplication
@EnablePdx
public class ClientApplication {

  public static void main(String[] args) {
    SpringApplication.run(ClientApplication.class, args);
  }
}
Pivotal GemFire PDX 序列化是 Java 序列化的替代方案,具有许多附加优点。其中之一是,它使得您的所有应用程序域模型类型可序列化变得轻而易举,而无需实现 java.io.Serializable
默认情况下,SDG 配置 MappingPdxSerializer 来序列化您的应用程序域模型类型,这不需要任何特殊的开箱即用配置即可正确识别需要序列化的应用程序域对象,然后执行序列化,因为 MappingPdxSerializer 中的逻辑基于 Spring Data 的映射基础设施。有关更多详细信息,请参阅 MappingPdxSerializer

请参阅 @EnablePdx Javadoc

有关更多详细信息,请参阅 配置 PDX

6.20.11. 配置 SSL

要启用 Pivotal GemFire SSL,请使用 @EnableSsl 注解您的 Spring、Pivotal GemFire 客户端或服务器应用程序类,如下所示

@SpringBootApplication
@ClientCacheApplication
@EnableSsl(components = SERVER)
public class ClientApplication {

  public static void main(String[] args) {
    SpringApplication.run(ClientApplication.class, args);
  }
}
Pivotal GemFire 最少要求您使用适当的配置属性或属性指定密钥库和信任库。密钥库和信任库配置属性或属性都可以引用相同的 KeyStore 文件。此外,如果文件已加密,您将需要指定用户名和密码来访问 KeyStore 文件。
Pivotal GemFire SSL 允许您配置需要 TLS 的系统特定组件,例如客户端/服务器、定位器、网关等。您可以选择指定 Pivotal GemFire 的所有组件都使用 SSL,方法是使用“ALL”。

请参阅 @EnableSsl Javadoc

有关更多详细信息,请参阅 配置 SSL

6.20.12. 配置安全性

要启用 Pivotal GemFire 安全性,请使用 @EnableSecurity 注解您的 Spring、Pivotal GemFire 客户端或服务器应用程序类,如下所示

@SpringBootApplication
@ClientCacheApplication
@EnableSecurity
public class ClientApplication {

  public static void main(String[] args) {
    SpringApplication.run(ClientApplication.class, args);
  }
}
在服务器上,您必须配置对身份验证凭据的访问。您可以实现 Pivotal GemFire SecurityManager 接口,或声明一个或多个 Apache Shiro Realms。有关更多详细信息,请参阅 配置服务器安全性
在客户端上,您必须配置用户名和密码。有关更多详细信息,请参阅 配置客户端安全性

有关更多详细信息,请参阅 配置安全性

6.20.13. 配置 Pivotal GemFire 属性

要配置功能导向的 SDG 配置注解未涵盖的其他低级 Pivotal GemFire 属性,请使用 @GemFireProperties 注解您的 Spring、Pivotal GemFire 客户端或服务器应用程序类,如下所示

@SpringBootApplication
@PeerCacheApplication
@EnableGemFireProperties(
    cacheXmlFile = "/path/to/cache.xml",
    conserveSockets = true,
    groups = "GroupOne",
    remoteLocators = "lunchbox[11235],mailbox[10101],skullbox[12480]"
)
public class ServerApplication {

  public static void main(String[] args) {
    SpringApplication.run(ServerApplication.class, args);
  }
}
某些 Pivotal GemFire 属性仅限于客户端,而其他属性仅限于服务器端。请查阅 Pivotal GemFire 文档以了解每个属性的正确用法。

有关更多详细信息,请参阅 配置 Pivotal GemFire 属性

6.20.14. 配置缓存

要将 Pivotal GemFire 用作 Spring 缓存抽象中的**缓存提供程序**,并让 SDG 自动为应用程序服务组件所需的缓存创建 Pivotal GemFire Region,请使用 @EnableGemfireCaching@EnableCachingDefinedRegions 注解您的 Spring、Pivotal GemFire 客户端或服务器应用程序类,如下所示

@SpringBootApplication
@ClientCacheApplication
@EnableCachingDefinedRegions
@EnableGemfireCaching
public class ClientApplication {

  public static void main(String[] args) {
    SpringApplication.run(ClientApplication.class, args);
  }
}

然后,只需继续定义需要缓存的应用程序服务,如下所示

@Service
public class BookService {

    @Cacheable("Books")
    public Book findBy(ISBN isbn) {
        ...
    }
}
@EnableCachingDefinedRegions 是可选的。也就是说,如果您愿意,可以手动定义您的 Region。

有关更多详细信息,请参阅 配置 Spring 的缓存抽象

6.20.15. 为持久化应用程序配置 Region、索引、存储库和实体

为了轻松创建 Spring、Pivotal GemFire 持久化客户端或服务器应用程序,请使用 @EnableEntityDefinedRegions@EnableGemfireRepositories@EnableIndexing 注解您的应用程序类,如下所示

@SpringBootApplication
@ClientCacheApplication
@EnableEntityDefinedRegions(basePackageClasses = Book.class)
@EnableGemfireRepositories(basePackageClasses = BookRepository.class)
@EnableIndexing
public class ClientApplication {

  public static void main(String[] args) {
    SpringApplication.run(ClientApplication.class, args);
  }
}
使用 @EnableIndexing 注解时,@EnableEntityDefinedRegions 注解是必需的。有关更多详细信息,请参阅 配置索引

接下来,定义您的实体类并使用 @Region 映射注解来指定存储您的实体的 Region。使用 @Indexed 注解来定义应用程序查询中使用的实体字段上的索引,如下所示

package example.app.model;

import ...;

@Region("Books")
public class Book {

  @Id
  private ISBN isbn;

  @Indexed;
  private Author author;

  @Indexed
  private LocalDate published;

  @LuceneIndexed
  private String title;

}
@Region("Books") 实体类注解由 @EnableEntityDefinedRegions 用于确定应用程序所需的 Region。有关更多详细信息,请参阅 配置特定类型的 RegionPOJO 映射

最后,定义您的 CRUD 存储库,其中包含简单的查询以持久化和访问 Books,如下所示

package example.app.repo;

import ...;

public interface BookRepository extends CrudRepository {

  List<Book> findByAuthorOrderByPublishedDesc(Author author);

}
有关更多详细信息,请参阅 Spring Data for Pivotal GemFire 存储库

请参阅 @Region Javadoc

请参阅 @Indexed Javadoc

请参阅 @LuceneIndexed Javadoc

有关更多详细信息,请参阅 配置 Region

有关更多详细信息,请参阅 Spring Data for Pivotal GemFire 存储库

6.20.16. 从集群定义的 Region 配置客户端 Region

或者,您可以使用 @EnableClusterDefinedRegions 从集群中已定义的 Region 定义客户端 [*PROXY] Region,如下所示

@SpringBootApplication
@ClientCacheApplication
@EnableClusterDefinedRegions
@EnableGemfireRepositories
public class ClientApplication {

  public static void main(String[] args) {
    SpringApplication.run(ClientApplication.class, args);
  }

  ...
}

有关更多详细信息,请参阅 配置集群定义的 Region

6.20.17. 配置函数

Pivotal GemFire 函数在分布式计算场景中很有用,在这种场景中,需要数据的可能昂贵的计算可以在集群中的节点之间并行执行。在这种情况下,将逻辑带到数据所在(存储)的位置比请求和获取数据由计算处理更有效。

结合 @EnableGemfireFunctions@GemfireFunction 注解,启用以 POJO 方法实现 Pivotal GemFire 函数定义,如下所示

@PeerCacheApplication
@EnableGemfireFunctions
class ServerApplication {

  public static void main(String[] args) {
    SpringApplication.run(ServerApplication.class, args);
  }

  @GemfireFunction
  Integer computeLoyaltyPoints(Customer customer) {
    ...
  }
}

结合 @EnableGemfireFunctionExecutions 和以下函数调用注解之一:@OnMember@OnMembers@OnRegion@OnServer@OnServers

@ClientCacheApplication
@EnableGemfireFunctionExecutions(basePackageClasses = CustomerRewardsFunction.class)
class ClientApplication {

  public static void main(String[] args) {
    SpringApplication.run(ClientApplication.class, args);
  }
}

@OnRegion("Customers")
interface CustomerRewardsFunctions {

  Integer computeLoyaltyPoints(Customer customer);

}

有关更多详细信息,请参阅 函数执行的注解支持

6.20.18. 配置连续查询

实时事件流处理对于数据密集型应用程序来说变得越来越重要,主要是为了及时响应用户请求。Pivotal GemFire 连续查询 (CQ) 将帮助您轻松实现这项相当复杂的任务。

通过使用 @EnableContinuousQueries 注解您的应用程序类来启用 CQ,并定义您的 CQ 以及相关的事件处理器,如下所示

@ClientCacheApplication
@EnableContinuousQueries
class ClientApplication {

  public static void main(String[] args) {
    SpringApplication.run(ClientApplication.class, args);
  }
}

然后,通过使用 @ContinousQuery 注解相关的处理器方法来定义您的 CQ,如下所示

@Service
class CustomerService {

  @ContinuousQuery(name = "CustomerQuery", query = "SELECT * FROM /Customers c WHERE ...")
  public void process(CqEvent event) {
    ...
  }
}

每当发生事件改变 Customer 数据以匹配您的连续 OQL 查询 (CQ) 中的谓词时,process 方法将被调用。

Pivotal GemFire CQ 仅是客户端功能。

有关更多详细信息,请参阅 连续查询 (CQ)配置连续查询

6.20.19. 配置集群配置

在使用 Pivotal GemFire 作为 Pivotal GemFire ClientCache 应用程序开发 Spring Data 应用程序时,在开发期间配置服务器以匹配客户端/服务器拓扑中的客户端非常有用。事实上,Pivotal GemFire 期望当您在客户端上有一个 "/Example" PROXY Region 时,集群中存在一个名称匹配的 Region(即 "Example")。

您可以使用 Gfsh 创建应用程序所需的所有 Region 和索引,或者,您可以在运行 Spring Data 应用程序时,将开发 Spring Data 应用程序时已表达的配置元数据推送出去。

这就像使用 @EnableClusterConfiguration(..) 注解您的主应用程序类一样简单。

使用 @EnableClusterConfiguration
@ClientCacheApplication
@EnableClusterConfiguration(useHttp = true)
class ClientApplication {
  ...
}
大多数情况下,在使用客户端/服务器拓扑时,尤其是在生产环境中,集群的服务器将使用 Gfsh 启动。在这种情况下,通常使用 HTTP(S) 将配置元数据(例如 Region 和 Index 定义)发送到集群。当使用 HTTP 时,配置元数据会发送到集群中的管理器,并一致地分布到集群中的服务器节点。
要使用 @EnableClusterConfiguration,您必须在 Spring 应用程序类路径中声明 org.springframework:spring-web 依赖项。

有关更多详细信息,请参阅 配置集群配置推送

6.20.20. 配置 GatewayReceivers

不同 Pivotal GemFire 集群之间的数据复制是一种日益重要的容错和高可用性 (HA) 机制。Pivotal GemFire WAN 复制是一种机制,允许一个 Pivotal GemFire 集群以可靠和容错的方式将其数据复制到另一个 Pivotal GemFire 集群。

Pivotal GemFire WAN 复制需要配置两个组件

  • GatewayReceiver - 接收来自远程 Pivotal GemFire 集群的 GatewaySender 数据的 WAN 复制组件。

  • GatewaySender - 将数据发送到远程 Pivotal GemFire 集群的 GatewayReceiver 的 WAN 复制组件。

要启用 GatewayReceiver,应用程序类需要使用 @EnableGatewayReceiver 注解,如下所示

@CacheServerApplication
@EnableGatewayReceiver(manualStart = false, startPort = 10000, endPort = 11000, maximumTimeBetweenPings = 1000,
    socketBufferSize = 16384, bindAddress = "localhost",transportFilters = {"transportBean1", "transportBean2"},
    hostnameForSenders = "hostnameLocalhost"){
      ...
      ...
    }
}
class MySpringApplication { .. }
Pivotal GemFire GatewayReceiver 仅是服务器端功能,只能在 CacheServer 或对等 Cache 节点上配置。

6.20.21. 配置 GatewaySenders

要启用 GatewaySender,应用程序类需要使用 @EnableGatewaySenders@EnableGatewaySender 注解,如下所示

@CacheServerApplication
@EnableGatewaySenders(gatewaySenders = {
		@EnableGatewaySender(name = "GatewaySender", manualStart = true,
			remoteDistributedSystemId = 2, diskSynchronous = true, batchConflationEnabled = true,
			parallel = true, persistent = false,diskStoreReference = "someDiskStore",
			orderPolicy = OrderPolicyType.PARTITION, alertThreshold = 1234, batchSize = 100,
			eventFilters = "SomeEventFilter", batchTimeInterval = 2000, dispatcherThreads = 22,
			maximumQueueMemory = 400,socketBufferSize = 16384,
			socketReadTimeout = 4000, regions = { "Region1"}),
		@EnableGatewaySender(name = "GatewaySender2", manualStart = true,
			remoteDistributedSystemId = 2, diskSynchronous = true, batchConflationEnabled = true,
			parallel = true, persistent = false, diskStoreReference = "someDiskStore",
			orderPolicy = OrderPolicyType.PARTITION, alertThreshold = 1234, batchSize = 100,
			eventFilters = "SomeEventFilter", batchTimeInterval = 2000, dispatcherThreads = 22,
			maximumQueueMemory = 400, socketBufferSize = 16384,socketReadTimeout = 4000,
			regions = { "Region2" })
}){
class MySpringApplication { .. }
}
Pivotal GemFire GatewaySender 仅是服务器端功能,只能在 CacheServer 或对等 Cache 节点上配置。

在上面的示例中,应用程序配置了两个 Region,Region1Region2。此外,将配置两个 GatewaySender 来服务这两个 Region。GatewaySender1 将配置为复制 Region1 的数据,GatewaySender2 将配置为复制 Region2 的数据。

如所示,每个 GatewaySender 属性都可以在每个 EnableGatewaySender 注解上配置。

也可以采用更通用的“默认”属性方法,其中所有属性都在 EnableGatewaySenders 注解上配置。这样,一组通用的默认值可以在父注解上设置,然后如果需要,可以在子注解上覆盖,如下所示

@CacheServerApplication
@EnableGatewaySenders(gatewaySenders = {
		@EnableGatewaySender(name = "GatewaySender", transportFilters = "transportBean1", regions = "Region2"),
		@EnableGatewaySender(name = "GatewaySender2")},
		manualStart = true, remoteDistributedSystemId = 2,
		diskSynchronous = false, batchConflationEnabled = true, parallel = true, persistent = true,
		diskStoreReference = "someDiskStore", orderPolicy = OrderPolicyType.PARTITION, alertThreshold = 1234, batchSize = 1002,
		eventFilters = "SomeEventFilter", batchTimeInterval = 2000, dispatcherThreads = 22, maximumQueueMemory = 400,
		socketBufferSize = 16384, socketReadTimeout = 4000, regions = { "Region1", "Region2" },
		transportFilters = { "transportBean2", "transportBean1" })
class MySpringApplication { .. }
regions 属性留空或未填充时,GatewaySender 将自动附加到应用程序中的每个已配置 Region

7. 使用 Pivotal GemFire API

一旦配置了 Pivotal GemFire 缓存和 Region,它们就可以注入并在应用程序对象中使用。本章描述了与 Spring 的事务管理功能和 DAO 异常层次结构的集成。本章还涵盖了对 Pivotal GemFire 管理对象的依赖注入支持。

7.1. GemfireTemplate

与 Spring 提供的许多其他高级抽象一样,Spring Data for Pivotal GemFire 提供了一个**模板**来简化 Pivotal GemFire 数据访问操作。该类提供了几个包含常见 Region 操作的方法,但还提供了通过使用 GemfireCallback 在不处理 Pivotal GemFire 检查异常的情况下对本机 Pivotal GemFire API 执行代码的功能。

模板类需要一个 Pivotal GemFire Region,一旦配置,它是线程安全的,并且可以在多个应用程序类中重用

<bean id="gemfireTemplate" class="org.springframework.data.gemfire.GemfireTemplate" p:region-ref="SomeRegion"/>

一旦配置了模板,开发人员就可以将其与 GemfireCallback 一起使用,直接与 Pivotal GemFire Region 交互,而无需处理检查异常、线程或资源管理问题。

template.execute(new GemfireCallback<Iterable<String>>() {

	public Iterable<String> doInGemfire(Region region)
	        throws GemFireCheckedException, GemFireException {

		Region<String, String> localRegion = (Region<String, String>) region;

		localRegion.put("1", "one");
		localRegion.put("3", "three");

		return localRegion.query("length < 5");
	}
});

为了充分发挥 Pivotal GemFire 查询语言的强大功能,开发人员可以使用 findfindUnique 方法,与 query 方法相比,它们可以跨多个 Region 执行查询、执行投影等。

当查询选择多个项目(通过 SelectResults)时,应使用 find 方法,而后者 findUnique,顾名思义,当只返回一个对象时使用。

7.2. 异常转换

使用新的数据访问技术不仅需要适应新的 API,还需要处理该技术特有的异常。

为了适应异常处理情况,*Spring Framework* 提供了一个与技术无关且一致的 异常层次结构,它将应用程序从专有且通常是“检查型”异常抽象为一组集中的运行时异常。

正如 *Spring Framework* 文档中提到的,异常转换可以通过使用 @Repository 注解和 AOP,通过定义 PersistenceExceptionTranslationPostProcessor bean 透明地应用于您的数据访问对象 (DAO)。当使用 Pivotal GemFire 时,只要声明了 CacheFactoryBean(例如,使用 <gfe:cache/><gfe:client-cache> 声明),就会启用相同的异常转换功能,该 CacheFactoryBean 充当异常转换器,并由 Spring 基础设施自动检测和相应使用。

7.3. 本地缓存事务管理

*Spring Framework* 最受欢迎的功能之一是 事务管理

如果您不熟悉 Spring 的事务抽象,那么我们强烈建议您阅读有关 *Spring 事务管理* 基础设施的内容,因为它提供了一个一致的*编程模型*,该模型透明地跨多个 API 工作,并且可以通过编程或声明方式进行配置(最受欢迎的选择)。

对于 Pivotal GemFire,Spring Data for Pivotal GemFire 提供了一个专用的、每个缓存的 PlatformTransactionManager,一旦声明,它允许通过 Spring 以原子方式执行 Region 操作。

使用 XML 启用事务管理
<gfe:transaction-manager id="txManager" cache-ref="myCache"/>
如果 Pivotal GemFire 缓存以默认名称 gemfireCache 定义,则可以通过删除 cache-ref 属性进一步简化上面的示例。与其他 Spring Data for Pivotal GemFire 命名空间元素一样,如果未配置缓存 bean 名称,则将使用上述命名约定。此外,如果未明确指定事务管理器名称,则事务管理器名称为“gemfireTransactionManager”。

目前,Pivotal GemFire 支持带有**读已提交**隔离的乐观事务。此外,为了保证此隔离,开发人员应避免进行手动修改缓存中值的**原地**更改。为防止这种情况发生,事务管理器默认将缓存配置为使用**读时复制**语义,这意味着每次执行读取时都会创建实际值的克隆。如果需要,可以通过 copyOnRead 属性禁用此行为。

由于在启用**读时复制**时会为给定键的值创建副本,因此您必须随后调用 Region.put(key, value) 才能以事务方式更新值。

有关底层 Geode 事务管理器的语义和行为的更多信息,请参阅 Geode CacheTransactionManager Javadoc 以及 文档

7.4. 全局 JTA 事务管理

Pivotal GemFire 也可以参与基于全局 JTA 的事务,例如由 Java EE 应用程序服务器(例如 WebSphere Application Server (WAS))使用容器管理事务 (CMT) 以及其他 JTA 资源管理的事务。

然而,与许多其他 JTA“兼容”资源(例如 ActiveMQ 等 JMS 消息代理)不同,Pivotal GemFire **不是** XA 兼容资源。因此,Pivotal GemFire 必须在 JTA 事务中定位为“*最后资源*”(*准备阶段*),因为它不实现两阶段提交协议,或者更确切地说不处理分布式事务。

许多支持CMT的托管环境都支持JTA事务中的“Last Resource”(不符合XA规范的资源),尽管JTA规范中并未实际要求。有关不符合XA规范的“Last Resource”的含义,请参阅Red Hat的文档。事实上,Red Hat的JBoss项目Narayana就是这样一个LGPL开源实现。Narayana将其称为“Last Resource Commit Optimization”(LRCO)。更多详情请参见此处

然而,无论您是在独立环境中使用支持“Last Resource”的开源JTA事务管理实现来使用Pivotal GemFire,还是在托管环境(例如Java EE AS,如WAS)中使用,Spring Data for Pivotal GemFire都能满足您的需求。

在涉及多个事务资源的JTA事务中,您必须完成一系列步骤才能正确地将Pivotal GemFire用作“Last Resource”。此外,在这种配置中,只能有一个不符合XA规范的资源(例如Pivotal GemFire)。

1) 首先,您必须完成Pivotal GemFire文档此处的步骤1-4。

上述#1独立于您的Spring [Boot]和/或[Data for Pivotal GemFire]应用程序,并且必须成功完成。

2) 参考Pivotal GemFire文档中的步骤5,当使用@EnableGemFireAsLastResource注解时,Spring Data for Pivotal GemFire的注解支持将尝试为您设置GemFireCachecopyOnRead属性。

但是,如果SDG的自动配置在这方面不成功,那么您必须在<gfe:cache><gfe:client-cache> XML元素中显式设置copy-on-read属性,或者在JavaConfig中将CacheFactoryBean类的copyOnRead属性设置为true。例如

ClientCache XML

使用XML设置copy-on-read(客户端)
<gfe:client-cache ... copy-on-read="true"/>

ClientCache JavaConfig

使用JavaConfig设置copyOnRead(客户端)
@Bean
ClientCacheFactoryBean gemfireCache() {

  ClientCacheFactoryBean gemfireCache = new ClientCacheFactoryBean();

  gemfireCache.setCopyOnRead(true);

  return gemfireCache;
}

Peer Cache XML

使用XML设置copy-on-read(服务器)
<gfe:cache ... copy-on-read="true"/>

Peer Cache JavaConfig

使用JavaConfig设置copyOnRead(服务器)
@Bean
CacheFactoryBean gemfireCache() {

  CacheFactoryBean gemfireCache = new CacheFactoryBean();

  gemfireCache.setCopyOnRead(true);

  return gemfireCache;
}
显式设置copy-on-read属性或copyOnRead属性实际上没有必要。启用事务管理会自动处理读取时的复制。

3) 此时,您跳过Pivotal GemFire文档中的步骤6-8,让Spring Data Geode发挥其魔力。您所需要做的就是使用Spring Data for Pivotal GemFire@EnableGemFireAsLastResource注解来注解您的Spring @Configuration类,Spring的事务管理基础设施和Spring Data for Pivotal GemFire的@EnableGemFireAsLastResource注解配置的组合就能实现这一点。

配置如下……​

@Configuration
@EnableGemFireAsLastResource
@EnableTransactionManagement(order = 1)
class GeodeConfiguration {
  ...
}

唯一的要求是……​

3.1) @EnableGemFireAsLastResource注解必须与Spring的@EnableTransactionManagement注解在同一个Spring @Configuration类上声明。

3.2) @EnableTransactionManagement注解的order属性必须显式设置为非Integer.MAX_VALUEInteger.MIN_VALUE的整数值(默认为Integer.MAX_VALUE)。

当然,希望您知道在使用JTA事务时,您还需要像这样配置Spring的JtaTransactionManager

@Bean
public JtaTransactionManager transactionManager(UserTransaction userTransaction) {

   JtaTransactionManager transactionManager = new JtaTransactionManager();

   transactionManager.setUserTransaction(userTransaction);

   return transactionManager;
}
本地缓存事务管理一节中的配置适用于此处。Spring Data for Pivotal GemFire的GemfireTransactionManager仅适用于“本地专用”缓存事务,适用于“全局”JTA事务。因此,在这种情况下,您配置SDG GemfireTransactionManager。您如上所示配置Spring的JtaTransactionManager

有关使用Spring事务管理与JTA的更多详情,请参见此处

实际上,Spring Data for Pivotal GemFire的@EnableGemFireAsLastResource注解导入的配置包含两个Aspect bean定义,它们在事务操作的适当点处理Pivotal GemFire o.a.g.ra.GFConnectionFactory.getConnection()o.a.g.ra.GFConnection.close()操作。

具体来说,正确的事件顺序如下:

  1. jtaTransation.begin()

  2. GFConnectionFactory.getConnection()

  3. 调用应用程序的@Transactional服务方法

  4. jtaTransaction.commit()jtaTransaction.rollback()

  5. 最后,GFConnection.close()

这与您作为应用程序开发人员,如果必须手动使用JTA API + Pivotal GemFire API时,会如何编写代码是一致的,如Pivotal GemFire示例所示。

谢天谢地,Spring为您完成了繁重的工作,您在应用适当的配置(如上所示)后所需要做的就是:

将服务方法声明为@Transactional
@Service
class MyTransactionalService {

  @Transactional
  public <Return-Type> someTransactionalServiceMethod() {
    // perform business logic interacting with and accessing multiple JTA resources atomically
  }

  ...
}

一旦您的应用程序进入@Transactional边界(即调用MyTransactionService.someTransactionalServiceMethod()时),上述#1和#4就会由Spring基于JTA的PlatformTransactionManager为您妥善处理。

#2和#3由Spring Data for Pivotal GemFire通过@EnableGemFireAsLastResource注解启用的新Aspects处理。

#3当然是您的应用程序的责任。

事实上,配置适当的日志后,您将看到正确的事件顺序……​

事务日志输出
2017-Jun-22 11:11:37 TRACE TransactionInterceptor - Getting transaction for [example.app.service.MessageService.send]

2017-Jun-22 11:11:37 TRACE GemFireAsLastResourceConnectionAcquiringAspect - Acquiring {data-store-name} Connection
from {data-store-name} JCA ResourceAdapter registered at [gfe/jca]

2017-Jun-22 11:11:37 TRACE MessageService - PRODUCER [ Message :
[{ @type = example.app.domain.Message, id= MSG0000000000, message = SENT }],
JSON : [{"id":"MSG0000000000","message":"SENT"}] ]

2017-Jun-22 11:11:37 TRACE TransactionInterceptor - Completing transaction for [example.app.service.MessageService.send]

2017-Jun-22 11:11:37 TRACE GemFireAsLastResourceConnectionClosingAspect - Closed {data-store-name} Connection @ [Reference [...]]

有关使用Pivotal GemFire缓存级别事务的更多详情,请参见此处

有关在JTA事务中使用Pivotal GemFire的更多详情,请参见此处

有关将Pivotal GemFire配置为“Last Resource”的更多详情,请参见此处

7.5. TransactionEventListener

在使用事务时,可能希望注册一个监听器以在事务提交之前或之后执行某些操作,或者在回滚发生后执行某些操作。

Spring Data for Pivotal GemFire通过@TransactionalEventListener注解,使得创建将在事务特定阶段调用的监听器变得容易。用@TransactionalEventListener注解的方法(如下所示)将在指定的phase中,收到从事务方法发布事件的通知。

事务提交后事件监听器
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handleAfterCommit(MyEvent event) {
    // do something after transaction is committed
}

为了调用上述方法,您必须在事务中发布一个事件,如下所示:

发布事务事件
@Service
class MyTransactionalService {

  @Autowired
  private final ApplicationEventPublisher applicationEventPublisher;

  @Transactional
  public <Return-Type> someTransactionalServiceMethod() {

    // Perform business logic interacting with and accessing multiple transactional resources atomically, then...

    applicationEventPublisher.publishEvent(new MyEvent(...));
  }

  ...
}

@TransactionalEventListener注解允许您指定事件处理方法将被调用的事务phase。选项包括:AFTER_COMMITAFTER_COMPLETIONAFTER_ROLLBACKBEFORE_COMMIT。如果未指定,phase默认为AFTER_COMMIT。如果您希望即使没有事务存在也调用监听器,您可以将fallbackExecution设置为true

7.6. 持续查询 (CQ)

Pivotal GemFire提供的一个强大功能是持续查询(或CQ)。

简而言之,CQ允许开发人员创建和注册一个OQL查询,然后在Pivotal GemFire中添加的新数据与查询谓词匹配时自动收到通知。Spring Data for Pivotal GemFire通过org.springframework.data.gemfire.listener包及其监听器容器为CQ提供了专门的支持;其功能和命名与Spring Framework中的JMS集成非常相似;事实上,熟悉Spring中JMS支持的用户应该会感到非常适应。

基本上,Spring Data for Pivotal GemFire允许POJO上的方法成为CQ的端点。只需定义查询并指示当匹配发生时应调用的方法。Spring Data for Pivotal GemFire会处理其余部分。这与Java EE的消息驱动Bean样式非常相似,但没有任何对基类或接口实现的要求,并且基于Pivotal GemFire。

目前,持续查询仅支持Pivotal GemFire的客户端/服务器拓扑。此外,使用的客户端池需要启用订阅。更多信息请参阅Pivotal GemFire文档

7.6.1. 持续查询监听器容器

Spring Data for Pivotal GemFire通过使用SDG的ContinuousQueryListenerContainer简化了CQ事件的创建、注册、生命周期和分派,它为用户完成了所有繁重的工作。熟悉EJB和JMS的用户应该会发现这些概念很熟悉,因为它被设计得尽可能接近Spring Framework中其消息驱动POJO(MDP)提供的支持。

SDG ContinuousQueryListenerContainer充当事件(或消息)监听器容器;它用于接收来自已注册CQ的事件,并调用注入其中的POJO。监听器容器负责所有消息接收的线程处理以及分派到监听器进行处理。它充当EDP(事件驱动POJO)和事件提供者之间的中介,并负责CQ的创建和注册(以接收事件)、资源获取和释放、异常转换等。这使您作为应用程序开发人员,能够编写与接收事件(并对其作出反应)相关的(可能复杂的)业务逻辑,并将样板Pivotal GemFire基础设施关注点委托给框架。

监听器容器是完全可定制的。开发人员可以选择使用CQ线程执行分派(同步交付),或者通过定义合适的java.util.concurrent.Executor(或Spring的TaskExecutor)使用新线程(来自现有池)进行异步方法。根据负载、监听器数量或运行时环境,开发人员应更改或调整执行器以更好地满足其需求。特别是,在托管环境(例如应用程序服务器)中,强烈建议选择合适的TaskExecutor以利用其运行时。

7.6.2. ContinuousQueryListenerContinuousQueryListenerAdapter

ContinuousQueryListenerAdapter类是Spring Data for Pivotal GemFire CQ支持中的最终组件。简而言之,该类允许您以最小的限制将几乎任何实现类公开为EDP。ContinuousQueryListenerAdapter实现了ContinuousQueryListener接口,这是一个简单的监听器接口,类似于Pivotal GemFire的CqListener

考虑以下接口定义。注意各种事件处理方法及其参数:

public interface EventDelegate {
     void handleEvent(CqEvent event);
     void handleEvent(Operation baseOp);
     void handleEvent(Object key);
     void handleEvent(Object key, Object newValue);
     void handleEvent(Throwable throwable);
     void handleQuery(CqQuery cq);
     void handleEvent(CqEvent event, Operation baseOp, byte[] deltaValue);
     void handleEvent(CqEvent event, Operation baseOp, Operation queryOp, Object key, Object newValue);
}
package example;

class DefaultEventDelegate implements EventDelegate {
    // implementation elided for clarity...
}

特别要注意的是,EventDelegate接口的上述实现完全没有Pivotal GemFire依赖项。它确实是一个POJO,我们可以并且将通过以下配置将其转换为EDP。

该类不必实现接口;接口仅用于更好地展示契约与实现之间的解耦。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:gfe="https://www.springframework.org/schema/gemfire"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
    http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
    https://www.springframework.org/schema/gemfire https://www.springframework.org/schema/gemfire/spring-gemfire.xsd
">

	<gfe:client-cache/>

	<gfe:pool subscription-enabled="true">
	   <gfe:server host="localhost" port="40404"/>
	</gfe:pool>

	<gfe:cq-listener-container>
	   <!-- default handle method -->
	   <gfe:listener ref="listener" query="SELECT * FROM /SomeRegion"/>
	   <gfe:listener ref="another-listener" query="SELECT * FROM /AnotherRegion" name="myQuery" method="handleQuery"/>
	</gfe:cq-listener-container>

	<bean id="listener" class="example.DefaultMessageDelegate"/>
	<bean id="another-listener" class="example.DefaultMessageDelegate"/>
  ...
<beans>
上面的示例展示了监听器可以具有的各种形式;至少需要监听器引用和实际查询定义。但是,可以为生成的持续查询指定名称(对监控很有用),也可以指定方法的名称(默认为handleEvent)。指定的方法可以具有各种参数类型,EventDelegate接口列出了允许的类型。

上面的示例使用Spring Data for Pivotal GemFire命名空间来声明事件监听器容器并自动注册监听器。完整的bean定义如下所示:

<!-- this is the Event Driven POJO (MDP) -->
<bean id="eventListener" class="org.springframework.data.gemfire.listener.adapter.ContinuousQueryListenerAdapter">
    <constructor-arg>
        <bean class="gemfireexample.DefaultEventDelegate"/>
    </constructor-arg>
</bean>

<!-- and this is the event listener container... -->
<bean id="gemfireListenerContainer" class="org.springframework.data.gemfire.listener.ContinuousQueryListenerContainer">
    <property name="cache" ref="gemfireCache"/>
    <property name="queryListeners">
      <!-- set of CQ listeners -->
      <set>
        <bean class="org.springframework.data.gemfire.listener.ContinuousQueryDefinition" >
               <constructor-arg value="SELECT * FROM /SomeRegion" />
               <constructor-arg ref="eventListener"/>
        </bean>
      </set>
    </property>
</bean>

每次收到事件时,适配器都会在Pivotal GemFire事件和所需方法参数之间自动透明地执行类型转换。由方法调用引起的任何异常都会被容器捕获和处理(默认情况下会记录日志)。

7.7. 连接Declarable组件

Pivotal GemFire XML配置(通常称为cache.xml)允许用户对象作为配置的一部分进行声明。通常,这些对象是CacheLoaders或Pivotal GemFire支持的其他可插入回调组件。使用原生的Pivotal GemFire配置,通过XML声明的每个用户类型都必须实现Declarable接口,该接口允许通过Properties实例将任意参数传递给声明的类。

在本节中,我们描述了如何在cache.xml中定义这些可插入组件时使用Spring进行配置,同时将您的缓存/区域配置定义在cache.xml中。这使得您的可插入组件可以专注于应用程序逻辑,而不是DataSources或其他协作者的位置或创建。

但是,如果您正在启动一个全新的项目,建议您直接在Spring中配置Cache、Region和其他可插入的Pivotal GemFire组件。这避免了从Declarable接口或本节中介绍的基类继承。

有关此方法的更多信息,请参见以下侧边栏。

消除Declarable组件

开发人员可以完全通过Spring配置自定义类型,如配置区域中所述。这样,开发人员不必实现Declarable接口,并且还可以从Spring IoC容器的所有功能中受益(不仅是依赖注入,还有生命周期和实例管理)。

作为使用Spring配置Declarable组件的示例,考虑以下声明(取自DeclarableJavadoc):

<cache-loader>
   <class-name>com.company.app.DBLoader</class-name>
   <parameter name="URL">
     <string>jdbc://12.34.56.78/mydb</string>
   </parameter>
</cache-loader>

为了简化解析、转换参数和初始化对象的工作,Spring Data for Pivotal GemFire提供了一个基类(WiringDeclarableSupport),它允许Pivotal GemFire用户对象通过模板bean定义进行连接,或者在缺少模板的情况下,通过Spring IoC容器执行自动连接。为了利用此功能,用户对象需要扩展WiringDeclarableSupport,它会自动定位声明的BeanFactory并在初始化过程中执行连接。

为什么需要基类?

在当前的Pivotal GemFire版本中,没有对象工厂的概念,声明的类型是实例化并直接使用的。换句话说,没有简单的方法可以在Pivotal GemFire之外管理对象的创建。

7.7.1. 使用模板bean定义进行配置

使用时,WiringDeclarableSupport会首先尝试定位现有的bean定义并将其用作连接模板。除非另行指定,否则组件类名将用作隐式bean定义名称。

让我们看看在这种情况下,我们的DBLoader声明会是什么样子:

class DBLoader extends WiringDeclarableSupport implements CacheLoader {

  private DataSource dataSource;

  public void setDataSource(DataSource dataSource){
    this.dataSource = dataSource;
  }

  public Object load(LoaderHelper helper) { ... }
}
<cache-loader>
   <class-name>com.company.app.DBLoader</class-name>
   <!-- no parameter is passed (use the bean's implicit name, which is the class name) -->
</cache-loader>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
    http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
">

  <bean id="dataSource" ... />

  <!-- template bean definition -->
  <bean id="com.company.app.DBLoader" abstract="true" p:dataSource-ref="dataSource"/>
</beans>

在上述场景中,由于未指定参数,因此将ID/名称为com.company.app.DBLoader的bean用作连接Pivotal GemFire创建的实例的模板。对于bean名称使用不同约定的情况,可以在Pivotal GemFire配置中传入bean-name参数:

<cache-loader>
   <class-name>com.company.app.DBLoader</class-name>
   <!-- pass the bean definition template name as parameter -->
   <parameter name="bean-name">
     <string>template-bean</string>
   </parameter>
</cache-loader>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
    http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
">

  <bean id="dataSource" ... />

   <!-- template bean definition -->
   <bean id="template-bean" abstract="true" p:dataSource-ref="dataSource"/>

</beans>
模板bean定义不必在XML中声明。允许任何格式(Groovy、注解等)。

7.7.2. 使用自动装配和注解进行配置

默认情况下,如果未找到bean定义,WiringDeclarableSupport自动装配声明实例。这意味着,除非实例提供任何依赖注入元数据,否则容器将查找对象setter并尝试自动满足这些依赖项。但是,开发人员也可以使用JDK 5注解为自动装配过程提供额外信息。

我们强烈建议阅读Spring文档中专门的章节,以获取有关支持的注解和启用因素的更多信息。

例如,上述假设的DBLoader声明可以通过以下方式注入Spring配置的DataSource

class DBLoader extends WiringDeclarableSupport implements CacheLoader {

  // use annotations to 'mark' the needed dependencies
  @javax.inject.Inject
  private DataSource dataSource;

  public Object load(LoaderHelper helper) { ... }
}
<cache-loader>
   <class-name>com.company.app.DBLoader</class-name>
   <!-- no need to declare any parameters since the class is auto-wired -->
</cache-loader>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
    http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
">

     <!-- enable annotation processing -->
     <context:annotation-config/>

</beans>

通过使用JSR-330注解,CacheLoader代码得到了简化,因为DataSource的位置和创建已经外部化,用户代码只关注加载过程。DataSource可以是事务性的、延迟创建的、在多个对象之间共享的或从JNDI检索的。这些方面可以轻松地通过Spring容器配置和更改,而无需修改DBLoader代码。

7.8. Spring缓存抽象支持

Spring Data for Pivotal GemFire提供了Spring 缓存抽象的实现,将Pivotal GemFire定位为Spring缓存基础设施中的缓存提供者

要将Pivotal GemFire用作后端实现,即Spring缓存抽象中的“缓存提供者”,只需将GemfireCacheManager添加到您的配置中:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:cache="http://www.springframework.org/schema/cache"
       xmlns:gfe="https://www.springframework.org/schema/gemfire"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
    http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/cache https://www.springframework.org/schema/cache/spring-cache.xsd
    https://www.springframework.org/schema/gemfire https://www.springframework.org/schema/gemfire/spring-gemfire.xsd
">

  <!-- enable declarative caching -->
  <cache:annotation-driven/>

  <gfe:cache id="gemfire-cache"/>

  <!-- declare GemfireCacheManager; must have a bean ID of 'cacheManager' -->
  <bean id="cacheManager" class="org.springframework.data.gemfire.cache.GemfireCacheManager"
      p:cache-ref="gemfire-cache">

</beans>
如果使用默认缓存bean名称(即“gemfireCache”),即没有显式ID的<gfe:cache>,则CacheManager bean定义上的cache-ref属性不是必需的。

GemfireCacheManager(Singleton)bean实例被声明并且声明式缓存被启用时(无论是使用<cache:annotation-driven/>在XML中,还是使用Spring的@EnableCaching注解在JavaConfig中),Spring缓存注解(例如@Cacheable)将识别使用Pivotal GemFire Region在内存中缓存数据的“缓存”。

这些缓存(即Regions)必须在使用它们的缓存注解之前存在,否则会发生错误。

举例来说,假设您有一个客户服务应用程序,其中包含一个执行缓存的CustomerService应用程序组件……​

@Service
class CustomerService {

@Cacheable(cacheNames="Accounts", key="#customer.id")
Account createAccount(Customer customer) {
  ...
}

那么您将需要以下配置。

XML

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:cache="http://www.springframework.org/schema/cache"
       xmlns:gfe="https://www.springframework.org/schema/gemfire"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
    http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/cache https://www.springframework.org/schema/cache/spring-cache.xsd
    https://www.springframework.org/schema/gemfire https://www.springframework.org/schema/gemfire/spring-gemfire.xsd
">

  <!-- enable declarative caching -->
  <cache:annotation-driven/>

  <bean id="cacheManager" class="org.springframework.data.gemfire.cache.GemfireCacheManager">

  <gfe:cache/>

  <gfe:partitioned-region id="accountsRegion" name="Accounts" persistent="true" ...>
    ...
  </gfe:partitioned-region>
</beans>

JavaConfig

@Configuration
@EnableCaching
class ApplicationConfiguration {

  @Bean
  CacheFactoryBean gemfireCache() {
    return new CacheFactoryBean();
  }

  @Bean
  GemfireCacheManager cacheManager() {
    GemfireCacheManager cacheManager = GemfireCacheManager();
    cacheManager.setCache(gemfireCache());
    return cacheManager;
  }

  @Bean("Accounts")
  PartitionedRegionFactoryBean accountsRegion() {
    PartitionedRegionFactoryBean accounts = new PartitionedRegionFactoryBean();

    accounts.setCache(gemfireCache());
    accounts.setClose(false);
    accounts.setPersistent(true);

    return accounts;
  }
}

当然,您可以自由选择任何您喜欢的区域类型(例如,REPLICATE、PARTITION、LOCAL等)。

有关Spring缓存抽象的更多详情,请再次参考文档

8. 使用Pivotal GemFire序列化

为了提高Pivotal GemFire内存数据网格的整体性能,Pivotal GemFire支持一种名为PDX的专用序列化协议,它比标准Java序列化更快,结果更紧凑,并且可以透明地跨各种语言平台(Java、C++和.NET)工作。

有关更多详细信息,请参见PDX序列化功能PDX序列化内部原理

本章讨论了Spring Data for Pivotal GemFire简化和改进Pivotal GemFire在Java中自定义序列化的各种方式。

8.1. 连接反序列化实例

序列化对象具有瞬态数据是相当常见的。瞬态数据通常取决于它在某个时间点所处的系统或环境。例如,DataSource是环境特定的。序列化此类信息是无用的,甚至可能是危险的,因为它特定于某个VM或机器。对于这种情况,Spring Data for Pivotal GemFire提供了一个特殊的Instantiator,它为Pivotal GemFire在反序列化过程中创建的每个新实例执行连接。

通过这种机制,您可以依靠Spring容器来注入和管理某些依赖项,从而轻松地将瞬态数据与持久数据分开,并以透明的方式拥有丰富的领域对象。

Spring用户可能会发现这种方法与@Configurable类似。WiringInstantiator的工作原理类似于WiringDeclarableSupport,它首先尝试定位bean定义作为连接模板,否则回退到自动连接。

有关连接功能的更多详细信息,请参见上一节(连接Declarable组件)。

要使用SDG Instantiator,请将其声明为bean,如下例所示:

<bean id="instantiator" class="org.springframework.data.gemfire.serialization.WiringInstantiator">
  <!-- DataSerializable type -->
  <constructor-arg>org.pkg.SomeDataSerializableClass</constructor-arg>
  <!-- type id -->
  <constructor-arg>95</constructor-arg>
</bean>

在Spring容器启动期间,一旦初始化,Instantiator默认情况下会向Pivotal GemFire序列化系统注册自己,并对Pivotal GemFire在反序列化期间创建的所有SomeDataSerializableClass实例执行连接。

8.2. 自动生成自定义Instantiators

对于数据密集型应用程序,随着数据流入,每台机器上可能会创建大量实例。Pivotal GemFire使用反射来创建新类型,但在某些情况下,这可能证明是昂贵的。一如既往,最好进行性能分析以量化是否存在这种情况。对于这种情况,Spring Data for Pivotal GemFire允许自动生成Instatiator类,这些类(使用默认构造函数)实例化新类型而无需使用反射。以下示例展示了如何创建实例化器:

<bean id="instantiatorFactory" class="org.springframework.data.gemfire.serialization.InstantiatorFactoryBean">
  <property name="customTypes">
    <map>
      <entry key="org.pkg.CustomTypeA" value="1025"/>
      <entry key="org.pkg.CustomTypeB" value="1026"/>
    </map>
  </property>
</bean>

前面的定义自动为两个类(CustomTypeACustomTypeB)生成了两个Instantiators,并使用用户ID 10251026将它们注册到Pivotal GemFire。这两个Instantiators避免了使用反射,并直接通过Java代码创建实例。

9. POJO映射

本节内容

9.1. 对象映射基础

本节涵盖 Spring Data 对象映射、对象创建、字段和属性访问、可变性与不可变性的基础知识。请注意,本节仅适用于不使用底层数据存储(如 JPA)对象映射的 Spring Data 模块。此外,请务必查阅存储特定章节,了解存储特定对象映射,例如索引、自定义列或字段名称等。

Spring Data 对象映射的核心职责是创建领域对象实例,并将存储原生数据结构映射到这些实例上。这意味着我们需要两个基本步骤

  1. 通过使用公开的构造函数之一创建实例。

  2. 实例填充以具体化所有公开的属性。

9.1.1. 对象创建

Spring Data 会自动尝试检测持久化实体的构造函数,该构造函数将用于具体化该类型的对象。解析算法的工作方式如下

  1. 如果有无参数构造函数,则使用它。其他构造函数将被忽略。

  2. 如果只有一个接受参数的构造函数,则使用它。

  3. 如果有多个接受参数的构造函数,则Spring Data将使用的构造函数必须使用@PersistenceConstructor进行注解。

值解析假定构造函数参数名与实体的属性名匹配,即解析将按照属性将要填充的方式执行,包括映射中的所有自定义(不同的数据存储列或字段名等)。这还要求类文件中提供参数名信息,或者构造函数上存在 @ConstructorProperties 注解。

值解析可以通过使用 Spring Framework 的 @Value 值注解和存储特定的 SpEL 表达式进行自定义。请查阅有关存储特定映射的部分以获取更多详细信息。

对象创建内部

为了避免反射的开销,Spring Data 对象创建默认使用运行时生成的工厂类,该类将直接调用域类的构造函数。即,对于此示例类型

class Person {
  Person(String firstname, String lastname) { … }
}

我们将在运行时创建一个在语义上等同于此的工厂类

class PersonObjectInstantiator implements ObjectInstantiator {

  Object newInstance(Object... args) {
    return new Person((String) args[0], (String) args[1]);
  }
}

这使我们获得了大约 10% 的反射性能提升。为了使领域类符合此优化条件,它需要遵循一组约束

  • 它不能是私有类

  • 它不能是非静态内部类

  • 它不能是 CGLib 代理类

  • Spring Data 将使用的构造函数不能是私有的

如果以上任何标准匹配,Spring Data 将回退到通过反射实例化实体。

9.1.2. 属性填充

一旦创建了实体实例,Spring Data 会填充该类的所有剩余持久化属性。除非已由实体的构造函数填充(即通过其构造函数参数列表消耗),否则将首先填充标识符属性,以允许解析循环对象引用。之后,所有尚未由构造函数填充的非瞬态属性将在实体实例上设置。为此,我们使用以下算法

  1. 如果属性是不可变的但公开了一个 wither 方法(见下文),我们使用 wither 方法创建一个带有新属性值的新实体实例。

  2. 如果定义了属性访问(即通过 getter 和 setter 访问),我们将调用 setter 方法。

  3. 默认情况下,我们直接设置字段值。

属性填充内部

与我们的对象构造优化类似,我们也使用 Spring Data 运行时生成的访问器类与实体实例进行交互。

class Person {

  private final Long id;
  private String firstname;
  private @AccessType(Type.PROPERTY) String lastname;

  Person() {
    this.id = null;
  }

  Person(Long id, String firstname, String lastname) {
    // Field assignments
  }

  Person withId(Long id) {
    return new Person(id, this.firstname, this.lastame);
  }

  void setLastname(String lastname) {
    this.lastname = lastname;
  }
}
示例 1. 生成的属性访问器
class PersonPropertyAccessor implements PersistentPropertyAccessor {

  private static final MethodHandle firstname;              (2)

  private Person person;                                    (1)

  public void setProperty(PersistentProperty property, Object value) {

    String name = property.getName();

    if ("firstname".equals(name)) {
      firstname.invoke(person, (String) value);             (2)
    } else if ("id".equals(name)) {
      this.person = person.withId((Long) value);            (3)
    } else if ("lastname".equals(name)) {
      this.person.setLastname((String) value);              (4)
    }
  }
}
1 PropertyAccessor 包含底层对象的可变实例。这是为了允许修改原本不可变的属性。
2 默认情况下,Spring Data 使用字段访问来读取和写入属性值。根据 private 字段的可见性规则,使用 MethodHandles 与字段交互。
3 该类公开了一个 withId(…) 方法,用于设置标识符,例如当一个实例插入到数据存储中并生成了标识符时。调用 withId(…) 会创建一个新的 Person 对象。所有后续的修改都将在新实例中进行,而不会触及旧实例。
4 使用属性访问允许直接方法调用而无需使用 MethodHandles

这使我们获得了大约 25% 的反射性能提升。为了使领域类符合此优化条件,它需要遵循一组约束

  • 类型不能位于默认包或 java 包下。

  • 类型及其构造函数必须是 public

  • 作为内部类的类型必须是 static 的。

  • 所使用的 Java 运行时必须允许在原始 ClassLoader 中声明类。Java 9 及更高版本施加了某些限制。

默认情况下,Spring Data 会尝试使用生成的属性访问器,如果检测到限制,则回退到基于反射的访问器。

让我们看看以下实体

示例 2. 示例实体
class Person {

  private final @Id Long id;                                                (1)
  private final String firstname, lastname;                                 (2)
  private final LocalDate birthday;
  private final int age; (3)

  private String comment;                                                   (4)
  private @AccessType(Type.PROPERTY) String remarks;                        (5)

  static Person of(String firstname, String lastname, LocalDate birthday) { (6)

    return new Person(null, firstname, lastname, birthday,
      Period.between(birthday, LocalDate.now()).getYears());
  }

  Person(Long id, String firstname, String lastname, LocalDate birthday, int age) { (6)

    this.id = id;
    this.firstname = firstname;
    this.lastname = lastname;
    this.birthday = birthday;
    this.age = age;
  }

  Person withId(Long id) {                                                  (1)
    return new Person(id, this.firstname, this.lastname, this.birthday, this.age);
  }

  void setRemarks(String remarks) {                                         (5)
    this.remarks = remarks;
  }
}
1 标识符属性是 final 的,但在构造函数中设置为 null。该类公开了一个 withId(…) 方法,用于设置标识符,例如当实例插入数据存储并生成标识符时。原始的 Person 实例保持不变,因为创建了一个新的实例。同样的模式通常也适用于其他由存储管理但可能必须为持久化操作更改的属性。
2 firstnamelastname 属性是普通的不可变属性,可能通过 getter 公开。
3 age 属性是不可变的,但由 birthday 属性派生。按照所示设计,数据库值将优先于默认值,因为 Spring Data 使用唯一声明的构造函数。即使目的是优先计算,重要的是此构造函数也接受 age 作为参数(可能忽略它),否则属性填充步骤将尝试设置 age 字段,并因其不可变且不存在 wither 而失败。
4 comment 属性是可变的,并通过直接设置其字段进行填充。
5 remarks 属性是可变的,通过直接设置 comment 字段或调用 setter 方法进行填充
6 该类公开了一个工厂方法和一个构造函数用于对象创建。这里的核心思想是使用工厂方法而不是额外的构造函数,以避免通过@PersistenceConstructor进行构造函数歧义消除的需要。相反,属性的默认值在工厂方法中处理。

9.1.3. 一般建议

  • 尽量坚持使用不可变对象 — 不可变对象创建起来很简单,因为实例化一个对象就只剩下调用其构造函数了。此外,这避免了您的领域对象中充斥着允许客户端代码操作对象状态的 setter 方法。如果您需要这些方法,最好将它们设为包保护,以便只能由有限数量的协同定位类型调用。仅限构造函数的实例化比属性填充快 30%。

  • 提供一个全参构造函数 — 即使您不能或不想将您的实体建模为不可变值,提供一个接受实体所有属性(包括可变属性)作为参数的构造函数仍然很有价值,因为这允许对象映射跳过属性填充以获得最佳性能。

  • 使用工厂方法而不是重载构造函数以避免@PersistenceConstructor —— 为了获得最佳性能,需要一个全参数构造函数,我们通常希望公开更多特定于应用程序用例的构造函数,这些构造函数省略了诸如自动生成的标识符等。使用静态工厂方法来公开这些全参数构造函数的变体是一种成熟的模式。

  • 确保您遵守允许使用生成的实例化器和属性访问器类的约束 —— 

  • 为了生成标识符,仍然使用带有wither方法的final字段 —— 

  • 使用 Lombok 避免样板代码 — 由于持久化操作通常需要一个接受所有参数的构造函数,它们的声明变成了繁琐的样板参数到字段赋值的重复,最好通过使用 Lombok 的 @AllArgsConstructor 来避免。

9.1.4. Kotlin支持

Spring Data 适应 Kotlin 的特性,以允许对象创建和修改。

Kotlin 对象创建

Kotlin类支持实例化,所有类默认是不可变的,需要显式属性声明来定义可变属性。考虑以下dataPerson

data class Person(val id: String, val name: String)

上述类编译成一个带有显式构造函数的典型类。我们可以通过添加另一个构造函数并用@PersistenceConstructor注解它来定制此类的构造函数偏好:

data class Person(var id: String, val name: String) {

    @PersistenceConstructor
    constructor(id: String) : this(id, "unknown")
}

Kotlin通过允许在未提供参数时使用默认值来支持参数可选性。当Spring Data检测到具有参数默认值的构造函数时,如果数据存储未提供值(或简单地返回null),则会省略这些参数,以便Kotlin可以应用参数默认值。考虑以下对name应用参数默认值的类:

data class Person(var id: String, val name: String = "unknown")

每次 name 参数不在结果中或其值为 null 时,name 都会默认为 unknown

Kotlin 数据类的属性填充

在 Kotlin 中,所有类默认都是不可变的,并且需要显式属性声明来定义可变属性。考虑以下 dataPerson

data class Person(val id: String, val name: String)

这个类实际上是不可变的。它允许创建新实例,因为 Kotlin 会生成一个 copy(…) 方法,该方法会创建新对象实例,复制现有对象的所有属性值,并应用作为方法参数提供的属性值。

9.2. 实体映射

Spring Data for Pivotal GemFire 提供对存储在 Region 中的实体进行映射的支持。映射元数据通过在应用程序域类上使用注解来定义,如下例所示:

示例 3. 将域类映射到 Pivotal GemFire Region
@Region("People")
public class Person {

  @Id Long id;

  String firstname;
  String lastname;

  @PersistenceConstructor
  public Person(String firstname, String lastname) {
    // …
  }

  …
}

@Region注解可用于自定义Person类实例存储的Region。@Id注解可用于注解应作为缓存Region键的属性,以标识Region条目。@PersistenceConstructor注解有助于消除多个可能可用的带参数构造函数的歧义,并显式将注解的构造函数标记为用于构建实体的构造函数。在没有或只有一个构造函数的应用程序域类中,您可以省略此注解。

除了将实体存储在顶级区域中,实体也可以存储在子区域中,如下例所示:

@Region("/Users/Admin")
public class Admin extends User {
  …
}

@Region("/Users/Guest")
public class Guest extends User {
  …
}

请务必使用Pivotal GemFire Region的完整路径,如通过使用<*-region>元素的idname属性与Spring Data for Pivotal GemFire XML命名空间定义的那样。

9.2.1. 按区域类型划分的实体映射

除了@Region注解之外,Spring Data for Pivotal GemFire还识别特定于类型的区域映射注解:@ClientRegion@LocalRegion@PartitionRegion@ReplicateRegion

在功能上,这些注解在SDG映射基础设施中与通用@Region注解的处理方式完全相同。然而,这些额外的映射注解在Spring Data for Pivotal GemFire的注解配置模型中很有用。当与Spring @Configuration注解类上的@EnableEntityDefinedRegions配置注解结合使用时,可以在本地缓存中生成区域,无论应用程序是客户端还是对等体。

这些注解让您能够更具体地指定您的应用程序实体类应映射到哪种类型的Region,并且还对Region的数据管理策略产生影响(例如,分区(也称为分片)与复制数据)。

在SDG注解配置模型中使用这些特定于类型的Region映射注解,可以省去您在配置中显式定义这些Region的麻烦。

9.3. 存储库映射

作为在实体类上使用@Region注解指定实体存储在哪个Region中的替代方法,您也可以在实体的Repository接口上指定@Region注解。有关更多详细信息,请参见Spring Data for Pivotal GemFire Repositories

但是,假设您想将Person记录存储在多个Pivotal GemFire Region中(例如,PeopleCustomers)。那么您可以按如下方式定义相应的Repository接口扩展:

@Region("People")
public interface PersonRepository extends GemfireRepository<Person, String> {
…
}

@Region("Customers")
public interface CustomerRepository extends GemfireRepository<Person, String> {
...
}

然后,单独使用每个Repository,您可以将实体存储在多个Pivotal GemFire Region中,如下例所示:

@Service
class CustomerService {

  CustomerRepository customerRepo;

  PersonRepository personRepo;

  Customer update(Customer customer) {
    customerRepo.save(customer);
    personRepo.save(customer);
    return customer;
  }

您甚至可以将update服务方法包装在Spring管理的事务中,无论是本地缓存事务还是全局事务。

9.4. MappingPdxSerializer

Spring Data for Pivotal GemFire 提供了一个自定义的PdxSerializer实现,名为MappingPdxSerializer,它使用Spring Data映射元数据来自定义实体序列化。

序列化器还允许您通过使用Spring Data EntityInstantiator抽象来定制实体实例化。默认情况下,序列化器使用ReflectionEntityInstantiator,它使用映射实体的持久化构造函数。持久化构造函数可以是默认构造函数、单独声明的构造函数,或显式使用@PersistenceConstructor注解的构造函数。

为了为构造函数参数提供参数,序列化器从提供的PdxReader中读取带有命名构造函数参数的字段,并通过使用Spring的@Value注解显式标识,如下例所示:

示例 4. 在实体构造函数参数上使用@Value
public class Person {

  public Person(@Value("#root.thing") String firstName, @Value("bean") String lastName) {
    …
  }
}

以这种方式注解的实体类将从PdxReader中读取“thing”字段,并将其作为构造函数参数firstname的参数值传递。lastName的值是一个名为“bean”的Spring bean。

除了EntityInstantiators提供的自定义实例化逻辑和策略之外,MappingPdxSerializer还提供了超越Pivotal GemFire自身的ReflectionBasedAutoSerializer的能力。

虽然Pivotal GemFire的ReflectionBasedAutoSerializer方便地使用Java反射来填充实体,并使用正则表达式来识别应由序列化器处理(序列化和反序列化)的类型,但它无法像MappingPdxSerializer那样执行以下操作:

  • 为每个实体字段或属性名称和类型注册自定义PdxSerializer对象。

  • 方便地识别ID属性。

  • 自动处理只读属性。

  • 自动处理瞬态属性。

  • 以空安全和类型安全的方式实现更强大的类型过滤(例如,不限于仅用正则表达式表达类型)。

现在我们更详细地探讨MappingPdxSerializer的每个特性。

9.4.1. 自定义PdxSerializer注册

MappingPdxSerializer让您能够根据实体的字段或属性名称和类型注册自定义PdxSerializers

例如,假设您定义了一个建模User的实体类型,如下所示:

package example.app.security.auth.model;

public class User {

  private String name;

  private Password password;

  ...
}

虽然用户的姓名可能不需要任何特殊的逻辑来序列化其值,但序列化密码可能需要额外的逻辑来处理字段或属性的敏感性质。

也许您希望在客户端和服务器之间通过网络发送密码值时保护它,除了TLS之外,并且您只想存储加盐的哈希。当使用MappingPdxSerializer时,您可以注册一个自定义的PdxSerializer来处理用户的密码,如下所示:

示例 5. 按POJO字段/属性类型注册自定义PdxSerializers
Map<?, PdxSerializer> customPdxSerializers = new HashMap<>();

customPdxSerializers.put(Password.class, new SaltedHashPasswordPdxSerializer());

mappingPdxSerializer.setCustomPdxSerializers(customPdxSerializers);

将应用程序定义的SaltedHashPasswordPdxSerializer实例注册到Password应用程序域模型类型后,MappingPdxSerializer将查询自定义PdxSerializer来序列化和反序列化所有Password对象,无论包含对象(例如User)如何。

但是,假设您只想自定义User对象上的Passwords序列化。为此,您可以通过指定Class字段或属性的完全限定名来为User类型注册自定义PdxSerializer,如下例所示:

示例 6. 按POJO字段/属性名称注册自定义PdxSerializers
Map<?, PdxSerializer> customPdxSerializers = new HashMap<>();

customPdxSerializers.put("example.app.security.auth.model.User.password", new SaltedHashPasswordPdxSerializer());

mappingPdxSerializer.setCustomPdxSerializers(customPdxSerializers);

请注意使用完全限定的字段或属性名称(即example.app.security.auth.model.User.password)作为自定义PdxSerializer注册键。

您可以使用更具逻辑性的代码片段来构造注册键,例如:User.class.getName().concat(".password");。我们建议使用此方法而不是前面显示的示例。前面的示例试图尽可能明确地说明注册的语义。

9.4.2. 映射ID属性

与Pivotal GemFire的ReflectionBasedAutoSerializer类似,SDG的MappingPdxSerializer也能够确定实体的标识符。但是,MappingPdxSerializer通过使用Spring Data的映射元数据来做到这一点,特别是通过使用Spring Data的@Id注解找到被指定为标识符的实体属性。或者,任何名为“id”且未明确使用@Id注解的字段或属性也被指定为实体的标识符。

例如:

class Customer {

  @Id
  Long id;

  ...
}

在这种情况下,在序列化期间调用PdxSerializer.toData(..)方法时,Customerid字段通过使用PdxWriter.markIdentifierField(:String)被标记为PDX类型元数据中的标识符字段。

9.4.3. 映射只读属性

当您的实体定义只读属性时会发生什么?

首先,了解什么是“只读”属性很重要。如果您按照JavaBeans规范(如Spring所做)定义POJO,您可能会定义一个具有只读属性的POJO,如下所示:

package example;

class ApplicationDomainType {

  private AnotherType readOnly;

  public AnotherType getReadOnly() [
    this.readOnly;
  }

  ...
}

readOnly属性是只读的,因为它不提供setter方法。它只有一个getter方法。在这种情况下,readOnly属性(不要与readOnly DomainType字段混淆)被认为是只读的。

因此,在反序列化期间,当在PdxSerializer.fromData(:Class<ApplicationDomainType>, :PdxReader)方法中填充ApplicationDomainType实例时,MappingPdxSerializer不会尝试为该属性设置值,特别是如果PDX序列化字节中存在值。

这在您可能返回某个实体类型的视图或投影,并且只想设置可写状态的情况下非常有用。也许实体的视图或投影基于授权或其他标准。关键是,您可以根据应用程序的用例和要求适当地利用此功能。如果您希望字段或属性始终被写入,只需定义一个setter方法。

9.4.4. 映射瞬态属性

同样,当您的实体定义transient属性时会发生什么?

您会期望您的实体的transient字段或属性在序列化实体时不会序列化到PDX。这正是发生的情况,与Pivotal GemFire自己的ReflectionBasedAutoSerializer不同,后者通过Java反射序列化对象中可访问的一切。

MappingPdxSerializer不会序列化任何被限定为瞬态的字段或属性,无论是通过使用Java自己的transient关键字(在类实例字段的情况下),还是通过在字段或属性上使用@Transient Spring Data注解。

例如,您可以通过以下方式定义具有瞬态字段和属性的实体:

package example;

class Process {

  private transient int id;

  private File workingDirectory;

  private String name;

  private Type type;

  @Transient
  public String getHostname() {
    ...
  }

  ...
}

Processid字段和可读的hostname属性都不会写入PDX。

9.4.5. 按类类型过滤

与Pivotal GemFire的ReflectionBasedAutoSerializer类似,SDG的MappingPdxSerializer允许您过滤要序列化和反序列化的对象类型。

然而,与Pivotal GemFire的ReflectionBasedAutoSerializer使用复杂的正则表达式来表达序列化器处理的类型不同,SDG的MappingPdxSerializer使用更强大的java.util.function.Predicate接口和API来表达类型匹配标准。

如果您喜欢使用正则表达式,可以使用Java的正则表达式支持实现Predicate

Java的Predicate接口的优点是,您可以使用方便且适当的API方法组合Predicates,包括:and(:Predicate)or(:Predicate)negate()

以下示例展示了Predicate API的实际应用:

Predicate<Class<?>> customerTypes =
  type -> Customer.class.getPackage().getName().startsWith(type.getName()); // Include all types in the same package as `Customer`

Predicate includedTypes = customerTypes
  .or(type -> User.class.isAssignble(type)); // Additionally, include User sub-types (e.g. Admin, Guest, etc)

mappingPdxSerializer.setIncludeTypeFilters(includedTypes);

mappingPdxSerializer.setExcludeTypeFilters(
  type -> !Reference.class.getPackage(type.getPackage()); // Exclude Reference types
传递给您的Predicate的任何Class对象都保证不为null

SDG的MappingPdxSerializer包含对包括和排除类类型过滤器的支持。

排除类型过滤

默认情况下,SDG的MappingPdxSerializer注册预定义的Predicates,它们过滤或排除以下包中的类型:

  • java.*

  • com.gemstone.gemfire.*

  • org.apache.geode.*

  • org.springframework.*

此外,MappingPdxSerializer在调用PdxSerializer.toData(:Object, :PdxWriter)时过滤null对象,在调用PdxSerializer.fromData(:Class<?>, :PdxReader)方法时过滤null类类型。

通过简单地定义Predicate并将其添加到MappingPdxSerializer,如前所示,可以非常容易地为其他类类型或整个包的类型添加排除项。

MappingPdxSerializer.setExcludeTypeFilters(:Predicate<Class<?>>)方法是累加的,这意味着它使用Predicate.and(:Predicate<Class<?>>)方法将您的应用程序定义的类型过滤器与上面指出的现有预定义类型过滤器Predicates进行组合。

但是,如果您想包含被排除类型过滤器隐式排除的类类型(例如java.security Principal),该怎么办?请参阅包含类型过滤

包含类型过滤

如果您想显式包含一个类类型,或覆盖一个隐式排除应用程序所需类类型(例如java.security.Principal,它默认被MappingPdxSerializer上的java.*包排除类型过滤器排除)的类类型过滤器,那么只需定义适当的Predicate并使用MappingPdxSerializer.setIncludeTypeFilters(:Predicate<Class<?>>)方法将其添加到序列化器中,如下所示:

Predicate<Class<?>> principalTypeFilter =
  type -> java.security.Principal.class.isAssignableFrom(type);

mappingPdxSerializer.setIncludeTypeFilters(principalTypeFilters);

同样,MappingPdxSerializer.setIncludeTypeFilters(:Predicate<Class<?>>)方法,像setExcludeTypeFilters(:Predicate<Class<?>>)一样,是累加的,因此使用Predicate.or(:Predicate<Class<?>>)组合任何传入的类型过滤器。这意味着您可以根据需要多次调用setIncludeTypeFilters(:Predicate<Class<?>>)

当存在包含类型过滤器时,MappingPdxSerializer会根据类类型是否未被隐式排除或是否被显式包含(两者中任何一个返回true)来决定是否反/序列化类类型的实例。然后,该类类型的实例将被适当地序列化或反序列化。

例如,当如前所示显式注册Predicate<Class<Principal>>的类型过滤器时,它会取消对java.*包类型的隐式排除类型过滤器。

10. Spring Data for Pivotal GemFire Repositories

Spring Data for Pivotal GemFire 提供对使用 Spring Data Repository 抽象的支持,以便轻松将实体持久化到 Pivotal GemFire 中并执行查询。Repository 编程模型的概述请参见此处

10.1. Spring XML配置

要引导Spring Data Repositories,请使用Spring Data for Pivotal GemFire Data命名空间中的<repositories/>元素,如下例所示:

示例 7. 在XML中引导Spring Data for Pivotal GemFire Repositories
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:gfe-data="https://www.springframework.org/schema/data/gemfire"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
    http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
    https://www.springframework.org/schema/data/gemfire https://www.springframework.org/schema/data/gemfire/spring-data-gemfire.xsd
">

  <gfe-data:repositories base-package="com.example.acme.repository"/>

</beans>

上述配置片段查找配置的基本包下的接口,并为这些接口创建由SimpleGemfireRepository支持的Repository实例。

除非您的应用程序域类正确映射到配置的区域,否则引导过程将失败。

10.2. Spring基于Java的配置

或者,许多开发人员更喜欢使用Spring的基于Java的容器配置

使用此方法,您可以使用SDG @EnableGemfireRepositories注解引导Spring Data Repositories,如下例所示:

示例 8. 使用@EnableGemfireRepositories引导Spring Data for Pivotal GemFire Repositories
@SpringBootApplication
@EnableGemfireRepositories(basePackages = "com.example.acme.repository")
class SpringDataApplication {
  ...
}

您可以不使用basePackages属性,而是选择使用类型安全的basePackageClasses属性。basePackageClasses允许您通过仅指定一个应用程序Repository接口类型来指定包含所有应用程序Repository类的包。考虑在每个包中创建一个特殊的无操作标记类或接口,其目的只是为了标识此属性引用的应用程序Repository的位置。

除了basePackagesbasePackageClasses属性之外,与Spring的@ComponentScan注解一样,@EnableGemfireRepositories注解提供了基于Spring的ComponentScan.Filter类型的包含和排除过滤器。您可以使用filterType属性按不同方面进行过滤,例如应用程序Repository类型是否使用特定注解进行注解,或是否扩展特定类类型等。有关更多详细信息,请参阅FilterType Javadoc

@EnableGemfireRepositories注解还允许您通过使用namedQueriesLocation属性指定位于Java Properties文件中的命名OQL查询的位置。属性名称必须与Repository查询方法的名称匹配,属性值是当Repository查询方法被调用时您希望执行的OQL查询。

如果您的应用程序需要一个或多个自定义存储库实现,则可以将repositoryImplementationPostfix属性设置为备用值(默认为Impl)。此功能通常用于扩展Spring Data Repository基础设施,以实现数据存储(例如SDG)未提供的功能。

Pivotal GemFire中需要自定义存储库实现的一个示例是在执行连接时。SDG存储库不支持连接。对于Pivotal GemFire的PARTITION区域,连接必须在共置的PARTITION区域上执行,因为Pivotal GemFire不支持“分布式”连接。此外,等值连接OQL查询必须在Pivotal GemFire函数内部执行。有关Pivotal GemFire等值连接查询的更多详细信息,请参见此处

SDG的存储库基础设施扩展的许多其他方面也可以定制。有关所有配置设置的更多详细信息,请参见@EnableGemfireRepositories Javadoc。

10.3. 执行OQL查询

Spring Data for Pivotal GemFire Repositories允许定义查询方法,以便轻松地针对托管实体映射到的区域执行Pivotal GemFire OQL查询,如下例所示:

示例 9. 示例存储库
@Region("People")
public class Person { … }
public interface PersonRepository extends CrudRepository<Person, Long> {

  Person findByEmailAddress(String emailAddress);

  Collection<Person> findByFirstname(String firstname);

  @Query("SELECT * FROM /People p WHERE p.firstname = $1")
  Collection<Person> findByFirstnameAnnotated(String firstname);

  @Query("SELECT * FROM /People p WHERE p.firstname IN SET $1")
  Collection<Person> findByFirstnamesAnnotated(Collection<String> firstnames);
}

上述示例中列出的第一个查询方法将导致派生以下OQL查询:SELECT x FROM /People x WHERE x.emailAddress = $1。第二个查询方法以相同的方式工作,只是它返回所有找到的实体,而第一个查询方法期望找到单个结果。

如果支持的关键字不足以声明和表达您的OQL查询,或者方法名变得过于冗长,那么您可以像第三和第四个方法所示,用@Query注解查询方法。

下表简要列出了查询方法中支持的关键字示例:

表 4. 查询方法支持的关键字
关键字 示例 逻辑结果

GreaterThan

findByAgeGreaterThan(int age)

x.age > $1

GreaterThanEqual

findByAgeGreaterThanEqual(int age)

x.age >= $1

LessThan

findByAgeLessThan(int age)

x.age < $1

LessThanEqual

findByAgeLessThanEqual(int age)

x.age <= $1

IsNotNull, NotNull

findByFirstnameNotNull()

x.firstname =! NULL

IsNull, Null

findByFirstnameNull()

x.firstname = NULL

In

findByFirstnameIn(Collection x)

x.firstname IN SET $1

NotIn

findByFirstnameNotIn(Collection x)

x.firstname NOT IN SET $1

IgnoreCase

findByFirstnameIgnoreCase(String firstName)

x.firstname.equalsIgnoreCase($1)

(无关键字)

findByFirstname(String name)

x.firstname = $1

Like

findByFirstnameLike(String name)

x.firstname LIKE $1

Not

findByFirstnameNot(String name)

x.firstname != $1

IsTrue, True

findByActiveIsTrue()

x.active = true

IsFalse, False

findByActiveIsFalse()

x.active = false

10.4. 使用注解的OQL查询扩展

许多查询语言,例如Pivotal GemFire的OQL(对象查询语言),具有Spring Data Commons存储库基础设施不直接支持的扩展。

Spring Data Commons存储库基础设施的目标之一是充当最低公分母,以保持对当今应用程序开发中可用和使用的最广泛数据存储的支持和可移植性。从技术上讲,这意味着开发人员可以通过重用现有的特定于应用程序的存储库接口来访问应用程序中Spring Data Commons支持的多个不同数据存储——这是一种方便而强大的抽象。

为了支持Pivotal GemFire的OQL查询语言扩展并保持跨不同数据存储的可移植性,Spring Data for Pivotal GemFire通过使用Java注解添加了对OQL查询扩展的支持。这些注解将被其他Spring Data Repository实现(例如Spring Data JPA或Spring Data Redis)忽略,因为它们没有类似的查询语言特性。

例如,许多数据存储很可能不实现Pivotal GemFire的OQL IMPORT关键字。将IMPORT作为注解(即@Import)而不是作为查询方法签名的一部分(特别是方法“名称”)实现,在评估查询方法名称以构建另一个适合数据存储语言的查询时,不会干扰解析基础设施。

目前,Spring Data for Pivotal GemFire支持的Pivotal GemFire OQL查询语言扩展集包括:

表 5. Repository查询方法支持的Pivotal GemFire OQL扩展
关键字 注解 描述 参数

HINT

@Hint

OQL查询索引提示

String[](例如@Hint({ "IdIdx", "TxDateIdx" }))

IMPORT

@Import

限定应用程序特定类型。

String(例如@Import("org.example.app.domain.Type"))

LIMIT

@Limit

限制返回的查询结果集。

Integer(例如@Limit(10);默认值为Integer.MAX_VALUE)

TRACE

@Trace

启用OQL查询特定的调试。

不适用

例如,假设您有一个Customers应用程序域类和相应的Pivotal GemFire Region,以及一个CustomerRepository和一个按姓氏查找Customers的查询方法,如下所示:

示例 10. 示例客户存储库
package ...;

import org.springframework.data.annotation.Id;
import org.springframework.data.gemfire.mapping.annotation.Region;
...

@Region("Customers")
public class Customer ... {

  @Id
  private Long id;

  ...
}
package ...;

import org.springframework.data.gemfire.repository.GemfireRepository;
...

public interface CustomerRepository extends GemfireRepository<Customer, Long> {

  @Trace
  @Limit(10)
  @Hint("LastNameIdx")
  @Import("org.example.app.domain.Customer")
  List<Customer> findByLastName(String lastName);

  ...
}

上述示例将生成以下OQL查询:

<TRACE> <HINT 'LastNameIdx'> IMPORT org.example.app.domain.Customer; SELECT * FROM /Customers x WHERE x.lastName = $1 LIMIT 10

Spring Data for Pivotal GemFire的Repository扩展在OQL注解扩展与@Query注解结合使用时,会小心避免创建冲突的声明。

另一个例子是,假设您在CustomerRepository中定义了一个原始的@Query注解查询方法,如下所示:

示例 11. CustomerRepository
public interface CustomerRepository extends GemfireRepository<Customer, Long> {

  @Trace
  @Limit(10)
  @Hint("CustomerIdx")
  @Import("org.example.app.domain.Customer")
  @Query("<TRACE> <HINT 'ReputationIdx'> SELECT DISTINCT * FROM /Customers c WHERE c.reputation > $1 ORDER BY c.reputation DESC LIMIT 5")
  List<Customer> findDistinctCustomersByReputationGreaterThanOrderByReputationDesc(Integer reputation);

}

上述查询方法将生成以下OQL查询:

IMPORT org.example.app.domain.Customer; <TRACE> <HINT 'ReputationIdx'> SELECT DISTINCT * FROM /Customers x WHERE x.reputation > $1 ORDER BY c.reputation DESC LIMIT 5

@Limit(10)注解不会覆盖原始查询中显式定义的LIMIT。同样,@Hint("CustomerIdx")注解也不会覆盖原始查询中显式定义的HINT。最后,@Trace注解是冗余的,没有额外的效果。

ReputationIdx索引可能不是最明智的索引,因为许多客户可能具有相同的声誉值,这降低了索引的有效性。请明智地选择索引和其他优化,因为不当或选择不佳的索引可能会由于维护索引的开销而对性能产生相反的影响。ReputationIdx仅用于示例目的。

10.5. 查询后处理

由于使用了Spring Data Repository抽象,定义数据存储特定查询(例如OQL)的查询方法约定既简单又方便。然而,有时仍然希望检查甚至可能修改从Repository查询方法生成的查询。

自2.0.x起,Spring Data for Pivotal GemFire 包含了o.s.d.gemfire.repository.query.QueryPostProcessor函数式接口。该接口的定义大致如下:

示例 12. QueryPostProcessor
package org.springframework.data.gemfire.repository.query;

import org.springframework.core.Ordered;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.query.QueryMethod;
import ...;

@FunctionalInterface
interface QueryPostProcessor<T extends Repository, QUERY> extends Ordered {

  QUERY postProcess(QueryMethod queryMethod, QUERY query, Object... arguments);

}

还提供了额外的默认方法,允许您组合QueryPostProcessor实例,类似于java.util.function.Function.andThen(:Function)java.util.function.Function.compose(:Function)的工作方式。

此外,QueryPostProcessor接口实现了org.springframework.core.Ordered接口,当在Spring容器中声明和注册多个QueryPostProcessors并用于为一组生成的查询方法查询创建处理管道时,这很有用。

最后,QueryPostProcessor接受与类型参数TQUERY相对应的类型参数。类型T扩展了Spring Data Commons标记接口org.springframework.data.repository.Repository。我们将在本节后面进一步讨论。Spring Data for Pivotal GemFire中所有QUERY类型参数的参数都是java.lang.String类型。

将查询定义为QUERY类型是有用的,因为此QueryPostProcessor接口可能会移植到Spring Data Commons,因此必须处理不同数据存储(如JPA、MongoDB或Redis)的所有形式的查询。

您可以实现此接口,以便在调用应用程序Repository接口方法时,接收包含从该方法生成的查询的回调。

例如,您可能希望记录所有应用程序存储库接口定义中的所有查询。您可以通过使用以下QueryPostProcessor实现来实现这一点:

示例 13. LoggingQueryPostProcessor
package example;

import ...;

class LoggingQueryPostProcessor implements QueryPostProcessor<Repository, String> {

  private Logger logger = Logger.getLogger("someLoggerName");

  @Override
  public String postProcess(QueryMethod queryMethod, String query, Object... arguments) {

      String message = String.format("Executing query [%s] with arguments [%s]", query, Arrays.toString(arguments));

      this.logger.info(message);
  }
}

LoggingQueryPostProcessor被类型化为Spring Data org.springframework.data.repository.Repository标记接口,因此会记录所有应用程序存储库接口查询方法生成的查询。

您可以将此日志记录的范围限制为仅来自某些类型的应用程序存储库接口的查询,例如,一个CustomerRepository,如下例所示:

示例 14. CustomerRepository
interface CustomerRepository extends CrudRepository<Customer, Long> {

  Customer findByAccountNumber(String accountNumber);

  List<Customer> findByLastNameLike(String lastName);

}

然后,您可以将LoggingQueryPostProcessor专门类型化为CustomerRepository,如下所示:

示例 15. CustomerLoggingQueryPostProcessor
class LoggingQueryPostProcessor implements QueryPostProcessor<CustomerRepository, String> { .. }

因此,只有在CustomerRepository接口中定义的查询(例如findByAccountNumber)才会被记录。

您可能希望为由存储库查询方法定义的特定查询创建QueryPostProcessor。例如,假设您希望将从CustomerRepository.findByLastNameLike(:String)查询方法生成的OQL查询限制为仅返回五个结果,并按firstName升序排序Customers。为此,您可以定义一个自定义QueryPostProcessor,如下例所示:

示例 16. OrderedLimitedCustomerByLastNameQueryPostProcessor
class OrderedLimitedCustomerByLastNameQueryPostProcessor implements QueryPostProcessor<CustomerRepository, String> {

  private final int limit;

  public OrderedLimitedCustomerByLastNameQueryPostProcessor(int limit) {
    this.limit = limit;
  }

  @Override
  public String postProcess(QueryMethod queryMethod, String query, Object... arguments) {

    return "findByLastNameLike".equals(queryMethod.getName())
      ? query.trim()
          .replace("SELECT", "SELECT DISTINCT")
          .concat(" ORDER BY firstName ASC")
          .concat(String.format(" LIMIT %d", this.limit))
      : query;
  }
}

虽然上述示例有效,但您可以使用Spring Data for Pivotal GemFire提供的Spring Data Repository约定来实现相同的效果。例如,相同的查询可以定义如下:

示例 17. 使用约定的CustomerRepository
interface CustomerRepository extends CrudRepository<Customer, Long> {

  @Limit(5)
  List<Customer> findDistinctByLastNameLikeOrderByFirstNameDesc(String lastName);

}

但是,如果您无法控制应用程序CustomerRepository接口定义,那么QueryPostProcessor(即OrderedLimitedCustomerByLastNameQueryPostProcessor)就很方便。

如果您希望确保LoggingQueryPostProcessor始终在Spring ApplicationContext中可能已声明和注册的其他应用程序定义的QueryPostProcessors之后执行,您可以通过覆盖o.s.core.Ordered.getOrder()方法来设置order属性,如下例所示:

示例 18. 定义order属性
class LoggingQueryPostProcessor implements QueryPostProcessor<Repository, String> {

  @Override
  int getOrder() {
    return 1;
  }
}

class CustomerQueryPostProcessor implements QueryPostProcessor<CustomerRepository, String> {

  @Override
  int getOrder() {
    return 0;
  }
}

这确保您始终在LoggingQueryPostProcessor记录查询之前看到其他QueryPostProcessors应用后处理的效果。

您可以在Spring ApplicationContext中定义任意数量的QueryPostProcessors,并以任何顺序将它们应用于所有或特定的应用程序Repository接口,并通过使用提供给postProcess(..)方法回调的参数来达到您想要的粒度。

11. 函数执行的注解支持

Spring Data for Pivotal GemFire 包含注解支持,以简化使用 Pivotal GemFire 函数执行

在底层,Pivotal GemFire API提供了类来实现在Pivotal GemFire服务器上部署的Pivotal GemFire函数并进行注册,然后这些函数可以由其他对等成员应用程序或从缓存客户端远程调用。

函数可以并行执行,分布在集群中的多个Pivotal GemFire服务器上,使用map-reduce模式聚合结果并返回给调用者。函数也可以被指定在单个服务器或Region上运行。Pivotal GemFire API支持通过各种预定义范围(在Region上、在成员上(分组)、在服务器上等)远程执行函数。远程函数的实现和执行,与任何RPC协议一样,需要一些样板代码。

Spring Data for Pivotal GemFire秉承Spring的核心价值主张,旨在隐藏远程函数执行的机制,让您专注于核心POJO编程和业务逻辑。为此,Spring Data for Pivotal GemFire引入了注解,以声明式地将POJO类的公共方法注册为Pivotal GemFire函数,以及通过使用注解接口来调用已注册函数(包括远程调用)的功能。

11.1. 实现与执行

需要解决两个独立的关注点:实现和执行。

第一个是函数实现(服务器端),它必须与FunctionContext交互以访问调用参数、ResultsSender以发送结果以及其他执行上下文信息。函数实现通常访问缓存和区域,并使用唯一ID向FunctionService注册。

调用函数的缓存客户端应用程序不依赖于实现。为了调用函数,应用程序实例化一个Execution,提供函数ID、调用参数和函数目标,该目标定义其范围:区域、服务器、服务器组、成员或成员组。如果函数产生结果,调用者使用ResultCollector聚合和获取执行结果。在某些情况下,需要自定义ResultCollector实现,并且可以向Execution注册。

这里的“客户端”和“服务器”是在函数执行的上下文中使用的,这可能与Pivotal GemFire客户端-服务器拓扑中的客户端和服务器具有不同的含义。虽然使用ClientCache实例的应用程序通常在集群中的一个或多个Pivotal GemFire服务器上调用函数,但也可以在对等(P2P)配置中执行函数,其中应用程序是托管对等Cache实例的集群成员。请记住,对等成员缓存应用程序受作为集群对等成员的所有约束。

11.2. 实现函数

使用Pivotal GemFire API,FunctionContext提供了一个运行时调用上下文,其中包括客户端的调用参数和ResultSender实现,用于将结果发送回客户端。此外,如果函数在Region上执行,FunctionContext实际上是RegionFunctionContext的一个实例,它提供了额外的信息,例如函数被调用的目标Region、与Execution关联的任何过滤器(一组特定键)等。如果Region是PARTITION Region,函数应该使用PartitionRegionHelper来提取本地数据集。

通过使用Spring,您可以编写一个简单的POJO,并使用Spring容器将您的一个或多个POJO的公共方法绑定到一个函数。旨在用作函数的POJO方法的签名通常必须符合客户端的执行参数。但是,在区域执行的情况下,也可以提供区域数据(据推测,如果区域是PARTITION区域,则数据保存在本地分区中)。

此外,函数可能需要应用的过滤器(如果有)。这表明客户端和服务器共享调用参数的契约,但方法签名可能包含额外的参数,用于传递由FunctionContext提供的值。一种可能性是客户端和服务器共享一个公共接口,但这并非严格要求。唯一的约束是方法签名包含与函数调用时相同的调用参数序列,这些参数在额外参数解析后必须完全对应。

例如,假设客户端提供一个String和一个int作为调用参数。这些参数作为数组在FunctionContext中提供,如下例所示:

Object[] args = new Object[] { "test", 123 };

Spring容器应该能够绑定到类似于以下任何方法签名(暂时忽略返回类型):

public Object method1(String s1, int i2) { ... }
public Object method2(Map<?, ?> data, String s1, int i2) { ... }
public Object method3(String s1, Map<?, ?> data, int i2) { ... }
public Object method4(String s1, Map<?, ?> data, Set<?> filter, int i2) { ... }
public void method4(String s1, Set<?> filter, int i2, Region<?,?> data) { ... }
public void method5(String s1, ResultSender rs, int i2) { ... }
public void method6(FunctionContest context) { ... }

一般规则是,一旦解析了任何额外参数(即区域数据和过滤器),剩余的参数必须与预期的函数方法参数在顺序和类型上完全一致。方法的返回类型必须为void或可序列化的类型(作为java.io.SerializableDataSerializablePdxSerializable)。后者也是调用参数的要求。

区域数据通常应定义为Map,以方便单元测试,但如有必要,也可以是Region类型。如上例所示,如果您需要控制结果如何返回给客户端,也可以直接传递FunctionContext本身或ResultSender

11.2.1. 用于函数实现的注解

以下示例展示了SDG的函数注解如何用于将POJO方法公开为Pivotal GemFire函数:

@Component
public class ApplicationFunctions {

   @GemfireFunction
   public String function1(String value, @RegionData Map<?, ?> data, int i2) { ... }

   @GemfireFunction(id = "myFunction", batchSize=100, HA=true, optimizedForWrite=true)
   public List<String> function2(String value, @RegionData Map<?, ?> data, int i2, @Filter Set<?> keys) { ... }

   @GemfireFunction(hasResult=true)
   public void functionWithContext(FunctionContext functionContext) { ... }

}

请注意,该类本身必须注册为Spring bean,并且每个Pivotal GemFire函数都用@GemfireFunction注解。在前面的示例中,使用了Spring的@Component注解,但您可以使用Spring支持的任何方法(例如XML配置或在使用Spring Boot时使用Java配置类)注册bean。这使得Spring容器能够创建该类的实例并将其包装在PojoFunctionWrapper中。Spring为每个用@GemfireFunction注解的方法创建一个包装器实例。每个包装器实例共享相同的目标对象实例来调用相应的方法。

POJO Function类是Spring bean这一事实可能带来其他好处。由于它与Pivotal GemFire组件(如缓存和区域)共享ApplicationContext,因此如有必要,可以将这些组件注入到类中。

Spring 创建包装器类并将函数注册到 Pivotal GemFire 的 FunctionService。用于注册每个函数的函数 ID 必须是唯一的。按照约定,它默认为简单(非限定)方法名。可以使用 @GemfireFunction 注解的 id 属性显式定义名称。

@GemfireFunction注解还提供了其他配置属性:HAoptimizedForWrite,它们对应于Pivotal GemFire的Function接口定义的属性。

如果POJO函数方法的返回类型是void,则hasResult属性会自动设置为false。否则,如果方法返回一个值,则hasResult属性设置为true。即使对于void方法返回类型,GemfireFunction注解的hasResult属性也可以设置为true以覆盖此约定,如前面所示的functionWithContext方法。据推测,其意图是您将直接使用ResultSender将结果发送给调用者。

最后,GemfireFunction 注解支持 requiredPermissions 属性,该属性指定执行 Function 所需的权限。默认情况下,所有 Function 都需要 DATA:WRITE 权限。该属性接受一个字符串数组,允许您根据应用程序和/或 Function UC 的需要修改权限。每个资源权限的预期格式如下:<RESOURCE>:<OPERATION>:[Target]:[Key]

RESOURCE 可以是 {data-store-javadoc]/org/apache/geode/security/ResourcePermission.Resource.html[ResourcePermission.Resource] 枚举值之一。OPERATION 可以是 {data-store-javadoc}/org/apache/geode/security/ResourcePermission.Operation.html[ResourcePermission.Operation] 枚举值之一。可选地,Target 可以是 Region 的名称或 {data-store-javadoc}/org/apache/geode/security/ResourcePermission.Target.html[ResourcePermission.Target] 枚举值之一。最后,可选地,如果指定了 Target Region,则 Key 是该 Region 中的有效 Key。

PojoFunctionWrapper 实现了 Pivotal GemFire 的 Function 接口,绑定方法参数,并在其 execute() 方法中调用目标方法。它还使用 ResultSender 将方法的返回值发送回调用者。

11.2.2. 批量处理结果

如果返回类型是数组或 Collection,则必须考虑如何返回结果。默认情况下,PojoFunctionWrapper 一次返回整个数组或 Collection。如果数组或 Collection 中的元素数量非常大,可能会带来性能损失。要将有效负载分成更小、更易于管理的块,您可以设置 batchSize 属性,如前面所示的 function2 中所示。

如果您需要对 ResultSender 进行更多控制,特别是当方法本身会使用过多内存来创建 Collection 时,您可以传入 ResultSender 或通过 FunctionContext 访问它,并在方法中直接使用它将结果发送回调用者。

11.2.3. 启用注解处理

根据 Spring 标准,您必须明确激活 @GemfireFunction 注解的注解处理。以下示例使用 XML 激活注解处理

<gfe:annotation-driven/>

以下示例通过注解 Java 配置类来激活注解处理

@Configuration
@EnableGemfireFunctions
class ApplicationConfiguration { ... }

11.3. 执行函数

调用远程 Function 的进程需要提供 Function 的 ID、调用参数、执行目标(onRegiononServersonServeronMemberonMembers)以及(可选)过滤集。通过使用 Spring Data for Pivotal GemFire,您只需定义一个由注解支持的接口。Spring 为该接口创建一个动态代理,该代理使用 FunctionService 来创建 Execution、调用 Execution,并在必要时将结果强制转换为定义的返回类型。此技术类似于 Spring Data for Pivotal GemFire 的 Repository 扩展的工作方式。因此,一些配置和概念应该很熟悉。

通常,单个接口定义映射到多个 Function 执行,每个执行对应于接口中定义的每个方法。

11.3.1. 用于函数执行的注解

为了支持客户端 Function 执行,提供了以下 SDG Function 注解:@OnRegion@OnServer@OnServers@OnMember@OnMembers。这些注解对应于 Pivotal GemFire 的 FunctionService 类提供的 Execution 实现。

每个注解都公开了适当的属性。这些注解还提供了一个可选的 resultCollector 属性,其值是实现 ResultCollector 接口的 Spring bean 的名称,用于执行。

代理接口将所有声明的方法绑定到相同的执行配置。虽然预计单方法接口很常见,但接口中的所有方法都由相同的代理实例支持,因此都共享相同的配置。

以下清单显示了一些示例

@OnRegion(region="SomeRegion", resultCollector="myCollector")
public interface FunctionExecution {

    @FunctionId("function1")
    String doIt(String s1, int i2);

    String getString(Object arg1, @Filter Set<Object> keys);

}

默认情况下,Function ID 是简单的(非限定的)方法名。@FunctionId 注解可用于将此调用绑定到不同的 Function ID。

11.3.2. 启用注解处理

客户端使用 Spring 的 classpath 组件扫描功能来发现带注解的接口。要在 XML 中启用 Function 执行注解处理,请在 XML 配置中插入以下元素

<gfe-data:function-executions base-package="org.example.myapp.gemfire.functions"/>

function-executions 元素在 gfe-data XML 命名空间中提供。base-package 属性是必需的,以避免扫描整个 classpath。可以提供额外的过滤器,如 Spring 参考文档中所述。

或者,您可以按如下方式注解您的 Java 配置类

@EnableGemfireFunctionExecutions(basePackages = "org.example.myapp.gemfire.functions")

11.4. 编程式函数执行

使用上一节中定义的 Function 执行注解接口,只需将您的接口自动装配到将调用 Function 的应用程序 bean 中

@Component
public class MyApplication {

    @Autowired
    FunctionExecution functionExecution;

    public void doSomething() {
         functionExecution.doIt("hello", 123);
    }
}

或者,您可以直接使用 Function 执行模板。在以下示例中,GemfireOnRegionFunctionTemplate 创建了一个 onRegion Function Execution

示例 19. 使用 GemfireOnRegionFunctionTemplate
Set<?, ?> myFilter = getFilter();
Region<?, ?> myRegion = getRegion();
GemfireOnRegionOperations template = new GemfireOnRegionFunctionTemplate(myRegion);
String result = template.executeAndExtract("someFunction", myFilter, "hello", "world", 1234);

在内部,Function Executions 始终返回一个 ListexecuteAndExtract 假设一个包含结果的单例 List 并尝试将该值强制转换为请求的类型。还有一个 execute 方法返回原样的 List。第一个参数是 Function ID。过滤器参数是可选的。其余参数是可变参数 List

11.5. 使用 PDX 执行函数

当使用 Spring Data for Pivotal GemFire 的 Function 注解支持结合 Pivotal GemFire 的 PDX 序列化时,需要牢记一些逻辑事项。

如本节前面所解释的,并以示例说明,您通常应使用带有 Spring Data for Pivotal GemFire Function 注解的 POJO 类来定义 Pivotal GemFire Functions,如下所示

public class OrderFunctions {

  @GemfireFunction(...)
  Order process(@RegionData data, Order order, OrderSource orderSourceEnum, Integer count) { ... }

}
Integer 类型的 count 参数是任意的,Order 类和 OrderSource 枚举的分离也是任意的,它们在逻辑上可能应该合并。但是,参数以这种方式设置是为了演示在 PDX 上下文中 Function 执行的问题。

您的 Order 类和 OrderSource 枚举可能定义如下

public class Order ... {

  private Long orderNumber;
  private LocalDateTime orderDateTime;
  private Customer customer;
  private List<Item> items

  ...
}


public enum OrderSource {
  ONLINE,
  PHONE,
  POINT_OF_SALE
  ...
}

当然,您可以定义一个 Function Execution 接口来调用“process” Pivotal GemFire 服务器 Function,如下所示

@OnServer
public interface OrderProcessingFunctions {
  Order process(Order order, OrderSource orderSourceEnum, Integer count);
}

显然,这个 process(..) Order Function 是从客户端使用 ClientCache 实例(即 <gfe:client-cache/>)调用的。这意味着 Function 参数也必须是可序列化的。在集群中调用对等成员 Function(例如 @OnMember(s))时也是如此。任何形式的 distribution 都要求客户端和服务器(或对等体)之间传输的数据是可序列化的。

现在,如果您已将 Pivotal GemFire 配置为使用 PDX 进行序列化(例如,而不是 Java 序列化),您还可以在 Pivotal GemFire 服务器的配置中将 pdx-read-serialized 属性设置为 true,如下所示

<gfe:cache ... pdx-read-serialized="true"/>

或者,您可以为 Pivotal GemFire 缓存客户端应用程序将 pdx-read-serialized 属性设置为 true,如下所示

<gfe:client-cache ... pdx-read-serialized="true"/>

这样做会导致从缓存(即 Regions)读取的所有值以及在客户端和服务器(或对等体)之间传递的信息保持序列化形式,包括但不限于 Function 参数。

Pivotal GemFire 仅序列化您通过使用 Pivotal GemFire 的 ReflectionBasedAutoSerializer 明确配置(注册)的应用程序域对象类型,或者通过使用“自定义” Pivotal GemFire PdxSerializer 明确配置(并推荐)的应用程序域对象类型。如果您使用 Spring Data for Pivotal GemFire 的 Repository 扩展,您甚至可能希望考虑使用 Spring Data for Pivotal GemFire 的 MappingPdxSerializer,它使用实体映射元数据来确定从应用程序域对象序列化到 PDX 实例的数据。

然而,不那么明显的是,Pivotal GemFire 自动处理 Java Enum 类型,无论它们是否明确配置(即,使用正则表达式模式和 classes 参数注册到 ReflectionBasedAutoSerializer,或者由“自定义” Pivotal GemFire PdxSerializer 处理),尽管 Java 枚举实现了 java.io.Serializable

因此,当您在注册 Pivotal GemFire Functions(包括 Spring Data for Pivotal GemFire Function 注解的 POJO 类)的 Pivotal GemFire 服务器上将 pdx-read-serialized 设置为 true 时,您在调用 Function Execution 时可能会遇到令人惊讶的行为。

您在调用 Function 时可能会传递以下参数

orderProcessingFunctions.process(new Order(123, customer, LocalDateTime.now(), items), OrderSource.ONLINE, 400);

然而,服务器上的 Pivotal GemFire Function 得到以下结果

process(regionData, order:PdxInstance, :PdxInstanceEnum, 400);

OrderOrderSource 已作为 PDX 实例传递给 Function。同样,所有这些都发生是因为 pdx-read-serialized 设置为 true,这在 Pivotal GemFire 服务器与多个不同客户端(例如,Java 客户端和原生客户端(如 C/C++、C# 等)的组合)交互的情况下可能是必要的。

这与 Spring Data for Pivotal GemFire 的强类型 Function 注解 POJO 类方法签名背道而驰,在该签名中,您通常会合理地期望应用程序域对象类型,而不是 PDX 序列化实例。

因此,Spring Data for Pivotal GemFire 包含了增强的 Function 支持,以自动将 PDX 类型的方法参数转换为 Function 方法签名(参数类型)定义的所需应用程序域对象类型。

然而,这还要求您在注册和使用 Spring Data for Pivotal GemFire Function 注解 POJO 的 Pivotal GemFire 服务器上明确注册一个 Pivotal GemFire PdxSerializer,如下例所示

<bean id="customPdxSerializer" class="x.y.z.gemfire.serialization.pdx.MyCustomPdxSerializer"/>

<gfe:cache ... pdx-serializer-ref="customPdxSerializeer" pdx-read-serialized="true"/>

或者,为了方便起见,您可以使用 Pivotal GemFire 的 ReflectionBasedAutoSerializer。当然,我们建议在可能的情况下,您使用自定义 PdxSerializer 来保持对序列化策略的更精细控制。

最后,Spring Data for Pivotal GemFire 会小心地不转换您的 Function 参数,如果您将 Function 参数视为泛型或 Pivotal GemFire 的 PDX 类型之一,如下所示

@GemfireFunction
public Object genericFunction(String value, Object domainObject, PdxInstanceEnum enum) {
 ...
}

Spring Data for Pivotal GemFire 仅当相应的应用程序域类型在 classpath 中且 Function 注解的 POJO 方法期望它时,才会将 PDX 类型数据转换为相应的应用程序域类型。

有关自定义、组合的应用程序特定 Pivotal GemFire PdxSerializers 以及基于方法签名的适当 POJO Function 参数类型处理的良好示例,请参见 Spring Data for Pivotal GemFire 的 ClientCacheFunctionExecutionWithPdxIntegrationTest 类。

12. Apache Lucene 集成

Pivotal GemFireApache Lucene 集成,让您可以使用 Lucene 查询索引和搜索存储在 Pivotal GemFire 中的数据。基于搜索的查询还包括分页查询结果的能力。

此外,Spring Data for Pivotal GemFire 添加了对基于 Spring Data Commons 投影基础设施的查询投影的支持。此功能允许根据应用程序的需要将查询结果投影到一流的应用程序域类型中。

在运行任何基于 Lucene 搜索的查询之前,必须创建 Lucene Index。可以在 Spring (Data for Pivotal GemFire) XML 配置中创建 LuceneIndex,如下所示

<gfe:lucene-index id="IndexOne" fields="fieldOne, fieldTwo" region-path="/Example"/>

此外,Apache Lucene 允许为每个字段指定 分析器,并且可以按以下示例所示进行配置

<gfe:lucene-index id="IndexTwo" lucene-service-ref="luceneService" region-path="/AnotherExample">
    <gfe:field-analyzers>
        <map>
            <entry key="fieldOne">
                <bean class="example.AnalyzerOne"/>
             </entry>
            <entry key="fieldTwo">
                <bean class="example.AnalyzerTwo"/>
             </entry>
        </map>
    </gfe:field-analyzers>
</gfe:lucene-index>

Map 可以指定为顶级 bean 定义,并通过嵌套的 <gfe:field-analyzers> 元素中的 ref 属性引用,如下所示:<gfe-field-analyzers ref="refToTopLevelMapBeanDefinition"/>

Spring Data for Pivotal GemFire 的 LuceneIndexFactoryBean API 和 SDG 的 XML 命名空间还允许在创建 LuceneIndex 时指定 org.apache.geode.cache.lucene.LuceneSerializerLuceneSerializer 允许您配置对象在索引时如何转换为 Lucene 文档。

以下示例演示了如何向 LuceneIndex 添加 LuceneSerializer

<bean id="MyLuceneSerializer" class="example.CustomLuceneSerializer"/>

<gfe:lucene-index id="IndexThree" lucene-service-ref="luceneService" region-path="/YetAnotherExample">
    <gfe:lucene-serializer ref="MyLuceneSerializer">
</gfe:lucene-index>

您还可以将 LuceneSerializer 指定为匿名嵌套 bean 定义,如下所示

<gfe:lucene-index id="IndexThree" lucene-service-ref="luceneService" region-path="/YetAnotherExample">
    <gfe:lucene-serializer>
        <bean class="example.CustomLuceneSerializer"/>
    </gfe:lucene-serializer>
</gfe:lucene-index>

或者,您可以在 Spring Java 配置中,在 @Configuration 类中声明或定义 LuceneIndex,如下例所示

@Bean(name = "Books")
@DependsOn("bookTitleIndex")
PartitionedRegionFactoryBean<Long, Book> booksRegion(GemFireCache gemfireCache) {

    PartitionedRegionFactoryBean<Long, Book> peopleRegion =
        new PartitionedRegionFactoryBean<>();

    peopleRegion.setCache(gemfireCache);
    peopleRegion.setClose(false);
    peopleRegion.setPersistent(false);

    return peopleRegion;
}

@Bean
LuceneIndexFactoryBean bookTitleIndex(GemFireCache gemFireCache,
        LuceneSerializer luceneSerializer) {

    LuceneIndexFactoryBean luceneIndex = new LuceneIndexFactoryBean();

    luceneIndex.setCache(gemFireCache);
    luceneIndex.setFields("title");
    luceneIndex.setLuceneSerializer(luceneSerializer);
    luceneIndex.setRegionPath("/Books");

    return luceneIndex;
}

@Bean
CustomLuceneSerializer myLuceneSerialier() {
    return new CustomeLuceneSerializer();
}

Pivotal GemFire 的 Apache Lucene 集成和支持有一些限制。

首先,LuceneIndex 只能在 Pivotal GemFire PARTITION Region 上创建。

其次,所有 LuceneIndexes 必须在 LuceneIndex 所适用的 Region 之前创建。

为了帮助确保 Spring 容器中定义的所有声明的 LuceneIndexes 在其适用的 Region 之前创建,SDG 包含 org.springframework.data.gemfire.config.support.LuceneIndexRegionBeanFactoryPostProcessor。您可以使用 <bean class="org.springframework.data.gemfire.config.support.LuceneIndexRegionBeanFactoryPostProcessor"/> 在 XML 配置中注册此 Spring BeanFactoryPostProcessoro.s.d.g.config.support.LuceneIndexRegionBeanFactoryPostProcessor 只能在使用 SDG XML 配置时使用。有关 Spring 的 BeanFactoryPostProcessors 的更多详细信息可以在此处找到。

Pivotal GemFire 的这些限制在未来的版本中可能不会适用,这也是 SDG LuceneIndexFactoryBean API 直接引用 Region 而不仅仅是 Region 路径的原因。

当您希望在应用程序生命周期的后期以及需求要求时,在具有数据的现有 Region 上定义 LuceneIndex 时,这更为理想。在可能的情况下,SDG 努力遵守强类型对象。但是,目前,您必须使用 regionPath 属性来指定 LuceneIndex 应用的 Region。

此外,在前面的示例中,请注意 Books Region bean 定义上存在 Spring 的 @DependsOn 注解。这会创建从 Books Region bean 到 bookTitleIndex LuceneIndex bean 定义的依赖关系,确保在 Region 之前创建 LuceneIndex

现在我们有了 LuceneIndex,我们可以执行基于 Lucene 的数据访问操作,例如查询。

12.1. Lucene 模板数据访问器

Spring Data for Pivotal GemFire 提供了两个主要的 Lucene 数据访问操作模板,具体取决于您的应用程序愿意处理的级别。

LuceneOperations 接口使用 Pivotal GemFire Lucene 类型定义查询操作,这些类型在以下接口定义中定义

public interface LuceneOperations {

    <K, V> List<LuceneResultStruct<K, V>> query(String query, String defaultField [, int resultLimit]
        , String... projectionFields);

    <K, V> PageableLuceneQueryResults<K, V> query(String query, String defaultField,
        int resultLimit, int pageSize, String... projectionFields);

    <K, V> List<LuceneResultStruct<K, V>> query(LuceneQueryProvider queryProvider [, int resultLimit]
        , String... projectionFields);

    <K, V> PageableLuceneQueryResults<K, V> query(LuceneQueryProvider queryProvider,
        int resultLimit, int pageSize, String... projectionFields);

    <K> Collection<K> queryForKeys(String query, String defaultField [, int resultLimit]);

    <K> Collection<K> queryForKeys(LuceneQueryProvider queryProvider [, int resultLimit]);

    <V> Collection<V> queryForValues(String query, String defaultField [, int resultLimit]);

    <V> Collection<V> queryForValues(LuceneQueryProvider queryProvider [, int resultLimit]);
}
[, int resultLimit] 表示 resultLimit 参数是可选的。

LuceneOperations 接口中的操作与 Pivotal GemFire 的 LuceneQuery 接口提供的操作相匹配。但是,SDG 的附加价值在于将专有的 Pivotal GemFire 或 Apache Lucene Exceptions 转换为 Spring 的高度一致和富有表现力的 DAO 异常层次结构,特别是当许多现代数据访问操作涉及多个存储或存储库时。

此外,当底层 Pivotal GemFire 或 Apache Lucene API 引入破坏接口的更改时,SDG 的 LuceneOperations 接口可以保护您的应用程序免受这些更改的影响。

然而,如果仅使用 Pivotal GemFire 和 Apache Lucene 数据类型(例如 Pivotal GemFire 的 LuceneResultStruct)提供 Lucene 数据访问对象(DAO)会很遗憾。因此,SDG 为您提供了 ProjectingLuceneOperations 接口来解决这些重要的应用程序问题。以下清单显示了 ProjectingLuceneOperations 接口定义

public interface ProjectingLuceneOperations {

    <T> List<T> query(String query, String defaultField [, int resultLimit], Class<T> projectionType);

    <T> Page<T> query(String query, String defaultField, int resultLimit, int pageSize, Class<T> projectionType);

    <T> List<T> query(LuceneQueryProvider queryProvider [, int resultLimit], Class<T> projectionType);

    <T> Page<T> query(LuceneQueryProvider queryProvider, int resultLimit, int pageSize, Class<T> projectionType);
}

ProjectingLuceneOperations 接口主要使用应用程序域对象类型,让您可以使用应用程序数据。query 方法变体接受一个投影类型,模板使用 Spring Data Commons Projection 基础设施将查询结果应用于给定投影类型的实例。

此外,模板将分页的 Lucene 查询结果包装在 Spring Data Commons Page 抽象的实例中。相同的投影逻辑仍然可以应用于页面中的结果,并且在访问集合中的每个页面时进行惰性投影。

例如,假设您有一个表示 Person 的类,如下所示

class Person {

    Gender gender;

    LocalDate birthDate;

    String firstName;
    String lastName;

    ...

    String getName() {
        return String.format("%1$s %2$s", getFirstName(), getLastName());
    }
}

此外,根据您的应用程序视图,您可能有一个单独的接口来表示人员为 Customers,如下所示

interface Customer {

    String getName()

}

如果我定义以下 LuceneIndex……

@Bean
LuceneIndexFactoryBean personLastNameIndex(GemFireCache gemfireCache) {

    LuceneIndexFactoryBean personLastNameIndex =
        new LuceneIndexFactoryBean();

    personLastNameIndex.setCache(gemfireCache);
    personLastNameIndex.setFields("lastName");
    personLastNameIndex.setRegionPath("/People");

    return personLastNameIndex;
}

那么您可以查询人员作为 Person 对象,如下所示

List<Person> people = luceneTemplate.query("lastName: D*", "lastName", Person.class);

或者,您可以查询 Customer 类型的 Page,如下所示

Page<Customer> customers = luceneTemplate.query("lastName: D*", "lastName", 100, 20, Customer.class);

然后可以使用 Page 来获取结果的各个页面,如下所示

List<Customer> firstPage = customers.getContent();

方便的是,Spring Data Commons Page 接口还实现了 java.lang.Iterable<T>,使其易于遍历内容。

Spring Data Commons Projection 基础设施的唯一限制是投影类型必须是接口。但是,可以扩展提供的 SDC Projection 基础设施并提供一个使用 CGLIB 生成代理类作为投影实体的自定义 ProjectionFactory

您可以使用 setProjectionFactory(:ProjectionFactory) 在 Lucene 模板上设置自定义 ProjectionFactory

12.2. 注解配置支持

最后,Spring Data for Pivotal GemFire 为 LuceneIndexes 提供了注解配置支持。

最终,SDG Lucene 支持将进入 Pivotal GemFire 的 Repository 基础设施扩展,以便 Lucene 查询可以像 OQL 支持今天的工作方式一样,在应用程序 Repository 接口上表示为方法。

但是,与此同时,如果您想方便地表达 LuceneIndexes,您可以直接在应用程序域对象上进行,如下例所示

@PartitionRegion("People")
class Person {

    Gender gender;

    @Index
    LocalDate birthDate;

    String firstName;

    @LuceneIndex;
    String lastName;

    ...
}

要启用此功能,您必须使用 SDG 的注解配置支持,特别是 @EnableEntityDefineRegions@EnableIndexing 注解,如下所示

@PeerCacheApplication
@EnableEntityDefinedRegions
@EnableIndexing
class ApplicationConfiguration {

  ...
}
LuceneIndexes 只能在 Pivotal GemFire 服务器上创建,因为 LuceneIndexes 仅适用于 PARTITION Regions。

鉴于我们前面定义的 Person 类,SDG 注解配置支持会找到 Person 实体类定义,并确定人员存储在名为“People”的 PARTITION Region 中,并且 PersonbirthDate 上有一个 OQL Index,在 lastName 上有一个 LuceneIndex

13. 在 Pivotal GemFire 中引导 Spring ApplicationContext

通常,基于 Spring 的应用程序使用 Spring Data for Pivotal GemFire 的功能来引导 Pivotal GemFire。通过指定使用 Spring Data for Pivotal GemFire XML 命名空间的 <gfe:cache/> 元素,将在与应用程序相同的 JVM 进程中创建并初始化一个具有默认设置的单个嵌入式 Pivotal GemFire 对等 Cache 实例。

然而,有时有必要(也许是您的 IT 组织强制要求)Pivotal GemFire 由提供的 Pivotal GemFire 工具套件完全管理和操作,可能使用 Gfsh。通过使用 Gfsh,Pivotal GemFire 引导您的 Spring ApplicationContext,而不是相反。Pivotal GemFire 负责引导并托管您的应用程序,而不是应用程序服务器或使用 Spring Boot 的 Java main 类。

Pivotal GemFire 不是应用程序服务器。此外,在使用此方法时,Pivotal GemFire 缓存配置方面存在限制。

13.1. 使用 Pivotal GemFire 引导通过 Gfsh 启动的 Spring Context

为了在通过 Gfsh 启动 Pivotal GemFire 服务器时引导 Pivotal GemFire 中的 Spring ApplicationContext,您必须使用 Pivotal GemFire 的 初始化器 功能。初始化器块可以声明一个应用程序回调,该回调在缓存由 Pivotal GemFire 初始化后启动。

初始化器在 初始化器 元素中声明,使用 Pivotal GemFire 原生 cache.xml 的最小代码片段。为了引导 Spring ApplicationContext,需要一个 cache.xml 文件,这与引导配置了组件扫描的 Spring ApplicationContext(例如 <context:component-scan base-packages="…​"/>)所需的最小 Spring XML 配置代码片段非常相似。

幸运的是,框架已经方便地提供了这样的初始化器:SpringContextBootstrappingInitializer

以下示例展示了此类的典型但最小化的配置,位于 Pivotal GemFire 的 cache.xml 文件中

<?xml version="1.0" encoding="UTF-8"?>
<cache xmlns="http://geode.apache.org/schema/cache"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://geode.apache.org/schema/cache https://geode.apache.org/schema/cache/cache-1.0.xsd"
       version="1.0">

  <initializer>
    <class-name>org.springframework.data.gemfire.support.SpringContextBootstrappingInitializer</class-name>
    <parameter name="contextConfigLocations">
      <string>classpath:application-context.xml</string>
    </parameter>
  </initializer>

</cache>

SpringContextBootstrappingInitializer 类遵循与 Spring 的 ContextLoaderListener 类类似的约定,ContextLoaderListener 类用于在 Web 应用程序中引导 Spring ApplicationContext,其中 ApplicationContext 配置文件由 contextConfigLocations Servlet 上下文参数指定。

此外,SpringContextBootstrappingInitializer 类还可以与 basePackages 参数一起使用,以指定包含适当注解的应用程序组件的逗号分隔的基本包列表。Spring 容器搜索这些组件以在 classpath 中查找和创建 Spring bean 和其他应用程序组件,如下例所示

<?xml version="1.0" encoding="UTF-8"?>
<cache xmlns="http://geode.apache.org/schema/cache"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://geode.apache.org/schema/cache https://geode.apache.org/schema/cache/cache-1.0.xsd"
       version="1.0">

  <initializer>
    <class-name>org.springframework.data.gemfire.support.SpringContextBootstrappingInitializer</class-name>
    <parameter name="basePackages">
      <string>org.mycompany.myapp.services,org.mycompany.myapp.dao,...</string>
    </parameter>
  </initializer>

</cache>

然后,当在 Gfsh 中启动 Pivotal GemFire 服务器时,将正确配置和构造的 CLASSPATHcache.xml 文件(前面所示)指定为命令行选项,命令行将如下所示

gfsh>start server --name=ExampleServer --log-level=config ...
    --classpath="/path/to/application/classes.jar:/path/to/spring-data-geode-<major>.<minor>.<maint>.RELEASE.jar"
    --cache-xml-file="/path/to/geode/cache.xml"

application-context.xml 可以是任何有效的 Spring 配置元数据,包括所有 SDG XML 命名空间元素。此方法的唯一限制是无法使用 SDG XML 命名空间配置 Pivotal GemFire 缓存。换句话说,不能指定任何 <gfe:cache/> 元素属性(例如 cache-xml-locationproperties-refcritical-heap-percentagepdx-serializer-reflock-lease 等)。如果使用,这些属性将被忽略。

原因在于 Pivotal GemFire 本身在初始化器被调用之前就已经创建并初始化了缓存。因此,缓存已经存在,并且由于它是一个“单例”,它不能被重新初始化或对其任何配置进行增强。

13.2. 惰性连接 Pivotal GemFire 组件

Spring Data for Pivotal GemFire 已经支持通过使用 SDG 的 WiringDeclarableSupport 类在 cache.xml 中声明和创建的 Pivotal GemFire 组件(例如 CacheListenersCacheLoadersCacheWriters 等)的自动装配,如使用自动装配和注解进行配置中所述。但是,这仅在 Spring 进行引导时(即当 Spring 引导 Pivotal GemFire 时)才有效。

当您的 Spring ApplicationContext 由 Pivotal GemFire 引导时,这些 Pivotal GemFire 应用程序组件将不被注意,因为 Spring ApplicationContext 尚不存在。直到 Pivotal GemFire 调用初始化器块,Spring ApplicationContext 才会被创建,而这仅在所有其他 Pivotal GemFire 组件(缓存、Region 等)都已创建和初始化之后才发生。

为了解决这个问题,引入了新的 LazyWiringDeclarableSupport 类。这个新类感知 Spring ApplicationContext。这个抽象基类的目的是让任何实现类注册自己,以便在初始化器被调用后,最终由 Pivotal GemFire 创建的 Spring 容器进行配置。本质上,这让您的 Pivotal GemFire 应用程序组件有机会通过 Spring 容器中定义的 Spring bean 进行配置和自动装配。

为了让您的 Pivotal GemFire 应用程序组件由 Spring 容器自动装配,您应该创建一个扩展 LazyWiringDeclarableSupport 的应用程序类,并注解任何需要作为 Spring bean 依赖项提供的类成员,类似于以下示例

public class UserDataSourceCacheLoader extends LazyWiringDeclarableSupport
    implements CacheLoader<String, User> {

  @Autowired
  private DataSource userDataSource;

  ...
}

如上文 CacheLoader 示例所示,您可能需要(尽管很少)在 Pivotal GemFire cache.xml 中同时定义 Region 和 CacheListener 组件。CacheLoader 可能需要访问应用程序存储库(或 Spring ApplicationContext 中定义的 JDBC DataSource)才能在启动时将 Users 加载到 Pivotal GemFire REPLICATE Region 中。

注意

以这种方式混合 Pivotal GemFire 和 Spring 容器的不同生命周期时要小心。并非所有用例和场景都受支持。Pivotal GemFire cache.xml 配置将类似于以下内容(来自 SDG 的测试套件)

<?xml version="1.0" encoding="UTF-8"?>
<cache xmlns="http://geode.apache.org/schema/cache"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://geode.apache.org/schema/cache https://geode.apache.org/schema/cache/cache-1.0.xsd"
       version="1.0">

  <region name="Users" refid="REPLICATE">
    <region-attributes initial-capacity="101" load-factor="0.85">
      <key-constraint>java.lang.String</key-constraint>
      <value-constraint>org.springframework.data.gemfire.repository.sample.User</value-constraint>
      <cache-loader>
        <class-name>
          org.springframework.data.gemfire.support.SpringContextBootstrappingInitializerIntegrationTest$UserDataStoreCacheLoader
        </class-name>
      </cache-loader>
    </region-attributes>
  </region>

  <initializer>
    <class-name>org.springframework.data.gemfire.support.SpringContextBootstrappingInitializer</class-name>
    <parameter name="basePackages">
      <string>org.springframework.data.gemfire.support.sample</string>
    </parameter>
  </initializer>

</cache>

14. 示例应用程序

示例应用程序现在在 Spring Pivotal GemFire Examples 存储库中维护。

Spring Data for Pivotal GemFire 项目还包括一个示例应用程序。该示例应用程序名为“Hello World”,演示了如何在 Spring 应用程序中配置和使用 Pivotal GemFire。在运行时,该示例提供了一个 Shell,让您可以对数据网格运行各种命令。它为不熟悉基本组件或 Spring 和 Pivotal GemFire 概念的开发人员提供了一个极好的起点。

该示例与分发包捆绑在一起,并且是基于 Maven 的。您可以将其导入任何支持 Maven 的 IDE(例如 Spring Tool Suite)或从命令行运行它们。

14.1. Hello World

“Hello World”示例应用程序演示了 Spring Data for Pivotal GemFire 项目的核心功能。它引导 Pivotal GemFire,配置它,对缓存执行任意命令,并在应用程序退出时将其关闭。可以同时启动多个应用程序实例并协同工作,无需任何用户干预即可共享数据。

在 Linux 下运行
如果在启动 Pivotal GemFire 或示例时遇到网络问题,请尝试向命令行添加以下系统属性 java.net.preferIPv4Stack=true(例如,-Djava.net.preferIPv4Stack=true)。有关替代(全局)修复(尤其是在 Ubuntu 上),请参阅 SGF-28

14.1.1. 启动和停止示例

“Hello World”示例应用程序设计为独立的 Java 应用程序。它具有一个 main 类,可以从您的 IDE(在 Eclipse 或 STS 中,通过 Run As/Java Application)或通过 Maven 命令行使用 mvn exec:java 启动。如果 classpath 设置正确,您还可以直接对生成的 artifact 使用 java

要停止示例,请在命令行键入 exit 或按 Ctrl+C 以停止 JVM 并关闭 Spring 容器。

14.1.2. 使用示例

一旦启动,示例将创建一个共享数据网格,并允许您对其发布命令。输出应类似于以下内容

INFO: Created {data-store-name} Cache [Spring {data-store-name} World] v. X.Y.Z
INFO: Created new cache region [myWorld]
INFO: Member xxxxxx:50694/51611 connecting to region [myWorld]
Hello World!
Want to interact with the world ? ...
Supported commands are:

get <key> - retrieves an entry (by key) from the grid
put <key> <value> - puts a new entry into the grid
remove <key> - removes an entry (by key) from the grid
...

例如,要向网格添加新项,可以使用以下命令

-> Bold Section qName:emphasis level:5, chunks:[put 1 unu] attrs:[role:bold]
INFO: Added [1=unu] to the cache
null
-> Bold Section qName:emphasis level:5, chunks:[put 1 one] attrs:[role:bold]
INFO: Updated [1] from [unu] to [one]
unu
-> Bold Section qName:emphasis level:5, chunks:[size] attrs:[role:bold]
1
-> Bold Section qName:emphasis level:5, chunks:[put 2 two] attrs:[role:bold]
INFO: Added [2=two] to the cache
null
-> Bold Section qName:emphasis level:5, chunks:[size] attrs:[role:bold]
2

可以同时运行多个实例。一旦启动,新的 VM 会自动看到现有区域及其信息,如下例所示

INFO: Connected to Distributed System ['Spring {data-store-name} World'=xxxx:56218/49320@yyyyy]
Hello World!
...

-> Bold Section qName:emphasis level:5, chunks:[size] attrs:[role:bold]
2
-> Bold Section qName:emphasis level:5, chunks:[map] attrs:[role:bold]
[2=two] [1=one]
-> Bold Section qName:emphasis level:5, chunks:[query length = 3] attrs:[role:bold]
[one, two]

我们鼓励您尝试该示例,启动(和停止)任意数量的实例,并在一个实例中运行各种命令,并观察其他实例如何响应。为了保留数据,至少一个实例需要始终保持活动状态。如果所有实例都关闭,则网格数据将完全销毁。

14.1.3. Hello World 示例说明

“Hello World”示例使用 Spring XML 和注解进行配置。初始引导配置是 app-context.xml,它包含在 cache-context.xml 文件中定义的缓存配置,并对 Spring 组件执行 classpath 组件扫描

缓存配置定义了 Pivotal GemFire 缓存、一个区域以及出于说明目的,一个充当日志记录器的 CacheListener

主要的 bean 是 HelloWorldCommandProcessor,它们依赖于 GemfireTemplate 与分布式结构交互。这两个类都使用注解来定义它们的依赖项和生命周期回调。

资源

除了本文档外,还有许多其他资源可以帮助您学习如何将 {data-store-product-name} 与 Spring Framework 一起使用。这些额外的第三方资源在本节中列出。

附录

附录 A: 命名空间参考

<repositories /> 元素

<repositories /> 元素触发 Spring Data 存储库基础设施的设置。最重要的属性是 base-package,它定义了要扫描 Spring Data 存储库接口的包。请参阅“[repositories.create-instances.spring]”。下表描述了 <repositories /> 元素的属性

表 6. 属性
名称 描述

base-package

定义在自动检测模式下扫描扩展 *Repository(实际接口由特定的 Spring Data 模块确定)的仓库接口的包。配置包下的所有包也都会被扫描。允许使用通配符。

repository-impl-postfix

定义自动检测自定义仓库实现的后缀。名称以配置的后缀结尾的类被视为候选。默认为 Impl

query-lookup-strategy

确定用于创建查找查询的策略。有关详细信息,请参阅“[repositories.query-methods.query-lookup-strategies]”。默认为 create-if-not-found

named-queries-location

定义搜索包含外部定义查询的 Properties 文件的位置。

consider-nested-repositories

是否应考虑嵌套仓库接口定义。默认为 false

附录 B: Populators 命名空间参考

<populator /> 元素

<populator /> 元素允许通过 Spring Data 存储库基础设施填充数据存储。[1]

表 7. 属性
名称 描述

locations

从何处查找文件以读取要填充仓库的对象。

附录 C: 存储库查询关键字

支持的查询关键字

下表列出了 Spring Data 存储库查询派生机制通常支持的关键字。但是,请查阅特定存储的文档,以获取受支持关键字的确切列表,因为此处列出的一些关键字可能不受特定存储的支持。

表 8. 查询关键字
逻辑关键字 关键字表达式

AND

And

OR

Or

AFTER

After, IsAfter

BEFORE

Before, IsBefore

CONTAINING

Containing, IsContaining, Contains

BETWEEN

Between, IsBetween

ENDING_WITH

EndingWith, IsEndingWith, EndsWith

EXISTS

Exists

FALSE

False, IsFalse

GREATER_THAN

GreaterThan, IsGreaterThan

GREATER_THAN_EQUALS

GreaterThanEqual, IsGreaterThanEqual

IN

In, IsIn

IS

Is, Equals, (或没有关键字)

IS_EMPTY

IsEmpty, Empty

IS_NOT_EMPTY

IsNotEmpty, NotEmpty

IS_NOT_NULL

NotNull, IsNotNull

IS_NULL

Null, IsNull

LESS_THAN

LessThan, IsLessThan

LESS_THAN_EQUAL

LessThanEqual, IsLessThanEqual

LIKE

Like, IsLike

NEAR

Near, IsNear

NOT

Not, IsNot

NOT_IN

NotIn, IsNotIn

NOT_LIKE

NotLike, IsNotLike

REGEX

Regex, MatchesRegex, Matches

STARTING_WITH

StartingWith, IsStartingWith, StartsWith

TRUE

True, IsTrue

WITHIN

Within, IsWithin

附录 D: 存储库查询返回类型

支持的查询返回类型

下表列出了 Spring Data 仓库通常支持的返回类型。但是,请查阅特定于存储的文档以获取支持返回类型的确切列表,因为此处列出的一些类型可能在特定存储中不受支持。

地理空间类型(例如 GeoResultGeoResultsGeoPage)仅适用于支持地理空间查询的数据存储。
表 9. 查询返回类型
返回类型 描述

void

表示无返回值。

Primitives

Java 基本类型。

Wrapper types

Java 包装类型。

T

一个唯一的实体。期望查询方法最多返回一个结果。如果未找到结果,则返回 null。超过一个结果会触发 IncorrectResultSizeDataAccessException

Iterator<T>

一个 Iterator

Collection<T>

一个 Collection

List<T>

一个 List

Optional<T>

一个 Java 8 或 Guava Optional。期望查询方法最多返回一个结果。如果未找到结果,则返回 Optional.empty()Optional.absent()。超过一个结果会触发 IncorrectResultSizeDataAccessException

Option<T>

Scala 或 Vavr Option 类型。语义上与 Java 8 的 Optional 行为相同,如前所述。

Stream<T>

一个 Java 8 Stream

Streamable<T>

Iterable 的便利扩展,直接公开了用于流式传输、映射和过滤结果、连接它们等方法。

实现 Streamable 并接受 Streamable 构造函数或工厂方法参数的类型

公开带有 Streamable 作为参数的构造函数或 ….of(…)/….valueOf(…) 工厂方法的类型。有关详细信息,请参阅[repositories.collections-and-iterables.streamable-wrapper]

Vavr Seq, List, Map, Set

Vavr 集合类型。有关详细信息,请参阅[repositories.collections-and-iterables.vavr]

Future<T>

一个 Future。期望方法使用 @Async 注解,并要求启用 Spring 的异步方法执行功能。

CompletableFuture<T>

一个 Java 8 CompletableFuture。期望方法使用 @Async 注解,并要求启用 Spring 的异步方法执行功能。

ListenableFuture

一个 org.springframework.util.concurrent.ListenableFuture。期望方法用 @Async 注解,并要求启用 Spring 的异步方法执行功能。

Slice

一个带大小的数据块,并指示是否有更多数据可用。需要一个 Pageable 方法参数。

Page<T>

一个 Slice 带有附加信息,例如总结果数。需要一个 Pageable 方法参数。

GeoResult<T>

一个结果条目,带有附加信息,例如到参考位置的距离。

GeoResults<T>

一个 GeoResult<T> 列表,带有附加信息,例如到参考位置的平均距离。

GeoPage<T>

一个带有 GeoResult<T>Page,例如到参考位置的平均距离。

Mono<T>

使用响应式仓库时,Project Reactor Mono 发出零个或一个元素。期望查询方法最多返回一个结果。如果未找到结果,则返回 Mono.empty()。多于一个结果会触发 IncorrectResultSizeDataAccessException

Flux<T>

使用响应式仓库时,Project Reactor Flux 发出零个、一个或多个元素。返回 Flux 的查询也可以发出无限数量的元素。

Single<T>

使用响应式仓库时,RxJava Single 发出一个元素。期望查询方法最多返回一个结果。如果未找到结果,则返回 Mono.empty()。多于一个结果会触发 IncorrectResultSizeDataAccessException

Maybe<T>

使用响应式仓库时,RxJava Maybe 发出零个或一个元素。期望查询方法最多返回一个结果。如果未找到结果,则返回 Mono.empty()。多于一个结果会触发 IncorrectResultSizeDataAccessException

Flowable<T>

使用响应式仓库时,RxJava Flowable 发出零个、一个或多个元素。返回 Flowable 的查询也可以发出无限数量的元素。

附录 E: Spring Data for Pivotal GemFire 架构


1. 参见[repositories.create-instances.spring]
© . This site is unofficial and not affiliated with VMware.