© 2010-2019 原作者。

您可以将此文档的副本用于自身用途,并分发给他人,前提是您不收取任何费用,并且无论以印刷版还是电子版分发,每个副本都包含此版权声明。

前言

Spring Data for Pivotal GemFire 侧重于将 Spring Framework 功能强大、非侵入式编程模型和概念与 Pivotal GemFire 集成,当使用 Pivotal GemFire 作为数据管理解决方案时,简化 Java 应用程序的配置和开发。

本文档假设您已经对 Spring Framework 核心和 Pivotal GemFire 概念有基本的了解和一定的熟悉程度。

虽然我们尽一切努力确保本文档全面完整且无错误,但有些主题超出了本文件的范围,可能需要更多解释(例如,在保持一致性的同时使用 HA 进行分区的数据分发管理)。此外,可能出现一些排版错误。如果您发现错误,甚至是更严重的错误,请通过在 JIRA 中提出相应的问题来告知 Spring Data 团队。

1. 简介

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

2. 需求

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

3. 新特性

从 1.2.0.RELEASE 版本开始,这个项目(以前称为 Spring GemFire)已重命名为 Spring Data for Pivotal GemFire,以反映它现在是 Spring Data 项目的一个模块,并且构建在 Pivotal GemFire 之上。

3.1. 1.2 版本的新特性

  • 通过 SDG gfe XML 命名空间完全支持 Pivotal GemFire 配置。现在 Pivotal GemFire 组件的配置无需依赖原生的 cache.xml 文件。

  • Pivotal GemFire 6.6.x 的 WAN 网关支持。参见 配置 WAN 网关

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

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

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

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

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

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

3.2. 1.3 版新增功能

  • 升级到 Spring Framework 3.2.8。

  • 升级到 Spring Data Commons 1.7.1。

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

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

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

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

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

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

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

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

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

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

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

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

  • 支持区域中的 PDX 持久键。

  • 当使用 colocated-with 属性指定位置时,完全支持在 Spring 上下文中创建分区区域 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。

  • 将 Spring Data for Pivotal GemFire 与 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]

  • Spring Data for Pivotal GemFire 现在可以在 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 容器中部署 Spring Data for Pivotal GemFire 的支持。

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

  • 添加了自动创建 DiskStore 目录位置的便捷功能。

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

  • 允许手动启动 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 的属性占位符配置可变的 Locator 和 Server 端点列表。

  • 允许将 <gfe-data:datasource> 元素与非 Spring 配置的 Pivotal GemFire 服务器一起使用。

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

  • 基于注解的数据过期

  • [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 构建 SDG 的 Maven POM。

  • 添加了对 CDI 的支持。

  • 允许配置不带 PoolClientCache

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

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

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

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

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

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

  • 修复了与为 Pivotal GemFire 服务器组配置的特定 Pool 关联的客户端 Region 上的存储库查询的 RegionNotFoundException

  • 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 属性作为区域键。

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

  • 允许 GemfireCacheManager 明确列出要在 Spring 的缓存抽象中使用的区域名称。

  • 配置 Pivotal GemFire 缓存、缓存服务器、定位器、池、区域、索引、DiskStore、到期、逐出、统计信息、组播、HttpService、身份验证、SSL、日志记录、系统属性。

  • 添加了在类路径上具有多个 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. 文档结构

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

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

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

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

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

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

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

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

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

  • 示例应用程序 描述了发行版中提供的示例,用于说明 Spring Data for Pivotal GemFire 中提供的各种功能。

5. 使用 Spring 容器引导 Pivotal GemFire

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

本节假设您基本熟悉 Pivotal GemFire。有关更多信息,请参阅 Pivotal GemFire 产品文档

5.1. 使用 Spring 的优势而非 Pivotal GemFire cache.xml

Spring Data for Pivotal GemFire 的 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 命名空间的背后,Spring Data for Pivotal GemFire 广泛使用了 Spring 的 FactoryBean 模式来简化 Pivotal GemFire 组件的创建、配置和初始化。

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

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

5.2. 使用核心命名空间

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

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

要使用 Spring Data for Pivotal GemFire 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 Spring Data for Pivotal GemFire XML 命名空间前缀。任何名称都可以,但在本参考文档中,使用 gfe
2 XML 命名空间前缀映射到 URI。
3 XML 命名空间 URI 位置。请注意,即使该位置指向外部地址(确实存在且有效),Spring 也会在本地解析该架构,因为它包含在 Spring Data for Pivotal GemFire 库中。
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 文档的默认命名空间声明指向 Spring Data for Pivotal GemFire XML 命名空间。
2 Spring 原始 bean 定义的 beans 命名空间前缀声明。
3 使用 beans 命名空间的 Bean 声明。请注意前缀。
4 使用 gfe 命名空间的 Bean 声明。由于 gfe 是默认命名空间,因此请注意没有前缀。

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

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

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

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

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

<datasource> 标签在语法上类似于 <gfe:pool>。可以使用一个或多个嵌套的 locatorserver 元素进行配置以连接到现有的数据网格。此外,支持所有可用于配置池的属性。此配置会自动为连接到定位器的集群成员上定义的每个区域创建客户端区域 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,每个虚拟机(更严格地说,每个 ClassLoader)只能打开一个缓存。在大多数情况下,缓存应该只创建一次。

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

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

<gfe:cache/>

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

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

<gfe:cache id="myCache"/>

可以使用 Spring 完全配置 Pivotal GemFire Cache。但是,也支持 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 配置文件外,您还可以指定 Pivotal GemFire 系统 属性,这些属性使用 Spring 的任何 Properties 支持功能。

例如,您可以使用 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>

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

缓存设置仅在需要创建新缓存时应用。如果虚拟机中已存在打开的缓存,则会忽略这些设置。

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 网关到达的事件。
8 启用 Pivotal GemFire 的 DynamicRegionFactory,它提供了一个分布式区域创建服务。
9 声明一个 JNDI 绑定以在 Pivotal GemFire 事务中注册外部数据源。
启用 PDX 序列化

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

Pivotal GemFire 提供了一个实现类 (org.apache.geode.pdx.ReflectionBasedAutoSerializer),它使用 Java 反射。但是,开发人员通常会提供他们自己的实现。属性的值只是一个对实现 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 应用程序(该应用程序也恰好是 Pivotal GemFire 集群的对等Cache成员)时,不应启用“自动重新连接”。

此限制的主要原因是大多数 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 分布式系统兼容。

Spring Data for Pivotal GemFire 的此功能(将 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),但 Spring Data for Pivotal GemFire 的配置元数据不会被记录。直接使用 Pivotal GemFire 的公共 Java API 时也是如此。它也不会被记录。

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

5.4.2. 配置 Pivotal GemFire CacheServer

Spring Data for Pivotal GemFire 包含对配置 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环境抽象 来支持从主代码库外部化特定于环境的属性,从而简化跨多台机器的部署。
为了避免初始化问题,由 Spring Data for Pivotal GemFire 启动的 CacheServer 会在 Spring 容器完全初始化**之后**启动。这样做可以让声明式定义的潜在区域、侦听器、写入器或实例化器在服务器开始接受连接之前完全初始化和注册。在以编程方式配置这些元素时请记住这一点,因为服务器可能在您的组件之前启动,因此连接的客户端可能无法立即看到它们。

5.4.3. 配置 Pivotal GemFire ClientCache

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

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

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

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

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

例如,要自定义 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 池和 Spring Data for Pivotal GemFire 池定义

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

<gfe:client-cache/>

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

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

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

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

<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,池为 gemfirePool,客户端区域为“示例”。但是,ClientCachegemfirePool 初始化 Pivotal GemFire 的 DEFAULT 池,并且客户端区域在客户端和服务器之间分发数据时使用 gemfirePool

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

<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 的池。Spring Data for Pivotal GemFire 使 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

“示例”区域明确引用并专门使用 serverPool。“AnotherExample”区域使用 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 接口连接到需要它的应用程序类,因此实际的 Region 类型与编程模型分离。通常,每个区域都与一个域对象关联,类似于关系数据库中的表。

Pivotal GemFire 实现以下类型的区域:

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

  • **PARTITION** - 数据在集群中定义该区域的许多缓存成员之间划分为桶(分片)。这提供了高读取和写入性能,适用于对单个节点来说太大的大型数据集。

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

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

有关各种区域类型及其功能以及配置选项的更多信息,请参阅 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/>

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

重要的是要意识到,Spring Data for Pivotal GemFire 使用 Spring BeanPostProcessor 在创建和初始化缓存后对其进行后处理,以确定在 Spring ApplicationContext 中添加为 bean 的 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"/>

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

5.5.3. 配置区域

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

  • LOCAL 区域:<local-region>

  • PARTITION 区域:<partitioned-region>

  • REPLICATE 区域:<replicated-region>

  • 客户端区域:<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 事务 API)事务。

index-update-type

synchronousasynchronous(默认为 synchronous

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

initial-capacity

整数(默认为 16)

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

key-constraint

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

预期的键类型。

负载因子

浮点数 (默认值:.75)

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

名称

任何有效的区域名称。

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

持久化

*布尔值 (默认值:false)

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

快捷方式

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

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

统计信息

布尔值(默认为 false

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

模板

区域模板的名称。

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

值约束

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

预期的值类型。

CacheListener 实例

CacheListener 实例向区域注册以处理区域事件,例如创建、更新、销毁条目等。CacheListener 可以是实现CacheListener接口的任何 bean。区域可以有多个侦听器,这些侦听器通过包含在包含*-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>

以下示例使用cache-listener元素的另一种形式,带有ref属性。这样做可以在定义单个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元素是 Pivotal GemFire 提供回调接口以实现响应缓存或区域事件调用自定义代码的 XML 命名空间中常用模式的一个示例。当使用 Spring 的 IoC 容器时,实现是一个标准的 Spring bean。为了简化配置,模式允许cache-listener元素出现一次,但是,如果允许多个实例,它可以包含嵌套 bean 引用和内部 bean 定义的任意组合。约定是使用单数形式(即cache-listener vs cache-listeners),这反映了最常见的情况实际上是一个实例。我们已经在高级缓存配置示例中看到了此模式的示例。

CacheLoaders 和 CacheWriters

cache-listener类似,XML 命名空间提供cache-loadercache-writer元素来为区域注册这些 Pivotal GemFire 组件。

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

以下示例声明一个同时具有CacheLoaderCacheWriter的区域。

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

以下示例显示启用了压缩的区域。

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

有关区域压缩的更多信息,请参见 Pivotal GemFire 的文档。

5.5.5. 堆外内存

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

使用堆外内存就像声明要使用的内存量,然后启用区域使用堆外内存一样简单,如下面的配置所示。

<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)中。区域的全路径名也应用于 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>

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

<gfe:replicated-region-template>

定义通用的“REPLICATE”区域属性。扩展 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”,“PartitionRegionTemplate”扩展“ExtendedRegionTemplate”,“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配置文件中定义的,则会出现这种情况。因此,首先执行查找以避免任何错误。这是设计使然,可能会更改。

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

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

请考虑以下 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;

    ...
}

在这里,我们在应用程序 DAO 中注入对Customers/Accounts Region 的引用。因此,开发人员通常会在 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 Region 在 Spring 容器中分别作为Customers/AccountsCustomers/Accounts/Orders bean 引用。使用lookup-region 元素和相应的语法(前面已描述)的好处是,它允许您直接引用子 Region,而无需不必要地为父 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 在引用父 Region 和子 Region 时(带或不带前导斜杠)都具有灵活性。例如,父 Region 可以引用为/CustomersCustomers,子 Region 可以引用为/Customers/AccountsCustomers/Accounts。但是,对于 Pivotal GemFire 的 Spring Data 在为 Region 命名 bean 时非常具体。它始终使用正斜杠 (/) 来表示子 Region(例如,/Customers/Accounts)。

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

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

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

5.5.8. 数据驱逐(带溢出)

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

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

例如,要将 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 文档。

配置 Region 进行溢出时,应通过disk-store元素配置存储以获得最大效率。

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

5.5.9. 数据过期

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

Pivotal GemFire 支持以下过期类型

  • 生存时间 (TTL):对象在最后一次创建或更新后可能保留在缓存中的时间(以秒为单位)。对于条目,在创建和 put 操作中,计数器设置为零。当创建 Region 和条目的计数器被重置时,Region 计数器将被重置。

  • 空闲超时 (TTI):对象在最后一次访问后可能保留在缓存中的时间(以秒为单位)。对象的空闲超时计数器在其 TTL 计数器被重置的任何时候都会被重置。此外,每次通过 get 操作或netSearch访问条目时,条目的空闲超时计数器都会被重置。当其一个条目的空闲超时被重置时,Region 的空闲超时计数器将被重置。

这些都可以应用于 Region 本身或 Region 中的条目。Pivotal GemFire 的 Spring Data 提供<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 文档关于过期的部分。

基于注解的数据过期

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

@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 条目值。Pivotal GemFire 的 Spring Data 的过期注解支持不涵盖 Region 的过期。但是,Pivotal GemFire 和 Pivotal GemFire 的 Spring Data 确实允许您使用 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>

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

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

要使用 Pivotal GemFire 的 Spring Data 将特定的 Pivotal GemFire Region 配置为适当地将过期策略应用于使用基于@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. (可选)使用 Pivotal GemFire 的 Spring Data 的@Expiration注解之一:@Expiration@IdleTimeoutExpiration@TimeToLiveExpiration,为存储在 Region 中的应用程序域对象添加注释,这些注解带有过期策略和自定义设置。

  3. (可选)在特定应用程序域对象根本没有使用 Pivotal GemFire 的 Spring Data 的@Expiration注解进行注释的情况下,但 Pivotal GemFire Region 配置为使用 SDG 的自定义AnnotationBasedExpiration类来确定存储在 Region 中的对象的过期策略和设置,您可以通过执行以下操作来设置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>

您可能已经注意到,Pivotal GemFire 的 Spring Data 的@Expiration注解使用String作为属性类型,而不是可能更合适的强类型——例如,int用于“超时”,SDG 的ExpirationActionType用于“操作”。为什么会这样呢?

好吧,让我们介绍 Pivotal GemFire 的 Spring Data 的另一个特性,它利用 Spring 的核心基础设施来方便配置:属性占位符和 Spring 表达式语言 (SpEL) 表达式。

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

@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 属性、调用方法等,而且过期“超时”和“操作”的值也可以是强类型的。请考虑以下示例(基于前面的示例)

<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 将 action 值基于实际的 ExpirationAction 枚举类型,如果枚举类型发生更改,则会很快导致已识别的错误。

例如,所有这些都在 Pivotal GemFire 的 Spring Data 测试套件中进行了演示和测试。有关更多详细信息,请参见源代码

5.5.10. 数据持久化

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

要在 Pivotal GemFire 的 Spring Data 中启用持久性,请在任何 <*-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 必须与区域类型匹配,并且如果也显式设置了 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) 事件消息传递以控制区域接收的条目事件。Pivotal GemFire 的 Spring Data 提供了 <gfe:subscription/> 子元素,用于将 REPLICATEPARTITION 区域的订阅策略设置为 ALLCACHE_CONTENT。以下示例显示了一个订阅策略设置为 CACHE_CONTENT 的区域

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

5.5.12. 本地区域

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

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

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

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

5.5.13. 复制区域

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

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

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

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

5.5.14. 分区区域

Pivotal GemFire 的 Spring Data XML 命名空间也支持 PARTITION 区域。

引用 Pivotal GemFire 文档:

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

PARTITION 区域是使用 partitioned-region 元素创建的。它的配置选项类似于 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 属性
名称 说明

copies

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)。在最后两种配置中,通常声明连接到缓存服务器的客户端区域。

Pivotal GemFire 的 Spring Data 通过其客户端缓存元素: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。如果未指定池,则将使用“DEFAULT”池。

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

有关可在客户端(尤其是在 Pool 上)设置的全部选项列表,请参见 Pivotal GemFire 的 Spring Data 模式(“Pivotal GemFire 的 Spring Data 模式”)和 Pivotal GemFire 关于客户端-服务器配置的文档。

客户端兴趣

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

以下示例显示了基于密钥和基于正则表达式的 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的,则会在客户端断开连接时立即销毁它。您需要决定您的客户端是否应该接收它断开连接期间收到的事件,或者它是否只需要接收重新连接后的最新事件。

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

最后,'result-policy`是一个枚举:KEYSKEYS_VALUENONE。默认为KEYS_VALUESresult-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)之间的转换。

用于 Pivotal GemFire 的 Spring Data 提供了<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(对象查询语言)查询的性能。

在用于 Pivotal GemFire 的 Spring Data 中,索引是用index元素声明的,如下例所示。

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

在用于 Pivotal GemFire 的 Spring Data 的 XML 模式(也称为 SDG XML 命名空间)中,index bean 声明不像 Pivotal GemFire 的本机cache.xml那样绑定到区域。相反,它们是类似于<gfe:cache>元素的顶级元素。这允许您在任何区域上声明任意数量的索引,无论它们是刚刚创建的还是已经存在的——这比 Pivotal GemFire 的本机cache.xml格式有了显著改进。

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

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

考虑以下示例,它具有lastName属性。

@Region("Customers")
class Customer {

  @Id
  Long id;

  String lastName;
  String firstName;

  ...
}

现在考虑以下示例,它具有用于查询Customer对象的应用程序定义的 SDG 存储库。

interface CustomerRepository extends GemfireRepository<Customer, Long> {

  Customer findByLastName(String lastName);

  ...
}

SDG 存储库查找器/查询方法导致生成并运行以下 OQL 语句。

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

因此,您可能希望创建一个具有类似于以下语句的Index

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

from子句必须引用一个有效的现有区域,这是Index应用于区域的方式。这并不是用于 Pivotal GemFire 的 Spring Data 特有的。它是 Pivotal GemFire 的一项功能。

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

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

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

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

5.6.1. 定义索引

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

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

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

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

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

5.6.2. IgnoreIfExistsOverride

用于 Pivotal GemFire 的 Spring Data 的两个Index配置选项值得特别提及:ignoreIfExistsoverride

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

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

每个选项的行为都大相径庭,并且完全取决于抛出的 Pivotal GemFire Index异常的类型。这也意味着,如果未抛出 Pivotal GemFire Index 类型异常,则这两个选项均无效。这些选项旨在专门处理 Pivotal GemFire IndexExistsExceptionIndexNameConflictException实例,这些实例可能由于各种原因(有时原因不明确)而发生。这些异常的原因如下:

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

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

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

IgnoreIfExists始终优先于Override,这主要是因为它使用的资源更少,因为它在两种异常情况下都会返回“现有”Index

IgnoreIfExists 行为

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

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

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

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

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

SDG尽力在被忽略的Index的定义与其现有Index的定义存在显著差异时通知用户。但是,为了实现这一点,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时,SDG才能做到这一点,这取决于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:snapshot-service>元素,该元素来自<gfe-data> XML命名空间。

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

<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的SnapshotServiceFactoryBean会将region-ref属性值解析为在Spring容器中定义的区域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 文件,然后方便地将 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 网关

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

5.10.1. Pivotal GemFire 7.0 中的 WAN 配置

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

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

<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注解

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

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

  • @ClientCacheApplication

  • @PeerCacheApplication

  • @CacheServerApplication

这三个注解是 Spring 应用程序开发人员在使用 Pivotal GemFire 时首先要接触到的。

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

您可以使用ClientCache实例将 Spring Boot 应用程序配置为 Pivotal GemFire 缓存客户端,该实例可以与用于管理应用程序数据的现有 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上运行、侦听默认缓存服务器端口40404CacheServer,如下所示

带有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,在所有系统网卡上接受连接。您无需执行任何特殊操作即可使用客户端-服务器拓扑。只需使用@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实例(除了创建ClientCache实例时@ClientCacheApplication注解提供的“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及更早版本不允许在一个类上声明多个相同类型的注解。

细心的读者可能已经注意到,在所有情况下,您都为所有主机名、端口和面向配置的注解属性指定了硬编码值。当应用程序被提升并部署到不同的环境(例如从开发到测试到预发布到生产)时,这不是理想的做法。

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

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属性。

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

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

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

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

@LocatorApplication用于配置和引导独立的Pivotal GemFire定位器应用程序进程。此进程只能是定位器,不能是其他任何东西。如果您尝试使用缓存实例启动定位器,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(注解创建的 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 提供的所有 Configurers 在回调中都接受两条信息:注解在 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. PropertiesProperties

以通常的 Spring 方式,您甚至可以使用其他 Properties 来表达 Properties,无论是通过……以下示例显示了在 application.properties 文件中设置嵌套属性

Properties 的 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. 配置嵌入式 Locator

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

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

运行嵌入式 Locator 的 Spring、Pivotal GemFire CacheServer 应用程序
@SpringBootApplication
@CacheServerApplication
@EnableLocator
class ServerApplication { .. }

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

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

然后,您可以通过连接到此 Locator 来启动其他启用了 Spring Boot @CacheServerApplication 的应用程序,如下所示

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

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

运行嵌入式 Locator 并连接到 Locator 的 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 类的运行配置文件中只有一个应设置 -Dspring.profiles.active=embedded-locator Java 系统属性。然后,您可以为其他每个运行配置文件更改 ..name..cache.server.port,并在您的本地系统上运行一个小型 Pivotal GemFire 服务器集群(分布式系统)。

@EnableLocator 注解旨在仅作为开发时注解,而不是应用程序开发者在生产中使用的注解。我们强烈建议将 Locator 作为独立的、独立的集群进程运行。

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

6.7.2. 配置嵌入式 Manager

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

Manager 允许启用 JMX 的客户端(例如 *Gfsh* shell 工具)连接到 Manager 以管理集群。鉴于这些都是启用 JMX 的客户端,也可以使用 JDK 提供的工具(如 JConsole 或 JVisualVM)连接到 Manager。

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

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

以下示例显示如何在 Java 中创建嵌入式 Manager

运行嵌入式 Manager 的 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 命令直接连接到 Manager
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 { .. }

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

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

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

6.9. 配置统计信息

为了更深入地了解 Pivotal GemFire 的运行时情况,您可以启用统计信息。收集统计数据有助于在出现复杂问题(通常是分布式问题,其中时间是一个关键因素)时进行系统分析和故障排除。

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

要启用统计信息,请使用@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 和原生客户端操作相同的数据集。

  • 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实例并进行注册。

您可以将此组合PdxSerializer声明为 Spring 容器中的托管 Bean,并使用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()...
  }
}

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

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

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

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

6.11. 配置 Pivotal GemFire 属性

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

使用@EnableGemFireProperties注解应用程序类是一种方便且不错的替代方法,可以替代创建gemfire.properties文件或在启动应用程序时将 Pivotal GemFire 属性设置为 Java 系统属性。

我们建议在将应用程序部署到生产环境时,在gemfire.properties文件中设置这些 Pivotal GemFire 属性。但是,在开发时,根据需要分别设置这些属性以用于原型设计、调试和测试目的可能会很方便。

一些您通常不必担心的不太常用的 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 将数据在缓存中组织成区域。您可以将区域视为关系数据库中的表。通常,一个区域只应存储单一类型的对象,这使其更有利于构建有效的索引和编写查询。我们将在后面介绍索引。

以前,Pivotal GemFire 的 Spring Data 用户需要通过编写非常冗长的 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 存储库的使用与使用新的@EnableEntityDefinedRegions注释的基于注解的配置的表达能力相结合。

大多数 Spring Data 应用程序开发人员应该已经熟悉Spring Data 存储库抽象和 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 Data for Pivotal GemFire 在 Spring 容器引导时提供应用程序存储库接口的实现。只要您遵循约定,SDG 甚至会实现您定义的查询方法。

现在,当您定义Book类时,您还通过在实体类型上声明 Spring Data for Pivotal GemFire 映射注释@Region 来指定将Book实例映射(存储)到的区域。当然,如果存储库接口(在本例中为BookRepository)的类型参数中引用的实体类型(在本例中为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 存储库时,从实体类创建区域最为有用。Spring Data for Pivotal GemFire 的存储库支持通过@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 默认情况下会创建一个客户端PROXYRegion。或者,如果您通过使用@PeerCacheApplication@CacheServerApplication注释将应用程序声明为对等Cache,则 SDG 默认情况下会创建一个服务器PARTITIONRegion

当然,您总可以在必要时覆盖默认值。要覆盖 Spring Data for Pivotal GemFire 应用的默认值,已引入四个新的区域映射注释

  • @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应用程序将连接到的集群中已定义的区域声明您的区域。

这允许您使用服务器集群作为数据定义的主要来源来集中您的配置,并确保集群的所有客户端应用程序都具有相同的配置。当在云管理环境中快速扩展大量相同客户端应用程序实例以处理增加的负载时,这尤其有用。

其理念是,用户不是由客户端应用程序(s)驱动数据字典,而是使用 Pivotal GemFire 的 *Gfsh* CLI shell 工具定义区域。这还有一个额外的好处,那就是当向集群添加其他对等节点时,它们也将拥有并共享相同的配置,因为 Pivotal GemFire 的 *集群配置服务* 会记住它。

例如,用户可能会在 *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 的 *集群配置服务*,添加到服务器集群中以处理增加负载(后端)的任何其他对等成员也将具有相同的配置,例如

向集群添加另一个对等成员
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”区域可能是实现书店应用程序服务所需的许多不同区域之一。不必单独创建和配置每个区域,SDG 方便地允许从集群定义客户端应用程序区域,如下所示

使用 @EnableClusterDefinedRegions 从集群定义客户端区域
@ClientCacheApplication
@EnableClusterDefinedRegions
class BookStoreClientApplication {

    public static void main(String[] args) {
        ....
    }

    ...
}
@EnableClusterDefinedRegions 只能在客户端使用。
您可以使用 clientRegionShortcut 注解属性来控制在客户端上创建的区域类型。默认情况下,会创建一个客户端 PROXY 区域。将 clientRegionShortcut 设置为 ClientRegionShortcut.CACHING_PROXY 以实现“*近缓存*”。此设置适用于从集群定义的区域创建的所有客户端区域。如果您想控制从集群上定义的区域创建的客户端区域的各个设置(如数据策略),则可以实现一个 RegionConfigurer,并根据区域名称使用自定义逻辑。

然后,在您的应用程序中使用“Books”区域就变得很简单了。您可以直接注入“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 并映射到“Books”区域来定义 Spring Data 存储库定义,如下所示

使用带有 SD 存储库的“Books”区域
interface BookRepository extends CrudRepository<Book, ISBN> {
    ...
}

然后,您可以将自定义 BooksDataAccessObjectBookRepository 注入到您的应用程序服务组件中,以执行所需的任何业务功能。

6.12.3. 配置逐出

使用 Pivotal GemFire 管理数据是一项主动任务。通常需要进行调整,并且您必须结合使用多种功能(例如,逐出和 过期)才能有效地使用 Pivotal GemFire 在内存中管理您的数据。

鉴于 Pivotal GemFire 是一个内存内数据网格 (IMDG),数据是在内存中管理的,并分布到参与集群的其他节点,以最大限度地减少延迟,最大限度地提高吞吐量并确保数据高度可用。由于应用程序的数据通常并非全部适合内存(即使是在整个节点集群中,更不用说单个节点上了),您可以通过向集群添加新节点来增加容量。这通常被称为线性横向扩展(而不是纵向扩展,这意味着添加更多内存、更多 CPU、更多磁盘或更多网络带宽——基本上是更多每种系统资源以处理负载)。

尽管如此,即使使用节点集群,通常也必须仅将最重要的数据保存在内存中。内存不足,甚至接近满负荷,都几乎从来都不是一件好事。停止世界垃圾收集,或者更糟糕的是 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) 条目过期策略。

用于 Pivotal GemFire 的 Spring Data 基于 早期和现有的条目过期注解支持(在用于 Pivotal GemFire 的 Spring Data 版本 1.5 中添加)进行基于注解的过期配置。

从本质上讲,用于 Pivotal GemFire 的 Spring Data 的过期注解支持基于 Pivotal GemFire 的 org.apache.geode.cache.CustomExpiry 接口的自定义实现。此 o.a.g.cache.CustomExpiry 实现检查存储在区域中的用户应用程序域对象中是否存在类型级别的过期注解。

用于 Pivotal GemFire 的 Spring Data 提供以下过期注解

  • 过期

  • 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 或不同的压缩编解码器来压缩内存区域值。Pivotal GemFire 默认使用 Google 的 Snappy 压缩库。

要启用压缩,请使用 @EnableCompression 注解应用程序类,如下所示

启用了区域压缩的 Spring 应用程序
@SpringBootApplication
@ClientCacheApplication
@EnableCompression(compressorBeanName = "MyCompressor", regionNames = { "Customers", "Orders" })
class ClientApplication { .. }
compressorBeanNameregionNames 属性都不是必需的。

compressorBeanName 默认值为 SnappyCompressor,启用 Pivotal GemFire 的 SnappyCompressor

regionNames 属性是一个区域名称数组,用于指定启用了压缩的区域。如果未显式设置 regionNames 属性,则默认情况下所有区域都会压缩值。

或者,您可以在 application.properties 文件中使用 spring.data.gemfire.cache.compression.compressor-bean-namespring.data.gemfire.cache.compression.region-names 属性来设置和配置这些 @EnableCompression 注解属性的值。
要使用 Pivotal GemFire 的区域压缩功能,您必须在应用程序的 pom.xml 文件(对于 Maven)或 build.gradle 文件(对于 Gradle)中包含 org.iq80.snappy:snappy 依赖项。这仅在您使用 Pivotal GemFire 对区域压缩的默认支持(默认情况下使用 SnappyCompressor)时才需要。当然,如果您使用其他压缩库,则需要在应用程序的类路径上包含该压缩库的依赖项。此外,您需要实现 Pivotal GemFire 的 Compressor 接口以适配您选择的压缩库,将其定义为 Spring 压缩器中的 bean,并将 compressorBeanName 设置为此自定义 bean 定义。

有关详细信息,请参阅 @EnableCompression 注解 Javadoc

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

6.12.6. 配置堆外内存

减少 JVM 堆内存压力和最大限度地减少 GC 活动的另一种有效方法是使用 Pivotal GemFire 的堆外内存支持。

区域条目不是存储在 JVM 堆上,而是存储在系统的内存中。正如 Pivotal GemFire 用户指南 中所述,当被存储的对象大小一致、大多小于 128K 且无需频繁反序列化时,堆外内存通常效果最佳。

要启用堆外内存,请使用 @EnableOffHeap 注解应用程序类,如下所示

启用了堆外内存的 Spring 应用程序
@SpringBootApplication
@PeerCacheApplication
@EnableOffHeap(memorySize = 8192m regionNames = { "Customers", "Orders" })
class ServerApplication { .. }

memorySize 属性是必需的。memorySize 属性的值指定区域可以使用兆字节 (m) 或千兆字节 (g) 的主内存数量。

regionNames 属性是一个区域名称数组,用于指定在主内存中存储条目的区域。如果未显式设置 regionNames 属性,则默认情况下所有区域都使用主内存。

或者,您可以在 application.properties 文件中使用 spring.data.gemfire.cache.off-heap.memory-sizespring.data.gemfire.cache.off-heap.region-names 属性来设置和配置这些 @EnableOffHeap 注解属性的值。

有关详细信息,请参阅 @EnableOffHeap 注解 Javadoc

6.12.7. 配置磁盘存储

或者,您可以配置 Regions 将数据持久化到磁盘。您也可以配置 Regions 在 Region 条目被驱逐时将数据溢出到磁盘。在这两种情况下,都需要一个DiskStore来持久化和/或溢出数据。当为具有持久性或溢出功能的 Region 未显式配置DiskStore时,Pivotal GemFire 将使用DEFAULT DiskStore

我们建议在将数据持久化和/或溢出到磁盘时定义特定于 Region 的DiskStores

Spring Data for Pivotal GemFire 提供了注解支持,可以通过使用@EnableDiskStore@EnableDiskStores注解来定义和创建应用程序 Region DiskStores

@EnableDiskStores是一个组合注解,用于聚合一个或多个@EnableDiskStore注解。

例如,虽然Book信息可能主要由来自某些外部数据源(例如亚马逊)的参考数据组成,但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注解(与@EnableDiskStores注解本身组合)创建的DiskStores的公共DiskStore属性。各个DiskStore配置会覆盖特定的全局设置,但@EnableDiskStores注解方便地定义了适用于该注解聚合的所有DiskStores的公共配置属性。

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. 配置索引

除非可以访问数据,否则在 Regions 中存储数据并没有多大用处。

除了Region.get(key)操作(尤其是在预先知道键的情况下),通常通过对包含数据的 Regions 执行查询来检索数据。在 Pivotal GemFire 中,查询是使用对象查询语言 (OQL) 编写的,客户端希望访问的特定数据集在查询的谓词中表达(例如,SELECT * FROM /Books b WHERE b.author.name = 'Jon Doe')。

通常,不使用索引进行查询效率低下。在不使用索引执行查询时,Pivotal GemFire 执行等效于全表扫描的操作。

为查询谓词中使用的对象上的字段创建和维护索引,以匹配查询投影表达的感兴趣的数据。可以创建不同类型的索引,例如索引和哈希索引。

Spring Data for Pivotal GemFire 简化了在存储和访问数据的 Regions 上创建索引的过程。无需像以前那样使用 Spring 配置显式声明Index bean 定义,我们可以如下在 Java 中创建Index bean 定义

使用 Java 配置的 Index 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 的 Index bean 定义
<gfe:index id="BooksIsbnIndex" expression="isbn" from="/Books" type="KEY"/>

但是,现在您可以直接在应用程序域对象类型的字段上定义索引,您知道这些字段将用于查询谓词以加快这些查询的速度。您甚至可以为从应用程序存储库接口上的用户定义查询方法生成的 OQL 查询应用索引。

重新使用之前的示例Book实体类,我们可以注释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 名称><字段/属性名称><索引类型>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注解定义的 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 配置中声明显式CacheManagerbean 定义(名为“cacheManager”)的需要。

其次,@EnableCachingDefinedRegions注解,就像“配置 Region”中描述的@EnableEntityDefinedRegions注解一样,会检查整个 Spring 应用程序,缓存带注解的服务组件以识别应用程序在运行时所需的所有缓存,并在应用程序启动时为这些缓存在 Pivotal GemFire 中创建 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 存储库抽象时,我们知道您的应用程序将需要所有 Region(例如由@Region注解的实体类定义的 Region)和索引(例如由@Indexed注解的实体字段和属性定义的索引)。

当您使用 Spring 的缓存抽象时,我们还知道应用程序的服务组件所需的所有缓存注解中标识的所有 Region。

从本质上讲,您只需使用 Spring 框架的所有 API 和功能(无论是在注解元数据、Java、XML 中表达,还是用于配置、映射或任何目的),就通过开发应用程序来告诉我们我们所需的一切。

关键在于,您可以专注于应用程序的业务逻辑,同时使用框架的功能和支持基础结构(例如 Spring 的缓存抽象、Spring Data 存储库、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 或索引定义的选项。要重新创建 Region 或索引,必须先使用 *Gfsh* 销毁 Region 或索引,然后重新启动客户端应用程序,以便将配置再次推送到服务器。或者,您可以使用 *Gfsh* 手动(重新)定义 Region 和索引。
与 *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 { .. }

您将立即获得一个具有 Pivotal GemFire ClientCache实例、Spring Data 存储库、使用 Pivotal GemFire 作为缓存提供程序的 Spring 缓存抽象(其中 Region 和索引不仅在客户端创建,而且还推送到集群中的服务器)的 Spring Boot 应用程序。

从那里,您只需要执行以下操作

  • 定义使用映射和索引注解进行注解的应用程序域模型对象。

  • 定义存储库接口以支持每种实体类型的基本数据访问操作和简单查询。

  • 定义包含事务实体的业务逻辑的服务组件。

  • 在需要缓存、事务行为等的服务方法上声明相应的注解。

在这种情况下,没有什么与应用程序后端服务(例如 Pivotal GemFire)所需的基础结构和底层实现有关。数据库用户具有类似的功能。现在 Spring 和 Pivotal GemFire 开发人员也一样。

结合以下 Spring Data for Pivotal GemFire 注解,此应用程序将轻松运行,付出很少的努力

  • @EnableContinuousQueries

  • @EnableGemfireFunctionExecutions

  • @EnableGemfireCacheTransactions

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实现的实例,并使用CacheFactory.setSecurityManager(:SecurityManager)方法在创建Pivotal GemFire对等Cache时设置它。

  • 创建一个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 Realm解决了,后者更广为人知,并且不是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支持的任何Realm

您甚至可以创建Apache Shiro Realm的自定义实现。

更多详情请参见Apache Shiro关于Realm的文档

当Apache Shiro位于集群中服务器的CLASSPATH中,并且一个或多个Apache Shiro Realm已在Spring容器中定义为bean时,Spring Data for Pivotal GemFire会检测到此配置,并在使用@EnableSecurity注解时使用Apache Shiro作为安全提供程序来保护您的Pivotal GemFire服务器。

您可以在此spring.io博客文章中找到有关Spring Data for Pivotal GemFire对使用Apache Shiro的Pivotal GemFire新的集成安全框架的支持的更多信息。

有关可用属性和关联配置属性的更多详细信息,请参阅@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
默认情况下,Spring Boot可以在将application.properties文件放在应用程序CLASSPATH的根目录中时找到它。当然,Spring支持多种使用其资源抽象来定位资源的方法。

有关可用属性和关联配置属性的更多详细信息,请参阅@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框架来说无关紧要,但为了代码的可读性,我们通常建议以方便下一个人维护代码为目标(将来这可能就是您自己)。

6.18.2. 其他基于配置的注解

以下SDG注解在本参考文档中未讨论,原因是该注解支持Pivotal GemFire的已弃用功能,或者因为存在更好的替代方法来完成该注解提供的功能。

  • @EnableAuth:启用Pivotal GemFire旧的身份验证和授权安全模型。(已弃用。Pivotal GemFire的新集成安全框架可以通过使用SDG的@EnableSecurity注解在客户端和服务器上启用,如“配置安全”中所述。)

  • @EnableAutoRegionLookup:不推荐。本质上,此注解支持查找在外部配置元数据(例如cache.xml或应用于服务器的集群配置)中定义的区域,并自动将这些区域注册为Spring容器中的bean。此注解与SDG XML命名空间中的<gfe:auto-region-lookup>元素相对应。更多详情请访问此处。使用Spring和Spring Data for Pivotal GemFire时,用户通常应该更倾向于Spring配置。请改用“配置区域”和“配置集群配置推送”。

  • @EnableBeanFactoryLocator:启用SDG GemfireBeanFactoryLocator功能,这仅在使用外部配置元数据(例如cache.xml)时才有用。例如,如果您在cache.xml中定义的区域上定义了一个CacheLoader,您仍然可以使用在Spring配置中定义的关系数据库DataSource bean自动装配此CacheLoader。此注解利用了此SDG 功能,如果您有大量的遗留配置元数据(例如cache.xml文件),则可能很有用。

  • @EnableGemFireAsLastResource:在全局 - JTA事务管理中与Pivotal GemFire一起讨论。

  • @EnableMcast:启用 Pivotal GemFire 的旧的对等发现机制,该机制使用基于 UDP 的组播网络。(已弃用。请改用 Pivotal GemFire 定位器。参见“配置嵌入式定位器”。)

  • @EnableRegionDataAccessTracing:用于调试目的。此注解通过注册一个 AOP 切面来启用对区域上执行的所有数据访问操作的跟踪,该切面代理在 Spring 容器中声明为 bean 的所有区域,拦截区域操作并记录事件。

6.19. 结论

正如我们在前面几节中了解到的那样,Spring Data for Pivotal GemFire 的新的基于注解的配置模型提供了强大的功能。希望它能够实现其目标,即在使用 Spring 和 Pivotal GemFire 时让您能够快速轻松地入门。

请记住,当您使用新的注解时,您仍然可以使用 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 服务器应用程序一起使用。

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

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 服务器应用程序一起使用。

有关更多详细信息,请参阅配置嵌入式管理器

6.20.5. 配置嵌入式 HTTP 服务器

使用@EnableHttpService注解您的 Spring @PeerCacheApplication@CacheServerApplication类,以启动监听端口7070的嵌入式 HTTP 服务器 (Jetty),如下所示

@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类,以启动监听端口11211的嵌入式 Memcached 服务器 (Gemcached),如下所示

@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类,以启动监听端口6379的嵌入式 Redis 服务器,如下所示

@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。

有关更多详细信息,请参阅配置日志记录

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. 配置缓存

要在Spring的缓存抽象中使用Pivotal GemFire作为缓存提供程序,并让SDG自动创建应用程序服务组件所需的Pivotal GemFire区域,请使用@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是可选的。也就是说,如果需要,您可以手动定义区域。

有关更多详细信息,请参见配置Spring的缓存抽象

6.20.15. 为持久性应用程序配置区域、索引、资源库和实体

要快速创建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映射注解指定实体将存储在其中的区域。使用@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用于确定应用程序所需的区域。有关更多详细信息,请参见配置特定类型的区域POJO映射

最后,使用简单的查询定义您的CRUD存储库以持久化和访问Books,如下所示

package example.app.repo;

import ...;

public interface BookRepository extends CrudRepository {

  List<Book> findByAuthorOrderByPublishedDesc(Author author);

}
有关更多详细信息,请参见Pivotal GemFire存储库的Spring Data

参见@RegionJavadoc

参见@IndexedJavadoc

有关更多详细信息,请参见配置区域

有关更多详细信息,请参见Pivotal GemFire存储库的Spring Data

6.20.16. 从集群定义的区域配置客户端区域

或者,您可以使用@EnableClusterDefinedRegions从集群中已定义的区域定义客户端[*PROXY*]区域,如下所示

@SpringBootApplication
@ClientCacheApplication
@EnableClusterDefinedRegions
@EnableGemfireRepositories
public class ClientApplication {

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

  ...
}

有关更多详细信息,请参见配置的集群定义区域

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 GemFireClientCache应用程序开发Spring Data应用程序时,在开发过程中将服务器配置为在客户端/服务器拓扑中与客户端匹配非常有用。实际上,Pivotal GemFire期望当您在客户端上具有“/Example”PROXYRegion时,服务器中存在具有匹配名称(即“Example”)的Region

您可以使用Gfsh创建应用程序所需的每个区域和索引,或者,您可以在运行Spring Data应用程序使用Pivotal GemFire时,只需推送在开发过程中已表达的配置元数据。

这与使用@EnableClusterConfiguration(..)注解您的主应用程序类一样简单

使用@EnableClusterConfiguration
@ClientCacheApplication
@EnableClusterConfiguration(useHttp = true)
class ClientApplication {
  ...
}
大多数情况下,在使用客户端/服务器拓扑时,尤其是在生产环境中,集群的服务器将使用Gfsh启动。在这种情况下,通常使用HTTP(S)将配置元数据(例如区域和索引定义)发送到集群。当使用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 GemFireGatewayReceiver仅是服务器端功能,只能在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 GemFireGatewaySender仅是服务器端功能,只能在CacheServer或对等Cache节点上配置。

在上面的示例中,应用程序配置了两个区域Region1Region2。此外,将配置两个GatewaySenders来为这两个区域提供服务。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(s)将自动附加到应用程序中配置的每个Region

7. 使用Pivotal GemFire API

配置Pivotal GemFire缓存和区域后,可以在应用程序对象内部注入和使用它们。本章介绍与Spring的事务管理功能和DAO异常层次结构的集成。本章还介绍了对Pivotal GemFire托管对象的依赖注入的支持。

7.1. GemfireTemplate

与Spring提供的许多其他高级抽象一样,Spring Data for Pivotal GemFire提供了一个**模板**来简化Pivotal GemFire数据访问操作。该类提供了几种包含常见区域操作的方法,但还提供了一种使用GemfireCallback对本机Pivotal GemFire API执行代码的能力,而无需处理Pivotal GemFire检查的异常。

模板类需要一个Pivotal GemFireRegion,一旦配置完成,它就是线程安全的,可以在多个应用程序类中重复使用

<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 来透明地应用于您的数据访问对象 (DAO),方法是定义一个PersistenceExceptionTranslationPostProcessor bean。当使用 Pivotal GemFire 时,只要声明了CacheFactoryBean(例如,使用<gfe:cache/><gfe:client-cache>声明),就会启用相同的异常转换功能,它充当异常转换器,并由 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 事务中“最后一个资源”非 XA 兼容资源,尽管 JTA 规范中实际上并不需要。有关非 XA 兼容“最后一个资源”的更多信息,请参阅 Red Hat 的文档。实际上,Red Hat 的 JBoss 项目Narayana就是这样一个 LGPL 开源实现。Narayana 将此称为“最后一个资源提交优化”(LRCO)。更多详细信息,请参阅此处

但是,无论您是在支持“最后一个资源”的开源 JTA 事务管理实现的独立环境中使用 Pivotal GemFire,还是在托管环境(例如 WAS 等 Java EE AS)中使用,Spring Data for Pivotal GemFire 都能满足您的需求。

要正确地将 Pivotal GemFire 用作 JTA 事务中的“最后一个资源”(涉及多个事务资源),您必须完成一系列步骤。此外,此类安排中只能有一个非 XA 兼容资源(例如 Pivotal GemFire)。

1) 首先,您必须完成 Pivotal GemFire 文档中此处的步骤 1-4。

上述步骤 1 与您的 Spring [Boot] 和/或 [Spring 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 设置读取时复制(客户端)
<gfe:client-cache ... copy-on-read="true"/>

ClientCache JavaConfig

使用 JavaConfig 设置 copyOnRead(客户端)
@Bean
ClientCacheFactoryBean gemfireCache() {

  ClientCacheFactoryBean gemfireCache = new ClientCacheFactoryBean();

  gemfireCache.setCopyOnRead(true);

  return gemfireCache;
}

对等Cache XML

使用 XML 设置读取时复制(服务器)
<gfe:cache ... copy-on-read="true"/>

对等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) 必须在也指定了 Spring 的@EnableTransactionManagement注解的同一个 Spring@Configuration类上声明@EnableGemFireAsLastResource注解。

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注解导入了包含 2 个 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
  }

  ...
}

上述步骤 #1 和 #4 由 Spring 的基于 JTA 的PlatformTransactionManager在您的应用程序进入@Transactional边界时(即,当调用MyTransactionService.someTransactionalServiceMethod()时)适当处理。

步骤 #2 和 #3 由使用@EnableGemFireAsLastResource注解启用的 Spring Data for Pivotal GemFire 的新 Aspect 处理。

当然,步骤 #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 配置为“最后一个资源”的更多详细信息,请参阅此处

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 事件的创建、注册、生命周期和调度,该容器负责 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实例将任意参数传递给声明的类。

在本节中,我们将介绍如何在 Spring 中定义cache.xml中定义的可插入组件时配置这些组件,同时保持在cache.xml中定义的 Cache/Region 配置。这允许您的可插入组件专注于应用程序逻辑,而不是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),允许通过**模板**bean 定义连接 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/name 为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 Cache 抽象的支持

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”),则CacheManager bean 定义上的cache-ref属性不是必需的,例如<gfe:cache> 没有显式 ID。

当声明GemfireCacheManager(单例)bean 实例并启用声明式缓存时(在 XML 中使用<cache:annotation-driven/>或在 JavaConfig 中使用 Spring 的@EnableCaching注解),Spring 缓存注解(例如@Cacheable)会标识将使用 Pivotal GemFire 区域在内存中缓存数据的“缓存”。

这些缓存(即区域)必须在使用它们的缓存注解之前存在,否则会发生错误。

例如,假设您有一个客户服务应用程序,其中包含执行缓存的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 是特定于环境的。序列化此类信息是没有用的,甚至可能很危险,因为它只存在于某个虚拟机或机器上。对于此类情况,Spring Data for Pivotal GemFire 提供了一个特殊的Instantiator,它在反序列化期间为 Pivotal GemFire 创建的每个新实例执行连接。

通过这种机制,您可以依靠 Spring 容器来注入和管理某些依赖项,从而轻松地将瞬态数据与持久性数据分离,并以透明的方式拥有丰富的域对象。

Spring 用户可能会发现这种方法类似于@ConfigurableWiringInstantiator的工作方式类似于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 标识符属性是最终的,但在构造函数中设置为null。该类公开了一个withId(…)方法,该方法用于设置标识符,例如,当实例插入到数据存储中并且已生成标识符时。原始Person实例保持不变,因为创建了一个新实例。对于其他可能必须更改以进行持久性操作的存储管理属性,通常也应用相同的模式。
2 firstnamelastname属性是可能通过 getter 公开的普通不可变属性。
3 age属性是从birthday属性派生的不可变属性。使用所示的设计,数据库值将胜过默认值,因为 Spring Data 使用唯一声明的构造函数。即使目的是应该首选计算,重要的是这个构造函数也需要age作为参数(以可能忽略它),否则属性填充步骤将尝试设置 age 字段,并且由于它是不可变的且没有 wither 而失败。
4 comment属性是可变的,通过直接设置其字段来填充。
5 remarks属性是可变的,通过直接设置comment字段或调用comment的 setter 方法来填充。
6 该类公开了一个工厂方法和一个构造函数来创建对象。这里的核心思想是使用工厂方法而不是附加构造函数来避免需要通过@PersistenceConstructor进行构造函数消歧。相反,属性的默认值在工厂方法中处理。

9.1.3. 常规建议

  • 尽量使用不可变对象 — 不可变对象的创建非常简单,只需调用其构造函数即可。此外,这避免了您的领域对象充斥着允许客户端代码操作对象状态的setter方法。如果您需要这些方法,最好将其设置为包私有,以便只有有限数量的同位置类型可以调用它们。仅使用构造函数创建对象的速度比填充属性快 30%。

  • 提供一个全参数构造函数 — 即使您无法或不想将您的实体建模为不可变值,提供一个将实体的所有属性(包括可变属性)作为参数的构造函数仍然很有价值,因为这允许对象映射跳过属性填充以获得最佳性能。

  • 使用工厂方法代替重载构造函数以避免 @PersistenceConstructor — 由于需要全参数构造函数来获得最佳性能,我们通常希望公开更多特定于应用程序用例的构造函数,这些构造函数省略了自动生成的标识符等。一个成熟的模式是使用静态工厂方法来公开这些全参数构造函数的变体。

  • 确保您遵守允许使用生成的实例化器和属性访问器类的约束条件

  • 对于要生成的标识符,仍然结合使用 final 字段和 wither 方法

  • 使用 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 中,还可以将实体存储在子 Region 中,如下例所示。

@Region("/Users/Admin")
public class Admin extends User {
  …
}

@Region("/Users/Guest")
public class Guest extends User {
  …
}

请务必使用 Pivotal GemFire Region 的完整路径,该路径使用 Spring Data for Pivotal GemFire XML 命名空间通过 <*-region> 元素的 idname 属性定义。

9.2.1. 按 Region 类型进行实体映射

除了 @Region 注解外,Spring Data for Pivotal GemFire 还识别特定于类型的 Region 映射注解:@ClientRegion@LocalRegion@PartitionRegion@ReplicateRegion

在功能上,这些注解在 SDG 映射基础设施中的处理方式与通用 @Region 注解完全相同。但是,这些额外的映射注解在 Spring Data for Pivotal GemFire 的注解配置模型中非常有用。当与 Spring @Configuration 注解类的 @EnableEntityDefinedRegions 配置注解结合使用时,无论应用程序是客户端还是对等体,都可以在本地缓存中生成 Region。

这些注解允许您更具体地指定应用程序实体类应映射到的 Region 类型,并且还会影响 Region 的数据管理策略(例如,分区(也称为分片)与复制数据)。

将这些特定于类型的 Region 映射注解与 SDG 注解配置模型一起使用,可以避免您必须在配置中显式定义这些 Region。

9.3. 仓库映射

作为通过在实体类上使用 @Region 注解来指定实体存储的 Region 的替代方法,您还可以指定实体的 Repository 接口上的 @Region 注解。有关更多详细信息,请参阅 Spring Data for Pivotal GemFire 仓库

但是,假设您想将 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 注解的构造函数。

为了为构造函数参数提供参数,序列化程序使用 Spring 的 @Value 注解从提供的 PdxReader 中读取具有命名构造函数参数的字段,如下例所示。

示例 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);

在使用 Password 应用程序域模型类型注册应用程序定义的 SaltedHashPasswordPdxSerializer 实例后,MappingPdxSerializer 将咨询自定义 PdxSerializer 来序列化和反序列化所有 Password 对象,无论包含的对象是什么(例如,User)。

但是,假设您只想自定义 User 对象上 Passwords 的序列化。为此,您可以通过指定类的字段或属性的完全限定名称来为 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;

  ...
}

在这种情况下,`Customer` 的 `id` 字段在序列化期间调用 `PdxSerializer.toData(..) ` 方法时,使用 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)` 方法中,`MappingPdxSerializer` 不会尝试为该属性设置值,尤其是在 PDX 序列化字节中存在值的情况下,也不会尝试填充 `ApplicationDomainType` 的实例。

这在您可能返回某个实体类型的视图或投影并且您只想设置可写状态的情况下非常有用。也许实体的视图或投影是基于授权或其他条件的。关键是,您可以根据应用程序的用例和需求充分利用此功能。如果您希望始终写入字段或属性,只需定义一个 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() {
    ...
  }

  ...
}

`Process` 的 `id` 字段和可读的 `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 方法(包括:and(:Predicate)or(:Predicate)negate())来组合 `Predicates`。

以下示例显示了 `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`,这些 `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. Pivotal GemFire 的 Spring Data 存储库

Pivotal GemFire 的 Spring Data 提供了对使用 Spring Data 存储库抽象将实体轻松持久化到 Pivotal GemFire 以及执行查询的支持。此处 提供了关于存储库编程模型的概述。

10.1. Spring XML 配置

要引导 Spring Data 存储库,请使用 Pivotal GemFire Data 命名空间中的 `<repositories/>` 元素,如下例所示

示例 7. 在 XML 中引导 Pivotal GemFire 的 Spring Data 存储库
<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 支持。

除非您已将应用程序域类正确映射到配置的区域,否则引导过程将失败。

10.2. Spring 基于 Java 的配置

或者,许多开发人员更喜欢使用 Spring 的 基于 Java 的容器配置

使用这种方法,您可以使用 SDG 的 `@EnableGemfireRepositories` 注解来引导 Spring Data 存储库,如下例所示

示例 8. 使用 `@EnableGemfireRepositories` 引导 Pivotal GemFire 的 Spring Data 存储库
@SpringBootApplication
@EnableGemfireRepositories(basePackages = "com.example.acme.repository")
class SpringDataApplication {
  ...
}

您可以选择使用类型安全的 `basePackageClasses` 属性,而不是使用 `basePackages` 属性。`basePackageClasses` 允许您通过仅指定一个应用程序存储库接口类型来指定包含所有应用程序存储库类的包。考虑在每个包中创建一个特殊的无操作标记类或接口,其作用除了标识此属性引用的应用程序存储库的位置外别无其他。

除了 `basePackages` 和 `basePackageClasses` 属性外,与 Spring 的 @ComponentScan 注解一样,`@EnableGemfireRepositories` 注解基于 Spring 的 ComponentScan.Filter 类型提供包含和排除过滤器。您可以使用 `filterType` 属性根据不同的方面进行过滤,例如应用程序存储库类型是否用特定注解进行注解或扩展特定类类型等等。有关更多详细信息,请参见 FilterType Javadoc

`@EnableGemfireRepositories` 注解还允许您使用 `namedQueriesLocation` 属性指定命名 OQL 查询的位置,这些查询位于 Java `Properties` 文件中。属性名称必须与存储库查询方法的名称匹配,属性值是要在调用存储库查询方法时执行的 OQL 查询。

如果您的应用程序需要一个或多个 自定义存储库实现,则可以将 `repositoryImplementationPostfix` 属性设置为替代值(默认为 `Impl`)。此功能通常用于扩展 Spring Data 存储库基础结构以实现数据存储(例如 SDG)未提供的功能。

在 Pivotal GemFire 中需要自定义存储库实现的一个示例是在执行联接时。SDG 存储库不支持联接。对于 Pivotal GemFire `PARTITION` 区域,必须在 collocated `PARTITION` 区域上执行联接,因为 Pivotal GemFire 不支持“分布式”联接。此外,Equi-Join OQL 查询必须在 Pivotal GemFire 函数内执行。此处 提供了有关 Pivotal GemFire *Equi-Join 查询* 的更多详细信息。

SDG 的存储库基础结构扩展的许多其他方面也可以自定义。有关所有配置设置的更多详细信息,请参见 @EnableGemfireRepositories Javadoc。

10.3. 执行 OQL 查询

Pivotal GemFire 的 Spring Data 存储库允许定义查询方法,以轻松地对托管实体映射到的区域执行 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<String> x)

x.firstname IN SET $1

NotIn

findByFirstnameNotIn(Collection<String> 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 的 Repository 基础架构不支持的扩展。

Spring Data Commons 的 Repository 基础架构的目标之一是充当最低公分母,以保持对当今应用程序开发中可用和使用的最广泛的各种数据存储的支持和可移植性。从技术上讲,这意味着开发人员可以通过重用其现有的应用程序特定 Repository 接口来访问其应用程序中 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

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 查询特定的调试。

NA

例如,假设您有一个Customers应用程序域类和相应的 Pivotal GemFire Region,以及一个CustomerRepository和一个按姓氏查找Customers的查询方法,如下所示

示例 10. 示例 Customers Repository
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接口,这在声明和注册多个QueryPostProcessors并在 Spring 容器中使用它们来为一组生成的查询方法查询创建处理管道时非常有用。

最后,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接口方法生成的查询。

例如,您可能希望记录来自所有应用程序 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标记接口,因此,它记录所有应用程序 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

您可能希望为 Repository 查询方法定义的特定查询创建一个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始终在其他可能已在 SpringApplicationContext中声明和注册的应用程序定义的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应用的后处理效果。

您可以在 SpringApplicationContext中定义任意数量的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 协议一样,远程函数的实现和执行需要一些样板代码。

Pivotal GemFire 的 Spring Data,忠于 Spring 的核心价值主张,旨在隐藏远程函数执行的机制,让您专注于核心 POJO 编程和业务逻辑。为此,Pivotal GemFire 的 Spring Data 引入了注解,用于将 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实现,用于将结果发送回客户端。此外,如果函数在区域上执行,FunctionContext实际上是RegionFunctionContext的实例,它提供其他信息,例如调用函数的目标区域、与Execution关联的任何过滤器(一组特定键)等等。如果区域是PARTITION区域,则函数应使用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 函数类是 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属性,该属性指定执行函数所需的权限。默认情况下,所有函数都需要DATA:WRITE权限。该属性接受字符串数组,允许您根据应用程序和/或函数 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可以是区域的名称或{data-store-javadoc}/org/apache/geode/security/ResourcePermission.Target.html[ResourcePermission.Target]枚举值之一。最后,如果指定,Key是在Target区域中的有效键。

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. 函数执行

调用远程函数的进程需要提供函数的 ID、调用参数、执行目标(onRegiononServersonServeronMemberonMembers)和(可选)过滤器集。使用 Pivotal GemFire 的 Spring Data,您只需定义一个受注解支持的接口即可。Spring 为接口创建动态代理,该代理使用FunctionService创建Execution,调用Execution,并在(必要时)将结果强制转换为定义的返回类型。此技术类似于 Pivotal GemFire 的 Spring Data 的 Repository 扩展的工作方式。因此,某些配置和概念应该很熟悉。

通常,单个接口定义映射到多个函数执行,每个对应于接口中定义的每个方法。

11.3.1. 函数执行的注解

为了支持客户端函数执行,提供了以下 SDG 函数注解:@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);

}

默认情况下,函数 ID 是简单的(非限定)方法名称。可以使用@FunctionId注解将此调用绑定到不同的函数 ID。

11.3.2. 启用注解处理

客户端使用 Spring 的类路径组件扫描功能来发现带注解的接口。要在 XML 中启用函数执行注解处理,请在您的 XML 配置中插入以下元素:

<gfe-data:function-executions base-package="org.example.myapp.gemfire.functions"/>

function-executions 元素位于 gfe-data XML 命名空间中。base-package 属性是必需的,以避免扫描整个类路径。可以提供其他过滤器,如 Spring 参考文档中所述。

或者,您可以按如下方式为您的 Java 配置类添加注解:

@EnableGemfireFunctionExecutions(basePackages = "org.example.myapp.gemfire.functions")

11.4. 编程方式的函数执行

使用上一节中定义的函数执行带注解的接口,只需将您的接口自动装配到将调用函数的应用程序 Bean 中即可。

@Component
public class MyApplication {

    @Autowired
    FunctionExecution functionExecution;

    public void doSomething() {
         functionExecution.doIt("hello", 123);
    }
}

或者,您可以直接使用函数执行模板。在以下示例中,GemfireOnRegionFunctionTemplate 创建一个 onRegion 函数Execution

示例 19. 使用GemfireOnRegionFunctionTemplate
Set<?, ?> myFilter = getFilter();
Region<?, ?> myRegion = getRegion();
GemfireOnRegionOperations template = new GemfireOnRegionFunctionTemplate(myRegion);
String result = template.executeAndExtract("someFunction", myFilter, "hello", "world", 1234);

在内部,函数Executions 始终返回一个ListexecuteAndExtract 假设一个包含结果的单例List,并尝试将该值强制转换为请求的类型。还有一个execute方法按原样返回List。第一个参数是函数 ID。过滤器参数是可选的。其余参数是可变参数List

11.5. 使用 PDX 的函数执行

当使用 Spring Data for Pivotal GemFire 的函数注解支持结合 Pivotal GemFire 的 PDX 序列化 时,需要注意一些逻辑方面的问题。

如本节前面所述,并且举例说明,您通常应该通过使用带有 Spring Data for Pivotal GemFire 函数注解 的 POJO 类来定义 Pivotal GemFire 函数,如下所示:

public class OrderFunctions {

  @GemfireFunction(...)
  Order process(@RegionData data, Order order, OrderSource orderSourceEnum, Integer count) { ... }

}
Integer类型的count参数是任意的,Order类和OrderSource枚举的分离也是任意的,这在逻辑上可能是可以合并的。但是,参数的设置方式是为了演示在 PDX 上下文中函数执行的问题。

您的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
  ...
}

当然,您可以定义一个函数Execution接口来调用“process”Pivotal GemFire 服务器函数,如下所示:

@OnServer
public interface OrderProcessingFunctions {
  Order process(Order order, OrderSource orderSourceEnum, Integer count);
}

显然,此process(..)Order函数是从客户端使用ClientCache实例(即<gfe:client-cache/>)调用的。这意味着函数参数也必须是可序列化的。当在集群中的对等体之间调用对等体到对等体成员函数(例如@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"/>

这样做会导致从缓存(即区域)读取的所有值以及客户端和服务器(或对等体)之间传递的信息保持序列化形式,包括但不限于函数参数。

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 类型,无论它们是显式配置的(即,使用ReflectionBasedAutoSerializer注册,使用正则表达式模式和classes参数,还是由“自定义”Pivotal GemFire PdxSerializer处理),尽管 Java 枚举实现了java.io.Serializable

因此,当您在注册了 Pivotal GemFire 函数(包括 Spring Data for Pivotal GemFire 函数带注解的 POJO 类)的 Pivotal GemFire 服务器上将pdx-read-serialized设置为true时,调用函数Execution时可能会遇到意外的行为。

调用函数时,您可能会传递以下参数:

orderProcessingFunctions.process(new Order(123, customer, LocalDateTime.now(), items), OrderSource.ONLINE, 400);

但是,服务器上的 Pivotal GemFire 函数得到以下内容:

process(regionData, order:PdxInstance, :PdxInstanceEnum, 400);

OrderOrderSource已作为 PDX 实例 传递给函数。同样,所有这些都是因为pdx-read-serialized设置为true,这在 Pivotal GemFire 服务器与多个不同的客户端(例如,Java 客户端和本机客户端,如 C/C++、C# 等的组合)交互的情况下可能是必要的。

这与 Spring Data for Pivotal GemFire 的强类型函数带注解的 POJO 类方法签名相冲突,在该签名中,您会合理地期望应用程序域对象类型,而不是 PDX 序列化实例。

因此,Spring Data for Pivotal GemFire 包含增强的函数支持,以自动将 PDX 类型的方法参数转换为函数方法签名(参数类型)定义的所需应用程序域对象类型。

但是,这也要求您在注册并使用了 Spring Data for Pivotal GemFire 函数带注解的 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来更精细地控制您的序列化策略。

最后,如果对函数参数进行泛型处理或作为 Pivotal GemFire 的 PDX 类型之一进行处理,Spring Data for Pivotal GemFire 会谨慎地不转换函数参数,如下所示:

@GemfireFunction
public Object genericFunction(String value, Object domainObject, PdxInstanceEnum enum) {
 ...
}

当且仅当相应的应用程序域类型位于类路径上并且函数带注解的 POJO 方法期望它时,Spring Data for Pivotal GemFire 才会将 PDX 类型的数据转换为相应的应用程序域类型。

有关自定义的、组合的应用程序特定 Pivotal GemFire PdxSerializers以及基于方法签名的适当 POJO 函数参数类型处理的良好示例,请参阅 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 集成和支持有一些限制。

首先,只能在 Pivotal GemFire PARTITION 区域上创建LuceneIndex

其次,所有LuceneIndexes都必须在应用LuceneIndex的区域之前创建。

为了确保在 Spring 容器中定义的所有声明的LuceneIndexes都在其应用的区域之前创建,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 也直接引用区域(而不是仅仅是区域路径)的原因。

当您想在应用程序生命周期的后期以及根据需求在具有数据的现有区域上定义LuceneIndex时,这更理想。在可能的情况下,SDG 努力遵守强类型对象。但是,目前,您必须使用regionPath属性来指定应用LuceneIndex的区域。

此外,在上例中,请注意Books区域 Bean 定义上存在 Spring 的@DependsOn注解。这会在Books区域 Bean 和bookTitleIndexLuceneIndex Bean 定义之间创建一个依赖关系,确保在应用LuceneIndex的区域之前创建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);

或者,您可以查询类型为 CustomerPage,如下所示

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 基础结构并提供自定义的 ProjectionFactory,该自定义 ProjectionFactory 使用 CGLIB 生成代理类作为投影实体。

您可以使用 setProjectionFactory(:ProjectionFactory) 在 Lucene 模板上设置自定义 ProjectionFactory

12.2. 注解配置支持

最后,Spring Data for Pivotal GemFire 提供了对 LuceneIndexes 的注解配置支持。

最终,SDG Lucene 支持将进入 Pivotal GemFire 的存储库基础结构扩展,以便可以像今天的 OQL 支持 一样,在应用程序 Repository 接口上将 Lucene 查询表示为方法。

但是,在此期间,如果您想方便地表达 LuceneIndexes,可以直接在您的应用程序领域对象上这样做,如下例所示

@PartitionRegion("People")
class Person {

    Gender gender;

    @Index
    LocalDate birthDate;

    String firstName;

    @LuceneIndex;
    String lastName;

    ...
}

要启用此功能,必须专门使用 @EnableEntityDefineRegions@EnableIndexing 注解与 SDG 的注解配置支持一起使用,如下所示

@PeerCacheApplication
@EnableEntityDefinedRegions
@EnableIndexing
class ApplicationConfiguration {

  ...
}
LuceneIndexes 只能在 Pivotal GemFire 服务器上创建,因为 LuceneIndexes 仅适用于 PARTITION 区域。

鉴于我们前面对 Person 类的定义,SDG 注解配置支持找到 Person 实体类定义,并确定人员存储在名为“People”的 PARTITION 区域中,并且 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 主类。

Pivotal GemFire 不是应用程序服务器。此外,在 Pivotal GemFire 缓存配置方面,使用此方法存在一些限制。

13.1. 使用 Pivotal GemFire 引导使用 Gfsh 启动的 Spring 上下文

为了在使用 *Gfsh* 启动 Pivotal GemFire 服务器时在 Pivotal GemFire 中引导 Spring ApplicationContext,必须使用 Pivotal GemFire 的 初始化器 功能。初始化器块可以声明一个应用程序回调,该回调在 Pivotal GemFire 初始化缓存后启动。

初始化器在 initializer 元素内声明,使用 Pivotal GemFire 原生 cache.xml 的最小代码段。为了引导 Spring ApplicationContext,需要一个 cache.xml 文件,这与需要使用 Spring XML 配置的最小代码段来引导使用组件扫描配置的 Spring ApplicationContext(例如 <context:component-scan base-packages="…​"/>)的方式类似。

幸运的是,框架已经方便地提供了这样一个初始化器: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 类类似的约定,后者用于在 Web 应用程序中引导 Spring ApplicationContext,其中 ApplicationContext 配置文件使用 contextConfigLocations Servlet 上下文参数指定。

此外,SpringContextBootstrappingInitializer 类还可以与 basePackages 参数一起使用,以指定包含适当注释的应用程序组件的以逗号分隔的基包列表。Spring 容器搜索这些组件以查找和创建类路径中的 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>

然后,使用正确配置和构造的 CLASSPATHcache.xml 文件(如前所示)作为在 *Gfsh* 中启动 Pivotal GemFire 服务器时的命令行选项指定,命令行将如下所示

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 已经提供了对自动连接 Pivotal GemFire 组件(例如 CacheListenersCacheLoadersCacheWriters 等)的支持,这些组件由 Pivotal GemFire 在 cache.xml 中声明和创建,方法是使用 SDG 的 WiringDeclarableSupport 类,如 使用自动连接和注释进行配置 中所述。但是,这仅在 Spring 执行引导时才有效(即,当 Spring 引导 Pivotal GemFire 时)。

当您的 Spring ApplicationContext 由 Pivotal GemFire 引导时,这些 Pivotal GemFire 应用程序组件会被忽略,因为 Spring ApplicationContext 尚未存在。Spring ApplicationContext 直到 Pivotal GemFire 调用初始化器块才会创建,而初始化器块仅在所有其他 Pivotal GemFire 组件(缓存、区域等)已创建和初始化后才会发生。

为了解决这个问题,引入了一个新的 LazyWiringDeclarableSupport 类。这个新类知道 Spring ApplicationContext。此抽象基类的目的是,任何实现类都注册自身以由 Spring 容器配置,该容器最终由 Pivotal GemFire 在调用初始化器后创建。从本质上讲,这使您的 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 中定义区域和 CacheListener 组件。CacheLoader 可能需要访问应用程序存储库(或者可能是在 Spring ApplicationContext 中定义的 JDBC DataSource),以便在启动时将 Users 加载到 Pivotal GemFire REPLICATE 区域中。

警告

以这种方式混合 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 示例 存储库中。

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启动。如果类路径设置正确,也可以直接在生成的构件上使用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

可以同时运行多个实例。启动后,新的虚拟机将自动看到现有的区域及其信息,如下例所示

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 组件

缓存配置定义了 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

定义搜索包含外部定义查询的属性文件的位置。

consider-nested-repositories

是否应考虑嵌套的存储库接口定义。默认为false

附录 B:填充器命名空间参考

<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

表示无返回值。

基本类型

Java基本类型。

包装器类型

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 SeqListMapSet

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:适用于 Pivotal GemFire 的 Spring Data 架构


1.参见 [repositories.create-instances.spring]