© 2010-2019 原始作者。

您可以为自己使用或分发给他人制作本文档的副本,但条件是您不得就此类副本收取任何费用,并且每个副本无论以印刷形式还是电子形式分发,都必须包含此版权声明。

前言

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

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

尽管已尽一切努力确保本文档全面完整且没有错误,但某些主题超出了本文档的范围,可能需要更多解释(例如,使用带高可用性的分区进行数据分发管理,同时仍保持一致性)。此外,可能已混入一些排版错误。如果您确实发现了错误,甚至更严重的错误,请通过在 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 Gateway 支持。请参阅 配置 WAN 网关

  • 使用专用的 SDG XML 命名空间 gfe-data 支持 Spring Data Repository。更多信息请参阅 Spring Data for Pivotal GemFire Repository

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

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

    <*-region> 元素不再允许嵌套的 <disk-store> 元素。
  • Pivotal GemFire Subregion 由嵌套的 <*-region> 元素支持。

  • 已添加一个 <local-region> 元素用于配置本地 Region。

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

3.2. 1.3 版本的新特性

  • 升级到 Spring Framework 3.2.8。

  • 升级到 Spring Data Commons 1.7.1。

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

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

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

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

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

  • 支持函数执行的 void 返回值。更多详细信息请参阅 注解支持函数执行

  • 支持持久化本地 Region。请参阅 本地 Region

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

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

  • 通过 SDG 的 gfe XML 命名空间支持所有 Cache Region 定义上的 concurrency-checks-enabled。请参阅 [bootstrap:region:common:attributes]

  • 支持客户端本地 Region 上的 CacheLoadersCacheWriters

  • 支持在 Pivotal GemFire Cache Subregion 上注册 CacheListenersAsyncEventQueuesGatewaySenders

  • 支持 Region 中的 PDX 持久化键。

  • 支持在使用 colocated-with 属性指定搭配时,在 Spring 上下文中正确创建分区 Region bean。

  • 使用 SDG gfe XML 命名空间中恰当的嵌套 <*-region> 元素语法全面支持 Cache Subregion。

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 Cache 事务。

  • 添加了对在使用 Gfsh 启动时,在 Pivotal GemFire Server 中引导 Spring ApplicationContext 的支持。请参阅 在 Pivotal GemFire 中引导 Spring ApplicationContext

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

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

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

  • 更改了 SDG <*-region> 元素的默认行为,从查找变为总是创建新的 Region,并提供了一个使用 ignore-if-exists 属性恢复旧行为的选项。请参阅 常见 Region 属性[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 命名空间 Region 类型元素中指定的所有默认值,转而依赖于 Pivotal GemFire 的默认值。

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

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

  • 使 Pivotal GemFire GatewayReceivers 能够手动启动。

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

  • 添加了对 Region 模板的支持。请参阅 [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 Region 数据压缩的支持。

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

  • 支持添加 EventSubstitutionFiltersGatewaySenders 的功能。

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 的集群配置服务配置的现有 Region 上添加 Spring 定义的 CacheListenersCacheLoadersCacheWriters 的支持。

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

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

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

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

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

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

  • 基于注解的数据过期

  • [gemfire-repositories:oql-extensions]

  • 添加了对 Cache 和 Region 数据快照的支持。请参阅 配置快照服务

3.7. 1.8 版本的新特性

  • 升级到 Pivotal GemFire 8.2.0。

  • 升级到 Spring Framework 4.2.9.RELEASE。

  • 升级到 Spring Data Commons 1.12.11.RELEASE。

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

  • 添加了对 CDI 的支持。

  • 启用 ClientCache 无需配置 Pool。

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

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

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

  • 阻止 SDG 定义的 Pool 在使用这些 Pool 的 Region 之前被销毁。

  • 处理定义为 Repository 查询方法的不区分大小写的 Pivotal GemFire OQL 查询。

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

  • 修复了在关联到配置用于 Pivotal GemFire 服务器组的特定 Pool 的客户端 Region 上进行 Repository 查询时出现的 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 中添加了对挂起和恢复的支持。

  • 在 Repository 中添加了对使用 bean id 属性作为 Region 键的支持,前提是 @Id 注解不存在。

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

  • 使 GemfireCacheManager 能够显式列出要在 Spring 的缓存抽象中使用的 Region 名称。

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

  • 添加了对类路径上多个 Spring Data 模块的 Repository 支持。

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 Repository 抽象中。

  • 更改了 Repository 接口和抽象,例如 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。

  • 添加了对并行 Cache/Region 快照的支持,以及在加载快照时调用回调。

  • 添加了对注册 QueryPostProcessors 的支持,用于自定义从 Repository 查询方法生成的 OQL。

  • 在 o.s.d.g.mapping.MappingPdxSerializer 中添加了对 include/exclude TypeFilters 的支持。

  • 更新了文档。

参考指南

4. 文档结构

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

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

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

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

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

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

  • 注解支持函数执行 描述了如何创建和使用 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 提供对 Pivotal GemFire 内存数据网格 (IMDG) 的完整配置和初始化,使用 Spring IoC 容器。该框架包括几个类,旨在帮助简化 Pivotal GemFire 组件的配置,包括:Caches、Regions、Indexes、DiskStores、Functions、WAN Gateways、持久性备份以及其他几个分布式系统组件,以少量工作支持各种应用程序用例。

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

5.1. 相较于 Pivotal GemFire 的 cache.xml,使用 Spring 的优势

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

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

5.2. 使用核心命名空间

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

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

要使用 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 会在本地解析 Schema,因为它包含在 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 Repository 和函数执行的支持,以及一个 <datasource> 标签,提供一种方便的方式连接到 Pivotal GemFire 集群。

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

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

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

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

当然,您也可以显式配置客户端 Region。例如,如果您想将数据缓存到本地内存中,如下例所示:

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

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

5.4. 配置 Cache

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

本节介绍如何创建和配置 peer Cache 成员,这适用于对等 (P2P) 拓扑和 cache 服务器。Cache 成员也可以用于独立应用程序和集成测试。然而,在典型的生产系统中,大多数应用程序进程充当 cache 客户端,创建 ClientCache 实例。这将在配置 Pivotal GemFire ClientCache客户端 Region 部分中介绍。

可以通过以下简单声明创建一个使用默认配置的 peer Cache

<gfe:cache/>

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

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

<gfe:cache id="myCache"/>

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

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

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

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

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

例如,您可以使用 util 命名空间中定义的 properties 元素直接定义 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>

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

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

5.4.1. 高级 Cache 配置

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

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

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

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

启用自动重新连接

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

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

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

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

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

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

不幸的是,目前无法收到断开连接事件的通知,也无法收到后续的重新连接事件通知。如果可以,那么您就能清楚地知道何时调用 ConfigurableApplicationContext.refresh()(如果应用程序适用的话),这就是不推荐在 peer Cache 应用程序中使用 Pivotal GemFire 的此“功能”的原因。

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

使用基于集群的配置

Pivotal GemFire 的 Cluster Configuration Service 是任何加入集群的 peer 成员通过使用 Locator 维护的共享持久化配置获取集群“一致视图”的便捷方式。使用基于集群的配置可确保 peer 成员加入时其配置与 Pivotal GemFire 分布式系统兼容。

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

所有 Pivotal GemFire 原生配置元数据,无论是来自 cache.xml 还是 Cluster Configuration Service,都会在任何 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 的 Cluster Configuration Service 的更多信息,请参阅产品文档

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 容器完全初始化之后启动。这样做可以确保声明式定义的潜在 Region、listeners、writers 或 instantiators 在服务器开始接受连接之前完全初始化和注册。在以编程方式配置这些元素时请记住这一点,因为服务器可能在您的组件之前启动,从而不会立即被连接的客户端看到。

5.4.3. 配置 Pivotal GemFire ClientCache

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

以下是使用默认配置的 Pivotal GemFire cache 客户端的最简单定义:

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

client-cache 支持许多与Cache 元素相同的选项。然而,与成熟的 peer Cache 成员不同,cache 客户端通过 Pool 连接到远程 cache 服务器。默认情况下,会创建一个 Pool 以连接到在 localhost 上运行并监听端口 40404 的服务器。所有客户端 Region 都使用默认 Pool,除非该 Region 配置为使用特定的 Pool。

可以使用 pool 元素定义 Pools。此客户端 Pool 可用于配置直接连接到服务器以进行单个实体访问,或通过一个或多个 Locators 连接到整个 cache。

例如,要自定义 client-cache 使用的默认 Pool,开发人员需要定义一个 Pool 并将其连接到 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,客户端 cache 初始化将包含对 ClientCache.readyForEvents() 的调用。

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

Pivotal GemFire 的 DEFAULT Pool 和 Spring Data for Pivotal GemFire Pool 定义

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

<gfe:client-cache/>

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

在这种情况下,“Example” Region 是 LOCAL 的,客户端与服务器之间没有数据分发。因此,不需要 Pool。Pivotal GemFire 的 ClientRegionShortcut(所有 LOCAL_* 快捷方式)定义的任何客户端仅本地 Region 都是如此。

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

当同时定义了 ClientCache、Pool 和基于代理的 Region 但未明确标识时,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,Pool 标识为 gemfirePool,客户端 Region 标识为“Example”。然而,ClientCache 会从 gemfirePool 初始化 Pivotal GemFire 的 DEFAULT Pool,并且客户端 Region 在客户端和服务器之间分发数据时使用 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 的 Pool。Spring Data for Pivotal GemFire 会导致 DEFAULT Pool 从 gemfirePool 初始化。这在定义了多个 Pool 且客户端 Region 使用不同的 Pool 或根本没有声明 Pool 的情况下很有用。

考虑以下情况:

<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-cacheDEFAULT pool 从 locatorPool 初始化,如 pool-name 属性所指定。由于两个 Pool 都已显式标识(命名)—— 分别是 locatorPoolserverPool,因此没有 Spring Data for Pivotal GemFire 定义的 gemfirePool

“Example” Region 显式引用并独占使用 serverPoolAnotherExample Region 使用 Pivotal GemFire 的 DEFAULT Pool,该 Pool 同样根据 client cache bean 定义的 pool-name 属性从 locatorPool 配置而来。

最后,YetAnotherExample Region 不使用 Pool,因为它标记为 LOCAL

AnotherExample Region 会首先查找名为 gemfirePool 的 Pool bean,但这需要定义一个匿名 Pool bean(即 <gfe:pool/>)或一个显式命名为 gemfirePool 的 Pool bean(例如,<gfe:pool id="gemfirePool"/>)。
如果我们将 locatorPool 的名称更改为 gemfirePool,或者使 Pool bean 定义成为匿名的,其效果将与上述配置相同。

5.5. 配置 Region

需要 Region 来存储和检索 cache 中的数据。org.apache.geode.cache.Region 是一个扩展 java.util.Map 的接口,支持使用熟悉的 key-value 语义进行基本数据访问。Region 接口被注入到需要它的应用程序类中,从而使实际的 Region 类型与编程模型解耦。通常,每个 Region 与一个领域对象关联,类似于关系数据库中的表。

Pivotal GemFire 实现了以下类型的 Region:

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

  • PARTITION - 数据在集群中定义该 Region 的许多 cache 成员之间划分成 bucket(分片)。这提供了高读写性能,适用于单个节点无法容纳的大型数据集。

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

  • Client - 技术上讲,客户端 Region 是一个 LOCAL Region,它充当集群中 cache 服务器上托管的 REPLICATE 或 PARTITION Region 的 PROXY。它可以持有本地创建或获取的数据,也可以是空的。本地更新会同步到 cache 服务器。此外,客户端 Region 可以订阅事件,以便与访问同一服务器 Region 的远程进程产生的更改保持同步。

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

5.5.1. 使用外部配置的 Region

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

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

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

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

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

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

lookup-region 允许您检索现有、预配置的 Regions,而无需暴露 Region 的语义或设置基础设施。

5.5.2. 自动 Region 查找

当您在 <gfe:cache> 元素上使用 cache-xml-location 属性时,auto-region-lookup 允许您将 Pivotal GemFire 原生 cache.xml 文件中定义的所有 Regions 导入到 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"/>)将特定 Regions 作为 bean 在 Spring 容器中引用,或者您可以通过使用以下方式选择导入 cache.xml 中定义的所有 Regions:

<gfe:auto-region-lookup/>

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

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

您可以像注入 Spring ApplicationContext 中定义的任何其他 bean 一样注入这些“自动查找”的 Regions,但有一个例外:您可能需要定义与“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 和 cache.xml 中定义的所有 Regions 在任何具有自动装配引用的组件之前创建。

5.5.3. 配置 Regions

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

  • LOCAL Region: <local-region>

  • PARTITION Region: <partitioned-region>

  • REPLICATE Region: <replicated-region>

  • 客户端 Region: <client-region>

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

通用 Region 属性

下表列出了所有 Region 类型可用的属性:

表 1. 通用 Region 属性
名称 描述

cache-ref

Pivotal GemFire Cache bean 引用

定义 Pivotal GemFire Cache 的 bean 名称(默认情况下为 'gemfireCache')。

cloning-enabled

boolean (默认: false)

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

close

boolean (默认: false)

确定在关闭时是否应关闭 region。

concurrency-checks-enabled

boolean (默认: true)

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

data-policy

请参阅 Pivotal GemFire 的数据策略

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

destroy

boolean (默认: false)

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

disk-store-ref

配置的 disk store 的名称。

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

disk-synchronous

boolean (默认: true)

确定 disk store 写入是否同步。

id

任何有效的 bean 名称。

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

ignore-if-exists

boolean (默认: false)

如果 region 已存在于 cache 中,则忽略此 bean 定义,改为进行查找。

ignore-jta

boolean (默认: false)

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

index-update-type

synchronousasynchronous (默认: synchronous)

确定在创建 entry 时 Indices 是同步还是异步更新。

initial-capacity

integer (默认: 16)

Region entry 数量的初始内存分配。

key-constraint

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

预期的 key 类型。

load-factor

float (默认: .75)

在用于存储 region entry 的底层 java.util.ConcurrentHashMap 上设置初始参数。

name

任何有效的 region 名称。

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

persistent

*boolean (默认: false)

确定 region 是否将 entry 持久化到本地磁盘(disk store)。

shortcut

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

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

statistics

boolean (默认: false)

确定 region 是否报告统计信息。

template

region template 的名称。

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

value-constraint

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

预期的 value 类型。

CacheListener 实例

CacheListener 实例在 Region 上注册,用于处理 Region 事件,例如 entry 的创建、更新、销毁等。CacheListener 可以是实现 CacheListener 接口的任何 bean。Region 可以有多个监听器,使用嵌套在包含的 *-region 元素中的 cache-listener 元素声明。

以下示例声明了两个 CacheListener’s。第一个引用命名的高级 Spring bean。第二个是匿名内部 bean 定义。

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

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

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

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

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

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

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

CacheLoaders 和 CacheWriters

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

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

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

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

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

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

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

5.5.4. 压缩

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

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

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

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

5.5.5. Off-Heap

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

使用 off-heap 内存非常简单,只需声明要使用的内存量,然后启用您的 Regions 使用 off-heap 内存,如下配置所示:

<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 配置属性来控制 off-heap 内存管理的其它方面:

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

Pivotal GemFire 的 ResourceManager 将使用这两个阈值(critical-off-heap-percentageeviction-off-heap-percentage)更有效地管理 off-heap 内存,方式与 JVM 管理 heap 内存非常相似。Pivotal GemFire ResourceManager 将通过逐出旧数据来防止 cache 消耗过多的 off-heap 内存。如果 off-heap 管理器无法跟上,则 ResourceManager 会拒绝向 cache 添加数据,直到 off-heap 内存管理器释放了足够的内存。

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

具体来说,请阅读管理 Off-Heap 内存一节。

5.5.6. Subregions

Spring Data for Pivotal GemFire 也支持 Sub-Regions,允许 Region 以分层关系排列。

例如,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>

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

<gfe:replicated-region-template>

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

<gfe:client-region-template>

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

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

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

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

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

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

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

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

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

模板化如何工作

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

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

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

此行为已更改,现在默认行为是先创建区域。如果区域已经存在,则创建逻辑会快速失败 (fail-fast),并抛出相应的异常。然而,很像 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 命名空间元素仅严格用于定义新的区域。当这些元素定义的区域已经存在且区域元素首先执行查找时,可能会出现一个问题:如果您在应用配置中定义了不同的区域语义和行为,例如逐出、过期、订阅等,则区域定义可能不匹配,并表现出与应用要求的行为相反的行为。更糟糕的是,您可能希望将区域定义为分布式区域(例如 PARTITION),而实际上现有区域定义是本地的。
推荐做法 - 仅使用 replicated-regionpartitioned-regionlocal-regionclient-region XML 命名空间元素来定义新的区域。

考虑以下 Pivotal GemFire 原生 cache.xml 配置文件:

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

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

</cache>

此外,考虑您可能已定义了一个应用 DAO 如下:

public class CustomerAccountDao extends GemDaoSupport {

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

    ...
}

在这里,我们将 Customers/Accounts 区域的引用注入到我们的应用 DAO 中。因此,开发者在 Spring XML 配置元数据中为这些区域中的部分或全部定义 Bean 是很常见的,如下所示:

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

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

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

</beans>

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

考虑以下更改配置元数据语法以使用嵌套格式的错误示例:

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

现在考虑另一个使用顶级 replicated-region 元素并将 ignore-if-exists 属性设置为先执行查找的错误示例:

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

在 Spring ApplicationContext 中定义的区域 Bean 包含以下内容:{ "Customers", "/Customers/Accounts", "/Customers/Accounts/Orders" }. 这意味着前面示例中所示的依赖注入引用(即 @Resource(name = "Customers/Accounts"))现在已损坏,因为实际上没有定义名称为 Customers/Accounts 的 Bean。因此,您不应按前面两个示例所示的方式配置区域。

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

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

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

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

5.5.8. 数据逐出(带溢出)

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

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

例如,要配置一个分区区域在内存大小超过 512 MB 时溢出到磁盘,您可以指定以下配置:

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

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

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

5.5.9. 数据过期

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

Pivotal GemFire 支持以下过期类型:

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

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

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

以下示例展示了一个设置了过期值的分区区域 (PARTITION Region):

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

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

基于注解的数据过期

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

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

您还可以通过使用 @IdleTimeoutExpiration 注解用于空闲超时 (TTI) 过期,使用 @TimeToLiveExpiration 注解用于存活时间 (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 互不覆盖。相反,当配置了不同的区域条目过期策略(例如 TTL 和 TTI)时,它们相互补充。

所有基于 @Expiration 的注解仅适用于区域条目值。Spring Data for Pivotal GemFire 的过期注解支持不涵盖区域的过期。然而,Pivotal GemFire 和 Spring Data for Pivotal GemFire 确实允许您使用 SDG XML 命名空间设置区域过期,如下所示:

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

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

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

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

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

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

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

  2. (可选)使用 Spring Data for Pivotal GemFire 的 @Expiration 注解之一(@Expiration@IdleTimeoutExpiration@TimeToLiveExpiration)标记存储在区域中的应用域对象,以定义过期策略和自定义设置。

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

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

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

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

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

嗯,这就引出了 Spring Data for Pivotal GemFire 的另一项特性,该特性利用了 Spring 的核心基础设施来提供配置便利:属性占位符和 Spring 表达式语言 (SpEL) 表达式。

例如,开发者可以使用属性占位符在 @Expiration 注解属性中同时指定过期 'timeout' 和 'action',如下面的示例所示:

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

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

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

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

当多个应用域对象可能共享相似的过期策略时,以及当您希望外部化配置时,这都很方便。

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

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

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

然后,在您的应用域对象上,您可以按如下方式定义超时和动作:

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

您可以想象,'expirationSettings' Bean 可以是一个比简单的 java.util.Properties 实例更有趣和有用的对象。在前面的示例中,properties 元素(expirationSettings)使用 SpEL 将 action 值基于实际的 ExpirationAction 枚举类型,如果枚举类型发生变化,这会迅速导致失败的识别。

作为一个示例,所有这些内容都在 Spring Data for Pivotal GemFire 测试套件中得到了演示和测试。有关更多详细信息,请参阅源代码

5.5.10. 数据持久化

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

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

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

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

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

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

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

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

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

5.5.11. 订阅策略

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

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

5.5.12. 本地区域

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

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

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

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

5.5.13. 复制区域

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

复制区域 (REPLICATE Regions) 可使用所有常见的配置选项。Spring Data for Pivotal GemFire 提供了一个 replicated-region 元素。以下示例显示了一个最小的声明:

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

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

5.5.14. 分区区域

Spring Data for Pivotal GemFire XML 命名空间也支持分区区域 (PARTITION Regions)。

引用 Pivotal GemFire 文档:

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

分区区域 (PARTITION Region) 是通过使用 partitioned-region 元素创建的。其配置选项与 replicated-region 类似,但增加了分区特有的功能,例如冗余副本数量、总最大内存、桶数量、分区解析器等。

以下示例展示了如何设置一个具有两个冗余副本的分区区域 (PARTITION Region):

<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 Regions) 特有的配置选项。这些选项是在前面描述的常见区域配置选项之外的。

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

copies

0..4

每个分区的副本数量,用于高可用性。默认情况下不创建副本,即没有冗余。每个副本提供额外的备份,但需要额外的存储空间。

colocated-with

有效的区域名称

此新创建的分区区域 (PARTITION Region) 所在的共置分区区域 (PARTITION Region) 的名称。

local-max-memory

正整数

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

total-max-memory

任意整数值

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

partition-listener

Bean 名称

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

partition-resolver

Bean 名称

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

recovery-delay

任意 long 值

在另一个成员崩溃后,现有成员等待多长时间(毫秒)来满足冗余。-1(默认值)表示故障后不恢复冗余。

startup-recovery-delay

任意 long 值

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

5.5.15. 客户端区域

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

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

与[_其他区域类型_](https://docs.springjava.cn/spring-data/geode/docs/2.3.0.RELEASE/reference/html/#bootstrap:region:attributes)一样,`client-region` 也支持 `CacheListener` 实例以及 `CacheLoader` 和 `CacheWriter`。它还需要一个连接池 (`Pool`) 来连接一组 Locators 或服务器。每个客户端区域可以有自己的池,也可以共享同一个池。如果未指定池,将使用“DEFAULT”池。

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

有关客户端,尤其是 Pool 的完整选项列表,请参阅 Spring Data for Pivotal GemFire schema(“Spring Data for Pivotal GemFire Schema”)以及 Pivotal GemFire 文档中关于客户端-服务器配置的部分。

客户端关注

为了最小化网络流量,每个客户端可以单独定义自己的“关注”策略,向 Pivotal GemFire 指明它实际需要的数据。在 Spring Data for Pivotal GemFire 中,可以为每个客户端区域单独定义“关注”。支持基于 Key 的关注类型和基于正则表达式的关注类型。

以下示例展示了基于 Key 和基于正则表达式的 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>

一个特殊的 Key ALL_KEYS 意味着对所有 Key 注册了“关注”。使用正则表达式 ".\*" 也可以达到同样的效果。

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

durable 表示当客户端连接到集群中的一个或多个服务器时,为客户端创建的“关注”策略和订阅队列是否跨客户端会话保持。如果客户端断开连接后又重新连接,则客户端断开连接期间,服务器上为该客户端维护一个持久的订阅队列。当客户端重新连接时,它会接收到客户端断开连接期间集群中服务器上发生的任何事件。

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

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 用于控制集群中的服务器应维护订阅队列的多少个副本。如果冗余度大于一,并且“主”订阅队列(即服务器)出现故障,则“备用”订阅队列将接管,从而确保客户端在高可用性(HA)场景下不会丢失事件。

除了 Pool 设置之外,服务器端 Region 还使用一个附加属性 enable-subscription-conflation 来控制发送给客户端的事件的合并。这也有助于进一步最小化网络流量,并且在应用程序只关心条目最新值的情况下很有用。然而,当应用程序保留事件的时间序列时,合并将阻碍该用例。默认值为 false。以下示例显示了服务器上的 Region 配置,客户端包含一个对应的客户端 [CACHING_]PROXY Region,对该服务器 Region 中的键感兴趣。

<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 支持在 Region 中缓存 JSON 文档,并具有使用 Pivotal GemFire OQL(对象查询语言)查询存储的 JSON 文档的能力。JSON 文档内部存储为 PdxInstance 类型,通过使用 JSONFormatter 类执行 JSON 文档(作为 String)之间的转换。

Spring Data for Pivotal GemFire 提供 <gfe-data:json-region-autoproxy/> 元素以启用 AOP 组件来通知适当的、代理的 Region 操作,从而有效地封装了 JSONFormatter,让你的应用程序可以直接处理 JSON 字符串。

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

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

此外,默认情况下,getAll()values() Region 操作的结果会为配置的 Region 进行转换。这是通过在本地内存中创建一个并行数据结构来完成的。这可能会给大型集合带来显著的开销,因此如果你想禁用这些 Region 操作的自动转换,请将 convert-returned-collections 设置为 false

某些 Region 操作(特别是使用 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 允许在 Region 数据上创建索引(有时也称为 indices),以提高 OQL(对象查询语言)查询的性能。

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

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

在 Spring Data for Pivotal GemFire 的 XML Schema(也称为 SDG XML Namespace)中,index bean 声明不像 Pivotal GemFire 原生 cache.xml 那样绑定到 Region。相反,它们是顶级元素,类似于 <gfe:cache> 元素。这让你可以在任何 Region 上声明任意数量的索引,无论它们是刚创建的还是已经存在的——这是对 Pivotal GemFire 原生 cache.xml 格式的显著改进。

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

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

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

@Region("Customers")
class Customer {

  @Id
  Long id;

  String lastName;
  String firstName;

  ...
}

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

interface CustomerRepository extends GemfireRepository<Customer, Long> {

  Customer findByLastName(String lastName);

  ...
}

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

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

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

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

from 子句必须引用一个有效、已存在的 Region,这是 Index 应用于 Region 的方式。这并不是 Spring Data for Pivotal GemFire 特有的功能,而是 Pivotal GemFire 的一项功能。

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

每个枚举值对应于实际创建(或“定义”——关于“定义”索引的更多内容请参阅下一节)Index 时调用的 QueryService create[|Key|Hash]Index 方法之一。例如,如果 IndexTypePRIMARY_KEY,则调用 QueryService.createKeyIndex(..) 来创建 KEY Index

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

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

5.6.1. 定义索引

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

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

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

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

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

5.6.2. IgnoreIfExistsOverride

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

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

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

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

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

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

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

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

IgnoreIfExists 行为

当抛出 IndexExistsExceptionignoreIfExists 设置为 true(或 <gfe:index ignore-if-exists="true">)时,本应由这个 index bean 定义或声明创建的 Index 将被简单地忽略,并返回已存在的 Index

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

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

当抛出 IndexNameConflictExceptionignoreIfExists 设置为 true(或 <gfe:index ignore-if-exists="true">)时,本应由这个 index bean 定义或声明创建的 Index 也被忽略,并再次返回“已存在的” Index,如同抛出 IndexExistsException 时一样。

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

当被忽略的 Index 定义与现有 Index 定义显著不同时,SDG 会尽最大努力通知用户。然而,为了实现这一点,SDG 必须能够找到现有 Index,这是通过 Pivotal GemFire API(唯一可用的方法)查找的。
Override 行为

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

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

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

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

如果是这样,SDG 很智能,即使在 override 模式下也会按原样返回现有 Index。这种行为没有坏处,因为名称和定义完全相同。当然,SDG 只有在能够通过 Pivotal GemFire 的 API 找到现有 Index 时才能实现这一点。如果找不到,则不执行任何操作,并抛出封装了 IndexNameConflictException 的 SDG GemfireIndexException

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

IndexNameConflictExceptions 实际上如何发生?

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

然而,什么时候会抛出 IndexNameConflictException 呢?

一个特殊情况是在 PARTITION Region (PR) 上定义的 Index。当在 PARTITION Region(例如,X)上定义 Index 时,Pivotal GemFire 会将 Index 定义(和名称)分发给集群中也托管相同 PARTITION Region(即“X”)的其他 peer 成员。将此 Index 定义分发给 peer 成员并随后由其创建此 Index 是基于按需知晓的原则(即由托管相同 PR 的 peer 成员)异步执行的。

在这段时间窗口内,这些挂起的 PR Indexes 可能无法被 Pivotal GemFire 识别——例如,通过调用 QueryService.getIndexes(),或使用 QueryService.getIndexes(:Region),甚至使用 QueryService.getIndex(:Region, indexName:String)

因此,SDG 或其他 Pivotal GemFire 缓存客户端应用程序(不涉及 Spring)唯一确定知道的方法就是尝试创建 Index。如果创建失败并抛出 IndexNameConflictExceptionIndexExistsException,应用程序就知道有问题。这是因为 QueryServiceIndex 创建会等待挂起的 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 实例被 Region 用于文件系统持久化备份、 evicted 条目的溢出以及 WAN Gateways 的持久化备份。多个 Pivotal GemFire 组件可以共享同一个 DiskStore。此外,可以为一个 DiskStore 定义多个文件系统目录,如上例所示。

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

5.8. 配置快照服务

Spring Data for Pivotal GemFire 支持使用 Pivotal GemFire 的快照服务进行缓存和 Region 快照。开箱即用的快照服务支持提供了几个方便的功能,以简化使用 Pivotal GemFire 的 CacheRegion 快照服务 API。

正如 Pivotal GemFire 文档所解释的,快照让你能够保存并随后重新加载缓存的数据,这对于在不同环境之间移动数据很有用,例如从生产环境移动到 staging 或测试环境,以便在受控上下文中重现数据相关问题。你可以将 Spring Data for Pivotal GemFire 的快照服务支持与 Spring 的 bean 定义 profile 结合使用,根据需要加载特定于环境的快照数据。

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

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

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

你可以根据需要定义任意数量的导入和导出。你可以只定义导入或只定义导出。文件位置和目录路径可以是绝对路径,也可以是相对于 Spring Data for Pivotal GemFire 应用程序工作目录的相对路径。

上面的示例非常简单,此处定义的快照服务引用了默认名称为 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 属性来为特定 Region 定义快照服务,如下所示:

<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 容器中定义的 Region bean,并创建一个 RegionSnapshotService。快照导入和导出定义的函数方式相同。但是,导出时 location 必须引用一个文件。

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

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 接口,用于过滤 Region 条目,以决定在导入时包含哪些条目到 Region 中,以及在导出时包含哪些条目到快照中。

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 Region。这些类中的构造函数接受一个可选的 Region pathname(例如 /Example)以及零个或多个 SnapshotMetadata 实例。

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

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

基于 Region 的 [Import|Export]SnapshotApplicationEvent 在以下情况下匹配:定义的快照服务 bean 是 RegionSnapshotService,并且其 Region 引用(由 region-ref 属性确定)与快照应用程序事件指定的 Region pathname 相匹配。

基于缓存的 [Import|Export]SnapshotApplicationEvent(即没有 Region pathname 的快照应用程序事件)会触发所有快照服务 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 Region 的快照服务 bean 会接收并处理导出事件,将过滤后的“/Example”Region 数据保存到应用程序工作目录子目录中的 data.snapshot 文件。

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

5.9. 配置 Function Service

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

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

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

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

Namespace 使用熟悉的方式声明函数,如下例所示:

<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 Gateways

WAN Gateways 提供了一种在地理位置之间同步 Pivotal GemFire 分布式系统的方法。Spring Data for Pivotal GemFire 提供 XML Namespace 支持来配置 WAN Gateways,如下例所示。

5.10.1. Pivotal GemFire 7.0 中的 WAN 配置

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

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

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

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

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

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

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

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

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

6. 使用注解通过 Spring 容器引导 Pivotal GemFire

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

在 Spring 上下文中引入基于注解的方法来配置 Pivotal GemFire 的主要动机是使 Spring 应用程序开发人员能够尽可能快尽可能容易启动和运行

让我们开始吧!

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

6.1. 简介

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

不同的支持拓扑会带来进一步的复杂性

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

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

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

与 Spring Boot 一样,Spring Data for Pivotal GemFire 基于注解的配置模型被设计为一种有主见、约定优于配置的方式来使用 Pivotal GemFire。事实上,这个基于注解的配置模型受到了 Spring Boot 以及几个其他 Spring 和 Spring Data 项目的启发。

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

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

再说一次,快速轻松入门是主要目标。

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

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

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

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

  • @ClientCacheApplication

  • @PeerCacheApplication

  • @CacheServerApplication

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

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

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

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

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

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

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

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

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

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

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

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

6.3. 客户端/服务器应用程序详解

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

6.4. 配置和引导 Locators

除了 Pivotal GemFire Cache 应用程序之外,您还可以创建 Pivotal GemFire Locator 应用程序。

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

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

要配置和引导独立的 Locator 进程,请执行以下操作:

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

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

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

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

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

同样,您可以根据需要启动尽可能多的由上面的 Locator 加入的 ServerApplication 类。您只需要确保成员名称是唯一的。

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

如果您想同时启动缓存实例和嵌入式 Locator,那么您应该改用 @EnableLocator 注解。

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

有关更多详细信息,请参阅配置嵌入式 Locator一节。

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

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

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

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

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

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

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

  • CacheServerConfigurer

  • ClientCacheConfigurer

  • ContinuousQueryListenerContainerConfigurer

  • DiskStoreConfigurer

  • IndexConfigurer

  • PeerCacheConfigurer

  • PoolConfigurer

  • RegionConfigurer

  • GatewayReceiverConfigurer

  • GatewaySenderConfigurer

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

考虑下面服务器应用程序的示例:

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

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

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

接下来,考虑下面客户端应用程序的示例:

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

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

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

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

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

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

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 很重要(除了在某些情况下是必需的)。这样做使得可以在 Spring 容器中从 XML、properties 和 Java 引用 bean。甚至可以将注解定义的 bean 注入到应用程序类中,无论出于何种目的,如下例所示:

@Component
class MyApplicationComponent {

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

  ...
}

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

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

以下示例展示了这种安排:

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

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

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

6.6.1. Properties 中的 Properties

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

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 Locators 用于客户端连接并查找集群中的服务器。此外,加入现有集群的新成员使用 Locators 来查找它们的对等点。

对于应用程序开发人员来说,在开发 Spring Boot 和 Spring Data for Pivotal GemFire 应用程序时,启动一个包含两三个 Pivotal GemFire 服务器的小集群通常很方便。与其启动单独的 Locator 进程,不如使用 @EnableLocator 注解您的 Spring Boot @CacheServerApplication 类,如下所示:

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

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

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

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

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

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

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

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

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

}

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

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

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

@EnableLocator 注解仅旨在用于开发时,而非应用程序开发人员在生产环境中使用。我们强烈建议在集群中将 Locators 作为独立的、独立的进程运行。

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

6.7.2. 配置嵌入式 Manager

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

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

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

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

以下示例展示了如何在 Java 中创建嵌入式 Manager:

Spring CacheServer 应用程序运行嵌入式 Manager
@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。如果不存在 Manager,Locator 将承担 Manager 的角色。但是,如果不存在 Locator,我们需要使用以下命令直接连接到 Manager:

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

有关 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 注解类,如下所示:

Spring CacheServer 应用程序运行嵌入式 HTTP 服务器
@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 注解类,如下所示:

Spring CacheServer 应用程序运行嵌入式 Memcached 服务器
@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 注解类,如下所示:

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

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

6.8. 配置日志记录

通常,有必要提高日志记录级别,以便准确了解 Pivotal GemFire 正在做什么以及何时进行。

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

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

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

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

查看 @EnableLogging 注解的 Javadoc 获取更多详细信息。

6.9. 配置统计信息

为了在运行时更深入地了解 Pivotal GemFire,您可以启用统计信息。收集统计数据有助于进行系统分析和故障排除,特别是在出现通常具有分布式性质且时序是关键因素的复杂问题时。

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

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

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

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

您可以使用 @EnableStatistics 注解的属性或相关的属性来定制统计信息的收集过程。

查看 @EnableStatistics 注解的 Javadoc 获取更多详细信息。

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

6.10. 配置 PDX

Pivotal GemFire 更强大的特性之一是 PDX 序列化。虽然全面讨论 PDX 超出了本文档的范围,但使用 PDX 进行序列化是 Java 序列化的更好替代方案,具有以下优势

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

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

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

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

通常,Pivotal GemFire 中的序列化在以下任何情况下都是必需的:数据在客户端和服务器之间或集群中的对等节点之间传输(在正常的分布和复制过程中),以及数据溢出或持久化到磁盘时。

启用 PDX 序列化比修改所有应用程序域对象类型以实现 java.io.Serializable 要简单得多,尤其是在您不希望对应用程序域模型施加此类限制或您对要序列化的对象没有任何控制权的情况下,当使用第三方库(例如,考虑一个带有 Coordinate 类型的地理空间 API)时更是如此。

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

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

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

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

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

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

您可以在 Spring 容器中将此组合式 PdxSerializer 声明为一个受管 bean,并在 @EnablePdx 注解中使用 serializerBeanName 属性按其 bean 名称引用此组合式 PdxSerializer。Spring Data for Pivotal GemFire 会代您将其注册到 Pivotal GemFire。

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

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

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

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

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

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

查看 @EnablePdx 注解的 Javadoc 获取更多详细信息。

6.11. 配置 Pivotal GemFire 属性

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

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

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

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

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

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

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

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

6.12. 配置 Region

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

Pivotal GemFire 将缓存中的数据组织到 Region 中。您可以将 Region 视为关系数据库中的表。通常,一个 Region 只应存储单一类型的对象,这使得它更适合构建有效的索引和编写查询。我们稍后会介绍索引配置。

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

以下示例演示了如何在 Java 中配置一个 Region bean

使用 Spring 基于 Java 的容器配置的示例 Region 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 中配置相同的 Region bean

使用 SDG XML Namespace 的示例 Region bean 定义
<gfe:partitioned-region id="exampleRegion" name="Example" persistent="true">
    ...
</gfe:partitioned-region>

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

手动定义和声明所有这些 Region 将既麻烦又容易出错。现在,有了一种更好的方法。

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

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

大多数 Spring Data 应用程序开发人员应该已经熟悉 Spring Data Repository 抽象以及 Spring Data for Pivotal GemFire 针对 Pivotal GemFire 数据访问操作进行优化的实现/扩展

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

建模 Book 的应用程序域对象类型
@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 仓库,如下所示

Books 的仓库
interface BookRepository extends CrudRepository<Book, ISBN> { .. }

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

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

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

Spring Data for Pivotal GemFire 使用映射上下文(包含应用程序中定义的所有实体的映射元数据)来确定运行时所需的所有 Region。

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

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

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

然而,通常会通过设置 basePackages 属性指定包含应用程序实体类的包名来限制扫描范围。

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

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

使用实体类类型进行实体定义的 Region 配置
@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. 配置特定类型的 Region

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

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

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

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

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

  • @ClientRegion

  • @LocalRegion

  • @PartitionRegion

  • @ReplicateRegion

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

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

Region 级别的空闲超时 (TTI) 和生存时间 (TTL) 过期策略与条目级别的 TTI 和 TTL 过期策略是相互独立且不同的。

无论如何,如果您想创建一个仅本地的客户端 Region,其数据不会分发回服务器上同名 Region 的对应数据,您可以声明 @ClientRegion 映射注解并将 shortcut 属性设置为 ClientRegionShortcut.LOCAL,如下所示

具有仅本地客户端 Region 的 Spring ClientCache 应用程序
@ClientRegion(shortcut = ClientRegionShortcut.LOCAL)
class ClientLocalEntityType { .. }

所有特定 Region 类型的注解都提供了额外的属性,这些属性既是 Region 类型通用的,也是该 Region 类型特有的。例如,PartitionRegion 注解中的 collocatedWithredundantCopies 属性仅适用于服务器端 PARTITION 类型的 Region。

有关 Pivotal GemFire Region 类型的更多详细信息,请参阅此处

6.12.2. 配置集群定义的 Region

除了 @EnableEntityDefinedRegions 注解外,Spring Data for Pivotal GemFire 还提供了其反向注解 @EnableClusterDefinedRegions。您可以选择不基于应用程序用例 (UC) 和需求定义和驱动的实体类(这是最常见和合乎逻辑的方法)来定义 Region,而是可以从 ClientCache 应用程序将连接到的集群中已定义的 Region 来声明您的 Region。

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

这个想法是,不让客户端应用程序驱动数据字典,而是由用户使用 Pivotal GemFire 的 Gfsh CLI shell 工具定义 Region。这样做还有一个额外的好处,即当向集群添加其他对等节点时,它们也将拥有并共享相同的配置,因为这些配置由 Pivotal GemFire 的 Cluster Configuration Service 记住。

举个例子,用户可以在 Gfsh 中定义一个 Region,如下所示

使用 Gfsh 定义 Region
gfsh>create region --name=Books --type=PARTITION
 Member   | Status
--------- | --------------------------------------
ServerOne | Region "/Books" created on "ServerOne"
ServerTwo | Region "/Books" created on "ServerTwo"

gfsh>list regions
List of regions
---------------
Books

gfsh>describe region --name=/Books
..........................................................
Name            : Books
Data Policy     : partition
Hosting Members : ServerTwo
                  ServerOne

Non-Default Attributes Shared By Hosting Members

 Type  |    Name     | Value
------ | ----------- | ---------
Region | size        | 0
       | data-policy | PARTITION

借助 Pivotal GemFire 的 Cluster Configuration Service,任何添加到服务器集群中以处理增加负载(后端)的其他对等成员也将具有相同的配置,例如

向集群添加一个额外的对等成员
gfsh>list members
  Name    | Id
--------- | ----------------------------------------------
Locator   | 10.0.0.121(Locator:68173:locator)<ec><v0>:1024
ServerOne | 10.0.0.121(ServerOne:68242)<v3>:1025
ServerTwo | 10.0.0.121(ServerTwo:68372)<v4>:1026

gfsh>start server --name=ServerThree --log-level=config --server-port=41414
Starting a Geode Server in /Users/you/geode/cluster/ServerThree...
...
Server in /Users/you/geode/cluster/ServerThree... on 10.0.0.121[41414] as ServerThree is currently online.
Process ID: 68467
Uptime: 3 seconds
Geode Version: 1.2.1
Java Version: 1.8.0_152
Log File: /Users/you/geode/cluster/ServerThree/ServerThree.log
JVM Arguments: -Dgemfire.default.locators=10.0.0.121[10334]
  -Dgemfire.use-cluster-configuration=true
  -Dgemfire.start-dev-rest-api=false
  -Dgemfire.log-level=config
  -XX:OnOutOfMemoryError=kill -KILL %p
  -Dgemfire.launcher.registerSignalHandlers=true
  -Djava.awt.headless=true
  -Dsun.rmi.dgc.server.gcInterval=9223372036854775806
Class-Path: /Users/you/geode/cluster/apache-geode-1.2.1/lib/geode-core-1.2.1.jar
  :/Users/you/geode/cluster/apache-geode-1.2.1/lib/geode-dependencies.jar

gfsh>list members
   Name     | Id
----------- | ----------------------------------------------
Locator     | 10.0.0.121(Locator:68173:locator)<ec><v0>:1024
ServerOne   | 10.0.0.121(ServerOne:68242)<v3>:1025
ServerTwo   | 10.0.0.121(ServerTwo:68372)<v4>:1026
ServerThree | 10.0.0.121(ServerThree:68467)<v5>:1027

gfsh>describe member --name=ServerThree
Name        : ServerThree
Id          : 10.0.0.121(ServerThree:68467)<v5>:1027
Host        : 10.0.0.121
Regions     : Books
PID         : 68467
Groups      :
Used Heap   : 37M
Max Heap    : 3641M
Working Dir : /Users/you/geode/cluster/ServerThree
Log file    : /Users/you/geode/cluster/ServerThree/ServerThree.log
Locators    : 10.0.0.121[10334]

Cache Server Information
Server Bind              :
Server Port              : 41414
Running                  : true
Client Connections       : 0

如您所见,“ServerThree”现在拥有“Books”Region。如果任何或所有服务器宕机,它们恢复时仍将拥有相同的配置以及“Books”Region。

在客户端,可能会启动许多图书商店客户端应用程序实例来针对图书商店在线服务处理图书。“Books”Region 可能是实现图书商店应用程序服务所需的许多不同 Region 中的一个。SDG 提供便利,允许客户端应用程序的 Region 从集群中定义,而不是必须单独创建和配置每个 Region,如下所示

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

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

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

然后,在应用程序中使用“Books”Region 就变得简单了。您可以直接注入“Books”Region,如下所示

使用“Books”Region
@org.springframework.stereotype.Repository
class BooksDataAccessObject {

    @Resource(name = "Books")
    private Region<ISBN, Book> books;

    // implement CRUD and queries with the "Books" Region
}

或者,甚至可以基于映射到“Books”Region 的应用程序域类型(实体)Book 来定义一个 Spring Data Repository 定义,如下所示

将“Books”Region 与 SD Repository 一起使用
interface BookRepository extends CrudRepository<Book, ISBN> {
    ...
}

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

6.12.3. 配置逐出

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

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

尽管如此,即使使用节点集群,通常也必须只将最重要的数据保留在内存中。内存耗尽,甚至接近满容量,都很少是一件好事,甚至根本不是好事。全局暂停 GC(Stop-the-world GCs)或更糟的 OutOfMemoryErrors 会使您的应用程序彻底崩溃。

因此,为了帮助管理内存并保留最重要的数据,Pivotal GemFire 支持最近最少使用 (LRU) 逐出。也就是说,Pivotal GemFire 使用最近最少使用算法,根据条目最后一次访问的时间来逐出 Region 条目。

要启用逐出,请使用 @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 { .. }

逐出策略通常在服务器上的 Region 中设置。

如前所述,policies 属性可以指定一个或多个嵌套的 @EvictionPolicy 注解,每个注解都单独针对需要应用逐出策略的一个或多个 Region 进行配置。

此外,您可以引用 Pivotal GemFire 的 org.apache.geode.cache.util.ObjectSizer 接口的自定义实现,该实现可以在 Spring 容器中定义为 bean,并通过使用 objectSizerName 属性按名称引用。

ObjectSizer 允许您定义用于评估和确定存储在 Region 中的对象大小的标准。

有关逐出配置选项的完整列表,请参阅 @EnableEviction 注解的 Javadoc

有关 Pivotal GemFire 逐出的更多详细信息,请参阅此处

6.12.4. 配置过期

除了逐出之外,还可以使用过期来管理内存,通过允许存储在 Region 中的条目过期。Pivotal GemFire 支持生存时间 (TTL) 和空闲超时 (TTI) 条目过期策略。

Spring Data for Pivotal GemFire 基于注解的过期配置基于 Spring Data for Pivotal GemFire 1.5 版本中添加的早期和现有的条目过期注解支持

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

Spring Data for Pivotal GemFire 提供了以下过期注解

  • @Expiration

  • @IdleTimeoutExpiration

  • @TimeToLiveExpiration

应用程序域对象类型可以使用一个或多个过期注解进行注解,如下所示

特定于应用程序域对象的过期策略
@Region("Books")
@TimeToLiveExpiration(timeout = 30000, action = "INVALIDATE")
class Book { .. }

要启用过期,请使用 @EnableExpiration 注解您的应用程序类,如下所示

启用了过期的 Spring 应用程序
@SpringBootApplication
@PeerCacheApplication
@EnableExpiration
class ServerApplication { .. }

除了应用程序域对象类型级别的过期策略之外,您还可以使用 @EnableExpiration 注解直接且逐个 Region 配置过期策略,如下所示

具有特定 Region 过期策略的 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 Region 设置了过期策略。

过期策略通常在服务器上的 Region 中设置。

有关过期配置选项的完整列表,请参阅 @EnableExpiration 注解的 Javadoc

有关 Pivotal GemFire 过期的更多详细信息,请参阅此处

6.12.5. 配置压缩

除了逐出过期之外,您还可以为数据 Region 配置压缩以减少内存消耗。

Pivotal GemFire 允许您使用可插拔的 Compressors 或不同的压缩编解码器来压缩内存中的 Region 值。Pivotal GemFire 默认使用 Google 的 Snappy 压缩库。

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

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

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

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

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

查看 @EnableCompression 注解的 Javadoc 获取更多详细信息。

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

6.12.6. 配置堆外内存

另一种有效减轻 JVM 堆内存压力并最小化 GC 活动的方法是使用 Pivotal GemFire 的堆外内存支持。

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

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

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

memorySize 属性是必需的。memorySize 属性的值指定 Region 可以使用的主要内存量,以兆字节 (m) 或千兆字节 (g) 为单位。

regionNames 属性是一个 Region 名称数组,指定了将条目存储在主要内存中的 Region。默认情况下,如果未明确设置 regionNames 属性,则所有 Region 都使用主要内存。

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

查看 @EnableOffHeap 注解的 Javadoc 获取更多详细信息。

6.12.7. 配置磁盘存储

此外,您可以配置 Region 将数据持久化到磁盘。您还可以配置 Region 在条目被逐出时将数据溢出到磁盘。在这两种情况下,都需要一个 DiskStore 来持久化和/或溢出数据。如果未为启用了持久化或溢出的 Region 配置显式的 DiskStore,Pivotal GemFire 将使用 DEFAULT DiskStore

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

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

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

例如,虽然 Book 信息可能主要包含来自外部数据源(如 Amazon)的参考数据,但 Order 数据本质上很可能是事务性的,并且是应用程序需要保留的东西(如果事务量足够大,甚至可能溢出到磁盘)——反正任何图书出版商和作者都希望如此。

使用 @EnableDiskStore 注解,您可以定义和创建一个 DiskStore,如下所示

定义了 DiskStore 的 Spring 应用程序
@SpringBootApplication
@PeerCacheApplication
@EnableDiskStore(name = "OrdersDiskStore", autoCompact = true, compactionThreshold = 70,
    maxOplogSize = 512, diskDirectories = @DiskDiretory(location = "/absolute/path/to/order/disk/files"))
class ServerApplication { .. }

同样,可以使用复合注解 @EnableDiskStores 定义多个 DiskStore

与 Spring Data for Pivotal GemFire 基于注解的配置模型中的其他注解一样,@EnableDiskStore@EnableDiskStores 都具有许多属性以及相关的配置属性,用于定制运行时创建的 DiskStores

此外,@EnableDiskStores 注解定义了某些通用的 DiskStore 属性,这些属性适用于所有由与 @EnableDiskStores 注解本身组合的 @EnableDiskStore 注解创建的 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. 配置索引

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

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

一般来说,没有索引的查询效率很低。在执行没有索引的查询时,Pivotal GemFire 会执行相当于全表扫描的操作。

索引是为查询谓词中使用的对象字段创建和维护的,用于匹配查询投影所表示的感兴趣的数据。可以创建不同类型的索引,例如键 (key) 索引和哈希 (hash) 索引。

Spring Data for Pivotal GemFire 可以轻松地在存储和访问数据的 Region 上创建索引。我们不再像以前那样使用 Spring 配置显式声明 Index bean 定义,而可以在 Java 中创建 Index bean 定义,如下所示:

使用 Java config 的 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 实体类,我们可以使用 @Indexed@LuceneIndexed 注解来标记我们知道在 BookRepository 接口中通过查询方法定义的查询中使用的 Book 字段,如下所示:

使用索引建模书籍的应用领域对象类型
@Region("Books")
class Book {

  @Id
  private ISBN isbn;

  @Indexed
  private Author author;

  private Category category;

  private LocalDate releaseDate;

  private Publisher publisher;

  @LuceneIndexed
  private String title;

}

在我们新的 Book 类定义中,我们使用 @Indexed 注解标记了 author 字段,使用 @LuceneIndexed 注解标记了 title 字段。此外,isbn 字段之前已经使用 Spring Data 的 @Id 注解标记,该注解标识了包含 Book 实例唯一标识符的字段,并且在 Spring Data for Pivotal GemFire 中,@Id 注解的字段或属性在存储条目时用作 Region 中的键。

  • @Id 注解的字段或属性会导致创建 Pivotal GemFire KEY 索引。

  • @Indexed 注解的字段或属性会导致创建 Pivotal GemFire HASH 索引(默认)。

  • @LuceneIndexed 注解的字段或属性会导致创建 Pivotal GemFire Lucene 索引,用于通过 Pivotal GemFire 的 Lucene 集成和支持进行基于文本的搜索。

当使用 @Indexed 注解而未设置任何属性时,索引的 nameexpressionfromClause 将从添加 @Indexed 注解的类的字段或属性中派生。expression 正是字段或属性的名称。fromClause 从领域对象类上的 @Region 注解中派生,如果未指定 @Region 注解,则为领域对象类的简单名称。

当然,您可以显式设置 @Indexed 注解的任何属性,以覆盖 Spring Data for Pivotal GemFire 提供的默认值。

使用定制索引建模书籍的应用领域对象类型
@Region("Books")
class Book {

  @Id
  private ISBN isbn;

  @Indexed(name = "BookAuthorNameIndex", expression = "author.name", type = "FUNCTIONAL")
  private Author author;

  private Category category;

  private LocalDate releaseDate;

  private Publisher publisher;

  @LuceneIndexed(name = "BookTitleIndex", destory = true)
  private String title;

}

索引的 name(在未显式设置时自动生成)也用作索引在 Spring 容器中注册的 bean 的名称。如有必要,此索引 bean 甚至可以通过名称注入到另一个应用组件中。

生成的索引名称遵循以下模式:<Region Name><Field/Property Name><Index Type>Idx。例如,author 索引的名称将是 BooksAuthorHashIdx

要启用索引,请使用 @EnableIndexing 注解标记应用类,如下所示:

启用索引的 Spring 应用
@SpringBootApplication
@PeerCacheApplication
@EnableEntityDefinedRegions
@EnableIndexing
class ServerApplication { .. }
除非同时声明了 @EnableEntityDefinedRegions,否则 @EnablingIndexing 注解无效。本质上,索引是从实体类类型上的字段或属性定义的,并且必须扫描实体类以检查实体的字段和属性是否存在索引注解。如果没有此扫描,则无法找到索引注解。我们还强烈建议您限制扫描范围。

虽然 Spring Data for Pivotal GemFire 存储库(尚不)支持 Lucene 查询,但 SDG 通过使用熟悉的 Spring 模板设计模式提供了对 Pivotal GemFire Lucene 查询的全面支持

最后,我们在本节末尾提供一些使用索引时需要记住的额外提示:

  • 执行 OQL 查询不需要 OQL 索引,但执行 Lucene 文本搜索需要 Lucene 索引。

  • OQL 索引不会持久化到磁盘。它们仅在内存中维护。因此,当 Pivotal GemFire 节点重新启动时,必须重建索引。

  • 您还需要注意维护索引相关的开销,特别是考虑到索引仅存储在内存中,并且尤其是在 Region 条目更新时。索引“维护”可以配置为异步任务。

当重新启动需要重建索引的 Spring 应用时,您可以使用另一种优化方法:先提前定义所有索引,然后一次性全部创建。在 Spring Data for Pivotal GemFire 中,这发生在 Spring 容器刷新时。

您可以通过将 @EnableIndexing 注解上的 define 属性设置为 true 来提前定义索引,然后一次性全部创建。

更多详细信息,请参阅 Pivotal GemFire 用户指南中的“一次创建多个索引”

创建合理的索引是一项重要任务,因为设计不良的索引可能会弊大于利。

有关完整配置选项列表,请参阅 @Indexed 注解和 @LuceneIndexed 注解的 Javadoc。

有关 Pivotal GemFire OQL 查询的更多详细信息,请参阅此处

有关 Pivotal GemFire 索引的更多详细信息,请参阅此处

有关 Pivotal GemFire Lucene 查询的更多详细信息,请参阅此处

6.13. 配置连续查询

Pivotal GemFire 的另一个非常重要和有用的特性是连续查询 (Continuous Queries)

在一个互联网驱动的世界里,事件和数据流无处不在。能够处理和处理大量数据流并实时对事件做出反应对于许多应用来说是一个日益重要的要求。一个例子是自动驾驶汽车。能够实时接收、过滤、转换、分析和处理数据是实时应用的关键区别和特征。

幸运的是,Pivotal GemFire 在这方面走在了时代前列。通过使用连续查询 (CQ),客户端应用可以表达它感兴趣的数据或事件,并注册监听器来处理事件发生时的处理。客户端应用可能感兴趣的数据表示为 OQL 查询,其中查询谓词用于过滤或识别感兴趣的数据。当数据发生更改或添加并匹配已注册 CQ 的查询谓词中定义的条件时,客户端应用将收到通知。

Spring Data for Pivotal GemFire 可以轻松定义和注册 CQ,以及相关的监听器来处理 CQ 事件,而无需 Pivotal GemFire 的大量繁琐管道。SDG 基于注解的新 CQ 配置建立在连续查询监听器容器 (continuous query listener container) 中现有的连续查询支持之上。

例如,假设一家图书出版商希望注册兴趣并在任何时候 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 注解(类似于 SDG 的 Function 注解的 POJO 方法)注解任何 Spring @Component 注解的 POJO 类方法。使用 @ContinuousQuery 注解定义的带有 CQ 的 POJO 方法在任何时候添加或更改与查询谓词匹配的数据时都会被调用。

此外,POJO 方法签名应符合ContinuousQueryListenerContinuousQueryListenerAdapter 部分概述的要求。

有关可用属性和配置设置的更多详细信息,请参阅 @EnableContinuousQueries@ContinuousQuery 注解的 Javadoc。

有关 Spring Data for Pivotal GemFire 连续查询支持的更多详细信息,请参阅此处

有关 Pivotal GemFire 连续查询的更多详细信息,请参阅此处

6.14. 配置 Spring 的缓存抽象

通过 Spring Data for Pivotal GemFire,Pivotal GemFire 可以用作 Spring 缓存抽象中的缓存提供者。

在 Spring 的缓存抽象中,缓存注解(例如 @Cacheable)标识了在调用可能耗费资源的操作之前执行缓存查找的缓存。应用服务方法的结 果在操作调用后被缓存。

在 Spring Data for Pivotal GemFire 中,Spring Cache 直接对应于 Pivotal GemFire Region。在调用任何带有缓存注解的应用服务方法之前,Region 必须存在。这对于任何识别在服务操作中使用的缓存的 Spring 缓存注解(即 @Cacheable@CachePut@CacheEvict)都适用。

例如,我们出版商的销售点 (PoS) 应用可能有一个功能,用于在销售交易期间确定或查找 BookPrice,如下例所示:

@Service
class PointOfSaleService

  @Cacheable("BookPrices")
  Price runPriceCheckFor(Book book) {
      ...
  }

  @Transactional
  Receipt checkout(Order order) {
      ...
  }

  ...
}

为了使您在使用 Spring Data for Pivotal GemFire 和 Spring 的缓存抽象时工作更轻松,已向基于注解的配置模型添加了两个新功能。

考虑以下 Spring 缓存配置:

使用 Pivotal GemFire 作为缓存提供者启用缓存
@EnableCaching
class CachingConfiguration {

  @Bean
  GemfireCacheManager cacheManager(GemFireCache gemfireCache) {

      GemfireCacheManager cacheManager = new GemfireCacheManager();

      cacheManager.setCache(gemfireCache);

      return cacheManager;
  }

  @Bean("BookPricesCache")
  ReplicatedRegionFactoryBean<Book, Price> bookPricesRegion(GemFireCache gemfireCache) {

    ReplicatedRegionFactoryBean<Book, Price> bookPricesRegion =
        new ReplicatedRegionFactoryBean<>();

    bookPricesRegion.setCache(gemfireCache);
    bookPricesRegion.setClose(false);
    bookPricesRegion.setPersistent(false);

    return bookPricesRegion;
  }

  @Bean("PointOfSaleService")
  PointOfSaleService pointOfSaleService(..) {
      return new PointOfSaleService(..);
  }
}

使用 Spring Data for Pivotal GemFire 的新功能,您可以将相同的缓存配置简化为以下内容:

启用 Pivotal GemFire 缓存
@EnableGemfireCaching
@EnableCachingDefinedRegions
class CachingConfiguration {

  @Bean("PointOfSaleService")
  PointOfSaleService pointOfSaleService(..) {
      return new PointOfSaleService(..);
  }
}

首先,@EnableGemfireCaching 注解取代了 Spring 的 @EnableCaching 注解,并且无需在 Spring 配置中声明一个显式的 CacheManager bean 定义(命名为 "cacheManager")。

其次,@EnableCachingDefinedRegions 注解,就像“配置 Region”中描述的 @EnableEntityDefinedRegions 注解一样,会检查整个 Spring 应用和带有缓存注解的服务组件,以识别应用在运行时所需的所有缓存,并在应用启动时在 Pivotal GemFire 中为这些缓存创建 Region。

创建的 Region 对于创建这些 Region 的应用进程是本地的。如果应用是对等 (peer) Cache,则 Region 仅存在于应用节点上。如果应用是 ClientCache,则 SDG 会创建客户端 PROXY Region,并期望这些同名 Region 已经存在于集群中的服务器上。

SDG 无法使用 Spring 的 CacheResolver 在运行时解析操作中使用的缓存来确定服务方法所需的缓存。
SDG 也支持在应用服务组件上使用 JCache (JSR-107) 缓存注解。有关如何使用等效的 Spring 缓存注解替代 JCache 缓存注解,请参阅核心 Spring Framework Reference Guide

有关将 Pivotal GemFire 用作 Spring 缓存抽象中的缓存提供者的更多详细信息,请参阅“对 Spring 缓存抽象的支持”部分。

有关 Spring 缓存抽象的更多详细信息,请参阅此处

6.15. 配置集群配置推送

这可能是 Spring Data for Pivotal GemFire 中最令人兴奋的新功能。

当客户端应用类被 @EnableClusterConfiguration 注解时,客户端应用在 Spring 容器中定义和声明为 bean 的任何 Region 或 Index 都将“推送”到客户端连接的服务器集群。不仅如此,这种“推送”是以一种方式进行的,即当使用 HTTP 时,Pivotal GemFire 会记住客户端推送的配置。如果集群中的所有节点都宕机,它们将以与之前相同的配置重新启动。如果向集群中添加新服务器,它将获得相同的配置。

从某种意义上说,这个功能与您使用 Gfsh 手动在集群中的所有服务器上创建 Region 和 Index 没有太大区别。不同之处在于,现在使用 Spring Data for Pivotal GemFire,您不再需要使用 Gfsh 来创建 Region 和 Index。您的 Spring Boot 应用,借助 Spring Data for Pivotal GemFire 的强大功能,已经包含了为您创建 Region 和 Index 所需的所有配置元数据。

当您使用 Spring Data Repository 抽象时,我们知道您的应用将需要所有 Region(例如由 @Region 注解的实体类定义的 Region)和 Index(例如由 @Indexed 注解的实体字段和属性定义的 Index)。

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

本质上,您只需使用 Spring Framework 的所有 API 和特性来开发您的应用,无论是通过注解元数据、Java、XML 还是其他方式表达,无论是用于配置、映射还是其他目的,您就已经告诉了我们所有需要知道的信息。

重点是,您可以专注于您的应用业务逻辑,同时利用框架的特性和支持基础设施(例如 Spring 的缓存抽象、Spring Data 存储库、Spring 的事务管理等),而 Spring Data for Pivotal GemFire 将代表您处理这些框架特性所需的 Pivotal GemFire 管道工作。

将配置从客户端推送到集群中的服务器并让集群记住它,部分得益于 Pivotal GemFire 的集群配置 (Cluster Configuration) 服务。Pivotal GemFire 的集群配置服务也是 Gfsh 用于记录用户从 shell 发出的集群中与 schema 相关的更改(例如,gfsh> create region --name=Example --type=PARTITION)的服务。

当然,由于集群可能会“记住”客户端从之前运行中推送的先前配置,Spring Data for Pivotal GemFire 小心翼翼地避免覆盖服务器中已定义的任何现有 Region 和 Index。这一点尤其重要,例如,当 Region 中已包含数据时!

目前,没有选项可以覆盖任何现有的 Region 或 Index 定义。要重新创建 Region 或 Index,您必须先使用 Gfsh 销毁 Region 或 Index,然后重新启动客户端应用,以便配置再次推送到服务器。或者,您可以使用 Gfsh 手动(重新)定义 Region 和 Index。
Gfsh 不同,Spring Data for Pivotal GemFire 仅支持从客户端在服务器上创建 Region 和 Index。对于高级配置和用例,您应该使用 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 和 Index 不仅在客户端创建,而且推送到集群中的服务器)的 Spring Boot 应用。

从那里,您只需要做以下事情:

  • 定义使用映射和索引注解标记的应用领域模型对象。

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

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

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

在这种情况下,没有任何内容涉及应用后端服务(如 Pivotal GemFire)所需的 infrastructure 和 plumbing。数据库用户拥有类似的功能。现在 Spring 和 Pivotal GemFire 开发人员也有了。

当与以下 Spring Data for Pivotal GemFire 注解结合使用时,这个应用真正开始起飞,而且只需要很少的努力:

  • @EnableContinuousQueries

  • @EnableGemfireFunctionExecutions

  • @EnableGemfireCacheTransactions

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

6.16. 配置 SSL

与通过网络传输数据一样重要,在传输过程中保护数据也同样重要。当然,在 Java 中实现这一目标的常用方法是使用安全套接字扩展 (SSE) 和传输层安全 (TLS)。

要启用 SSL,请使用 @EnableSsl 注解标记您的应用类,如下所示:

启用 SSL 的 Spring ClientCache 应用
@SpringBootApplication
@ClientCacheApplication
@EnableSsl
public class ClientApplication { .. }

然后您需要设置必要的 SSL 配置属性或特性:keystores、用户名/密码等。

您可以单独为不同的 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 推出了一个新的集成安全性 (Integrated Security) 框架(取代了其旧的身份验证和授权安全模型),用于处理身份验证和授权。这个新安全框架的主要特性和好处之一是它集成了 Apache Shiro,因此可以将身份验证和授权请求委托给 Apache Shiro 来强制执行安全性。

本节的其余部分演示了 Spring Data for Pivotal GemFire 如何进一步简化 Pivotal GemFire 的安全性问题。

6.17.1. 配置服务器安全性

有几种不同的方法可以为 Pivotal GemFire 集群中的服务器配置安全性。

  • 实现 Pivotal GemFire 的 org.apache.geode.security.SecurityManager 接口,并使用完全限定的类名将 Pivotal GemFire 的 security-manager 属性设置为引用您的应用 SecurityManager 实现。或者,用户可以构建并初始化其 SecurityManager 实现的实例,并在创建 Pivotal GemFire 对等 (peer) Cache 时使用 CacheFactory.setSecurityManager(:SecurityManager) 方法进行设置。

  • 创建包含您应用定义的用户、角色和权限的 Apache Shiro shiro.ini 文件,然后将 Pivotal GemFire 的 security-shiro-init 属性设置为引用此 shiro.ini 文件,该文件必须在 CLASSPATH 中可用。

  • 仅使用 Apache Shiro,使用 Spring Data for Pivotal GemFire 的新 @EnableSecurity 注解标记您的 Spring Boot 应用类,并在 Spring 容器中定义一个或多个 Apache Shiro Realms 作为 bean,用于访问您应用的安全性元数据(即授权用户、角色和权限)。

第一种方法的问题在于您必须实现自己的 SecurityManager,这可能相当繁琐且容易出错。实现自定义的 SecurityManager 在访问存储安全元数据的任何数据源(例如 LDAP 甚至专有的内部数据源)中的安全元数据方面提供了一些灵活性。然而,通过配置和使用 Apache Shiro Realms,这个问题已经得到解决,Apache Shiro Realms 更普遍为人所知且不是 Pivotal GemFire 特定的。

请参阅 Pivotal GemFire 关于身份验证 (Authentication)授权 (Authorization) 的安全示例,以此作为实现您自己的自定义、应用特定的 SecurityManager 的一种可能方式。但是,我们强烈建议您不要这样做。

第二种方法是使用 Apache Shiro INI 文件,虽然稍好一些,但您仍然需要熟悉 INI 文件格式。此外,INI 文件是静态的,运行时不易更新。

第三种方法是最理想的,因为它遵循了广为人知且行业接受的概念(即 Apache Shiro 的安全框架),并且易于设置,如下例所示:

使用 Apache Shiro 的 Spring 服务器应用
@SpringBootApplication
@CacheServerApplication
@EnableSecurity
class ServerApplication {

  @Bean
  PropertiesRealm shiroRealm() {

      PropertiesRealm propertiesRealm = new PropertiesRealm();

      propertiesRealm.setResourcePath("classpath:shiro.properties");
      propertiesRealm.setPermissionResolver(new GemFirePermissionResolver());

      return propertiesRealm;
  }
}
前面示例中配置的 Realm 可以轻松地是 Apache Shiro 支持的任何 Realms

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

有关更多详细信息,请参阅 Apache Shiro 关于 Realms 的文档

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

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

有关可用属性和相关配置属性的更多详细信息,请参阅 @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 注解处理客户端和服务器应用的安全性。当用户决定将其应用从嵌入式对等 (peer) 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 通过使用其 Resource abstraction(资源抽象) 支持多种定位资源的方式。

有关可用属性和相关配置属性的更多详细信息,请参阅 @EnableSecurity 注解的 Javadoc

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

6.18. 配置技巧

以下技巧可以帮助您最大限度地利用新的基于注解的配置模型

6.18.1. 配置组织

正如我们在“配置集群配置推送”一节中所看到的,当使用注解启用许多 Pivotal GemFire 或 Spring Data for Pivotal GemFire 特性时,我们开始在 Spring @Configuration@SpringBootApplication 类上堆叠大量注解。在这种情况下,开始对配置进行一些划分是有意义的。

例如,考虑以下声明

包含各种配置的 Spring ClientCache 应用程序
@SpringBootApplication
@ClientCacheApplication
@EnableContinuousQueries
@EnableCachingDefinedRegions
@EnableEntityDefinedRegions
@EnableIndexing
@EnableGemfireCacheTransactions
@EnableGemfireCaching
@EnableGemfireFunctionExecutions
@EnableGemfireRepositories
@EnableClusterConfiguration
class ClientApplication { .. }

我们可以按关注点将此配置分解如下

包含各种配置的 Spring ClientCache 应用程序(用于启动)
@SpringBootApplication
@Import({ GemFireConfiguration.class, CachingConfiguration.class,
    FunctionsConfiguration.class, QueriesConfiguration.class,
    RepositoriesConfiguration.class })
class ClientApplication { .. }

@ClientCacheApplication
@EnableClusterConfiguration
@EnableGemfireCacheTransactions
class GemFireConfiguration { .. }

@EnableGemfireCaching
@EnableCachingDefinedRegions
class CachingConfiguration { .. }

@EnableGemfireFunctionExecutions
class FunctionsConfiguration { .. }

@EnableContinuousQueries
class QueriesConfiguration {

   @ContinuousQuery(..)
   void processCqEvent(CqEvent event) {
       ...
   }
}

@EnableEntityDefinedRegions
@EnableGemfireRepositories
@EnableIndexing
class RepositoriesConfiguration { .. }

虽然这对 Spring Framework 本身无关紧要,但我们通常建议以代码的可读性为目标,为了下一个必须维护代码的人(将来的某个时候可能就是您自己)。

6.18.2. 其他基于配置的注解

以下 SDG 注解未在本参考文档中讨论,原因可能是该注解支持 Pivotal GemFire 的已弃用特性,或者有更好、更替代的方式来实现该注解提供的功能

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

  • @EnableAutoRegionLookup:不建议使用。本质上,该注解支持查找外部配置元数据(例如应用于服务器时的 cache.xml 或集群配置)中定义的 Region,并自动将这些 Region 注册为 Spring 容器中的 bean。此注解对应于 SDG XML 命名空间中的 <gfe:auto-region-lookup> 元素。更多详细信息请参见此处。使用 Spring 和 Spring Data for Pivotal GemFire 时,用户通常应优先选择 Spring 配置。请参见“配置 Region”和“配置集群配置推送”。

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

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

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

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

6.19. 总结

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

请记住,当您使用新注解时,您仍然可以使用 Java 配置或 XML 配置。您甚至可以在 Spring @Configuration@SpringBootApplication 类上使用 Spring 的 @Import@ImportResource 注解来组合所有三种方法。一旦您明确提供了一个 bean 定义,而该定义原本可以通过 Spring Data for Pivotal GemFire 使用其中一个注解提供,则基于注解的配置就会退出。

在某些情况下,您甚至可能需要回退到 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. 配置 Peer Cache 应用程序

要配置和引导 Pivotal GemFire Peer 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. 配置嵌入式 Locator

使用 @EnableLocator 注解您的 Spring @PeerCacheApplication@CacheServerApplication 类,以启动一个绑定到所有 NICs 并监听默认 Locator 端口 10334 的嵌入式 Locator,如下所示

@SpringBootApplication
@CacheServerApplication
@EnableLocator
public class ServerApplication {

  public static void main(String[] args) {
    SpringApplication.run(ServerApplication.class, args);
  }
}
@EnableLocator 只能用于 Pivotal GemFire 服务器应用程序。

请参阅 @EnableLocator Javadoc

更多详细信息请参阅 配置嵌入式 Locator

6.20.4. 配置嵌入式 Manager

使用 @EnableManager 注解您的 Spring @PeerCacheApplication@CacheServerApplication 类,以启动一个绑定到所有 NICs 并监听默认 Manager 端口 1099 的嵌入式 Manager,如下所示

@SpringBootApplication
@CacheServerApplication
@EnableManager
public class ServerApplication {

  public static void main(String[] args) {
    SpringApplication.run(ServerApplication.class, args);
  }
}
@EnableManager 只能用于 Pivotal GemFire 服务器应用程序。

请参阅 @EnableManager Javadoc

更多详细信息请参阅 配置嵌入式 Manager

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. 配置 Logging

要配置或调整 Pivotal GemFire 的日志记录 (logging),请使用 @EnableLogging 注解您的 Spring、Pivotal GemFire 客户端或服务器应用程序类,如下所示

@SpringBootApplication
@ClientCacheApplication
@EnableLogging(logLevel="trace")
public class ClientApplication {

  public static void main(String[] args) {
    SpringApplication.run(ClientApplication.class, args);
  }
}
默认的 log-level 是“config”。此外,此注解不会调整您的应用程序中的日志级别,仅针对 Pivotal GemFire。

请参阅 @EnableLogging Javadoc

更多详细信息请参阅 配置 Logging

6.20.9. 配置 Statistics

要在运行时收集 Pivotal GemFire 的统计信息 (statistics),请使用 @EnableStatistics 注解您的 Spring、Pivotal GemFire 客户端或服务器应用程序类,如下所示

@SpringBootApplication
@ClientCacheApplication
@EnableStatistics
public class ClientApplication {

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

更多详细信息请参阅 配置 Statistics

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 Serialization 是 Java Serialization 的替代方案,具有许多额外的好处。首先,它能轻松地使您的所有应用程序领域模型类型可序列化,而无需实现 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 和 truststore。keystore 和 truststore 的配置属性或特性可以引用同一个 KeyStore 文件。此外,如果文件已加密,您将需要指定用户名和密码来访问 KeyStore 文件。
Pivotal GemFire SSL 允许您配置系统中需要 TLS 的特定组件,例如 client/server、Locators、Gateways 等。或者,您可以使用“ALL”指定 Pivotal GemFire 的所有组件都使用 SSL。

请参阅 @EnableSsl Javadoc

更多详细信息请参阅 配置 SSL

6.20.12. 配置 Security

要启用 Pivotal GemFire 安全 (security),请使用 @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。更多详细信息请参阅 配置服务器安全
在客户端,您必须配置用户名和密码。更多详细信息请参阅 配置客户端安全

更多详细信息请参阅 配置 Security

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 Region,请使用 @EnableGemfireCaching@EnableCachingDefinedRegions 注解您的 Spring、Pivotal GemFire 客户端或服务器应用程序类,如下所示

@SpringBootApplication
@ClientCacheApplication
@EnableCachingDefinedRegions
@EnableGemfireCaching
public class ClientApplication {

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

然后,只需继续定义需要缓存的应用程序服务,如下所示

@Service
public class BookService {

    @Cacheable("Books")
    public Book findBy(ISBN isbn) {
        ...
    }
}
@EnableCachingDefinedRegions 是可选的。也就是说,如果您愿意,可以手动定义您的 Region。

更多详细信息请参阅 配置 Spring 的缓存抽象

6.20.15. 为持久化应用程序配置 Region、Index、Repository 和 Entity

为了快速创建 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 注解是必需的。更多详细信息请参阅 配置 Index

接下来,定义您的实体类并使用 @Region 映射注解指定存储实体数据的 Region。使用 @Indexed 注解定义用于应用程序查询的实体字段上的 Index,如下所示

package example.app.model;

import ...;

@Region("Books")
public class Book {

  @Id
  private ISBN isbn;

  @Indexed;
  private Author author;

  @Indexed
  private LocalDate published;

  @LuceneIndexed
  private String title;

}
@Region("Books") 实体类注解由 @EnableEntityDefinedRegions 使用,以确定应用程序所需的 Region。更多详细信息请参阅 配置特定类型 RegionPOJO 映射

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

package example.app.repo;

import ...;

public interface BookRepository extends CrudRepository {

  List<Book> findByAuthorOrderByPublishedDesc(Author author);

}
更多详细信息请参阅 Spring Data for Pivotal GemFire Repository

请参阅 @Region Javadoc

请参阅 @Indexed Javadoc

请参阅 @LuceneIndexed Javadoc

更多详细信息请参阅 配置 Region

更多详细信息请参阅 Spring Data for Pivotal GemFire Repository

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

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

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

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

  ...
}

更多详细信息请参阅 配置集群定义的 Region

6.20.17. 配置 Functions

Pivotal GemFire Functions 在分布式计算场景中非常有用,在这种场景下,需要数据的潜在昂贵计算可以在集群中的节点上并行执行。在这种情况下,将逻辑带到数据所在(存储)的地方比请求和获取数据进行计算更高效。

使用 @EnableGemfireFunctions 以及 @GemfireFunction 注解来启用作为 POJO 方法实现的 Pivotal GemFire Functions 定义,如下所示

@PeerCacheApplication
@EnableGemfireFunctions
class ServerApplication {

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

  @GemfireFunction
  Integer computeLoyaltyPoints(Customer customer) {
    ...
  }
}

使用 @EnableGemfireFunctionExecutions 以及以下某个 Function 调用注解:@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);

}

更多详细信息请参阅 Function 执行的注解支持

6.20.18. 配置 Continuous Query

实时、事件流处理对于数据密集型应用程序来说变得越来越重要,主要是为了及时响应用户请求。Pivotal GemFire Continuous Query (CQ) 将帮助您相当轻松地完成这项复杂的任务。

通过使用 @EnableContinuousQueries 注解您的应用程序类来启用 CQ,并按照如下方式定义您的 CQ 以及相关的事件处理器

@ClientCacheApplication
@EnableContinuousQueries
class ClientApplication {

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

然后,通过使用 @ContinuousQuery 注解关联的处理方法来定义您的 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 仅是客户端特性。

更多详细信息请参阅 Continuous Query (CQ)配置 Continuous Query

6.20.19. 配置集群配置

当使用 Pivotal GemFire 作为 Pivotal GemFire ClientCache 应用程序开发 Spring Data 应用程序时,在开发过程中,在客户端/服务器拓扑中配置服务器以与客户端匹配会很有用。事实上,Pivotal GemFire 期望当客户端有一个 "/Example" PROXY Region 时,服务器中存在一个名称匹配的 Region(即 "Example")。

您可以使用 Gfsh 创建应用程序所需的每个 Region 和 Index,或者,您可以在运行使用 Pivotal GemFire 开发的 Spring Data 应用程序时,直接推送已经表达的配置元数据。

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

使用 @EnableClusterConfiguration
@ClientCacheApplication
@EnableClusterConfiguration(useHttp = true)
class ClientApplication {
  ...
}
大多数情况下,当使用客户端/服务器拓扑时,尤其是在生产环境中,集群的服务器将使用 Gfsh 启动。在这种情况下,通常使用 HTTP(S) 将配置元数据(例如 Region 和 Index 定义)发送到集群。使用 HTTP 时,配置元数据会发送到集群中的 Manager,并一致地分发到集群中的服务器节点。
为了使用 @EnableClusterConfiguration,您必须在 Spring 应用程序的 classpath 中声明 org.springframework:spring-web 依赖项。

更多详细信息请参阅 配置集群配置推送

6.20.20. 配置 GatewayReceiver

在不同的 Pivotal GemFire 集群之间复制数据是一种越来越重要的容错和高可用性 (HA) 机制。Pivotal GemFire WAN 复制是一种机制,它允许一个 Pivotal GemFire 集群以可靠且容错的方式将其数据复制到另一个 Pivotal GemFire 集群。

Pivotal GemFire WAN 复制需要配置两个组件

  • GatewayReceiver - 接收来自远程 Pivotal GemFire 集群的 GatewaySender 的数据的 WAN 复制组件。

  • GatewaySender - 将数据发送到远程 Pivotal GemFire 集群的 GatewayReceiver 的 WAN 复制组件。

要启用 GatewayReceiver,应用程序类需要使用 @EnableGatewayReceiver 注解,如下所示

@CacheServerApplication
@EnableGatewayReceiver(manualStart = false, startPort = 10000, endPort = 11000, maximumTimeBetweenPings = 1000,
    socketBufferSize = 16384, bindAddress = "localhost",transportFilters = {"transportBean1", "transportBean2"},
    hostnameForSenders = "hostnameLocalhost"){
      ...
      ...
    }
}
class MySpringApplication { .. }
Pivotal GemFire GatewayReceiver 仅是服务器端特性,只能在 CacheServer 或 peer Cache 节点上配置。

6.20.21. 配置 GatewaySender

要启用 GatewaySender,应用程序类需要使用 @EnableGatewaySenders@EnableGatewaySender 注解,如下所示

@CacheServerApplication
@EnableGatewaySenders(gatewaySenders = {
		@EnableGatewaySender(name = "GatewaySender", manualStart = true,
			remoteDistributedSystemId = 2, diskSynchronous = true, batchConflationEnabled = true,
			parallel = true, persistent = false,diskStoreReference = "someDiskStore",
			orderPolicy = OrderPolicyType.PARTITION, alertThreshold = 1234, batchSize = 100,
			eventFilters = "SomeEventFilter", batchTimeInterval = 2000, dispatcherThreads = 22,
			maximumQueueMemory = 400,socketBufferSize = 16384,
			socketReadTimeout = 4000, regions = { "Region1"}),
		@EnableGatewaySender(name = "GatewaySender2", manualStart = true,
			remoteDistributedSystemId = 2, diskSynchronous = true, batchConflationEnabled = true,
			parallel = true, persistent = false, diskStoreReference = "someDiskStore",
			orderPolicy = OrderPolicyType.PARTITION, alertThreshold = 1234, batchSize = 100,
			eventFilters = "SomeEventFilter", batchTimeInterval = 2000, dispatcherThreads = 22,
			maximumQueueMemory = 400, socketBufferSize = 16384,socketReadTimeout = 4000,
			regions = { "Region2" })
}){
class MySpringApplication { .. }
}
Pivotal GemFire GatewaySender 仅是服务器端特性,只能在 CacheServer 或 peer Cache 节点上配置。

在上面的示例中,应用程序配置了两个 Region:Region1Region2。此外,将配置两个 GatewaySender 来服务这两个 Region。GatewaySender1 将配置为复制 Region1 的数据,而 GatewaySender2 将配置为复制 Region2 的数据。

如所示,每个 GatewaySender 属性可以在每个 EnableGatewaySender 注解上配置。

也可以采用更通用的“默认”属性方法,即所有属性都在 EnableGatewaySenders 注解上配置。这样,可以在父注解上设置一组通用、默认的值,然后如果需要,可以在子注解上覆盖,如下所示

@CacheServerApplication
@EnableGatewaySenders(gatewaySenders = {
		@EnableGatewaySender(name = "GatewaySender", transportFilters = "transportBean1", regions = "Region2"),
		@EnableGatewaySender(name = "GatewaySender2")},
		manualStart = true, remoteDistributedSystemId = 2,
		diskSynchronous = false, batchConflationEnabled = true, parallel = true, persistent = true,
		diskStoreReference = "someDiskStore", orderPolicy = OrderPolicyType.PARTITION, alertThreshold = 1234, batchSize = 1002,
		eventFilters = "SomeEventFilter", batchTimeInterval = 2000, dispatcherThreads = 22, maximumQueueMemory = 400,
		socketBufferSize = 16384, socketReadTimeout = 4000, regions = { "Region1", "Region2" },
		transportFilters = { "transportBean2", "transportBean1" })
class MySpringApplication { .. }
regions 属性留空或未填充时,GatewaySender 将自动连接到应用程序中配置的每个 Region

7. 使用 Pivotal GemFire API

一旦配置了 Pivotal GemFire Cache 和 Region,就可以将它们注入并在应用程序对象内部使用。本章描述了与 Spring 事务管理功能和 DAO 异常层次结构的集成。本章还涵盖了对 Pivotal GemFire 管理对象的依赖注入支持。

7.1. GemfireTemplate

与 Spring 提供的许多其他高级抽象一样,Spring Data for Pivotal GemFire 提供了一个模板来简化 Pivotal GemFire 数据访问操作。该类提供了几个包含常见 Region 操作的方法,同时也提供了使用 GemfireCallback 针对原生 Pivotal GemFire API 执行代码的能力,而无需处理 Pivotal GemFire 的受检异常。

模板类需要一个 Pivotal GemFire Region,并且一旦配置完成,它是线程安全的,可以在多个应用程序类中复用

<bean id="gemfireTemplate" class="org.springframework.data.gemfire.GemfireTemplate" p:region-ref="SomeRegion"/>

一旦模板配置完成,开发人员可以将其与 GemfireCallback 一起使用,直接操作 Pivotal GemFire Region,而无需处理受检异常、线程或资源管理问题

template.execute(new GemfireCallback<Iterable<String>>() {

	public Iterable<String> doInGemfire(Region region)
	        throws GemFireCheckedException, GemFireException {

		Region<String, String> localRegion = (Region<String, String>) region;

		localRegion.put("1", "one");
		localRegion.put("3", "three");

		return localRegion.query("length < 5");
	}
});

为了充分利用 Pivotal GemFire 查询语言的强大功能,开发人员可以使用 findfindUnique 方法,与 query 方法相比,这些方法可以跨多个 Region 执行查询、执行投影等。

当查询选择多个项(通过 SelectResults)时应使用 find 方法,而后者 findUnique,顾名思义,在只返回一个对象时使用。

7.2. 异常转换

使用新的数据访问技术不仅需要适应新的 API,还需要处理该技术特定的异常。

为了适应异常处理情况,Spring Framework 提供了一个技术无关且一致的异常层次结构,将应用程序从专有的、通常是“受检”的异常抽象到一组特定的运行时异常。

正如 Spring Framework 文档中所述,异常转换 可以通过使用 @Repository 注解和 AOP 来透明地应用于您的数据访问对象 (DAO),方法是定义一个 PersistenceExceptionTranslationPostProcessor bean。在使用 Pivotal GemFire 时,只要声明了 CacheFactoryBean(例如使用 <gfe:cache/><gfe:client-cache> 声明),就会启用相同的异常转换功能,CacheFactoryBean 充当异常转换器,并被 Spring 基础设施自动检测并相应地使用。

7.3. 本地缓存事务管理

Spring Framework 最受欢迎的功能之一是事务管理

如果您不熟悉 Spring 的事务抽象,那么我们强烈建议您阅读有关 Spring 的事务管理基础设施的内容,因为它提供了一个一致的编程模型,可以透明地跨多个 API 工作,并且可以通过编程方式或声明方式(最受欢迎的选择)进行配置。

对于 Pivotal GemFire,Spring Data for Pivotal GemFire 提供了一个专用的、每个缓存的 PlatformTransactionManager,一旦声明,它允许通过 Spring 原子地执行 Region 操作

使用 XML 启用事务管理
<gfe:transaction-manager id="txManager" cache-ref="myCache"/>
如果 Pivotal GemFire 缓存使用默认名称 gemfireCache 定义,则可以通过删除 cache-ref 属性来进一步简化上述示例。与 Spring Data for Pivotal GemFire 的其他命名空间元素一样,如果未配置缓存 bean 名称,则将使用上述命名约定。此外,如果未明确指定,事务管理器名称为“gemfireTransactionManager”。

目前,Pivotal GemFire 支持具有读已提交 (read committed) 隔离级别的乐观事务。此外,为了保证这种隔离,开发人员应避免进行手动修改缓存中现有值的原地修改 (in-place changes)。为了防止这种情况发生,事务管理器默认配置缓存使用读时复制 (copy on read) 语义,这意味着每次执行读操作时都会创建一个实际值的克隆。如果需要,可以通过 copyOnRead 属性禁用此行为。

由于在启用读时复制 (copy on read) 时会创建给定键的值的副本,因此您必须随后调用 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 事务中(准备阶段)被定位为“Last Resource”(最后资源)。

许多支持 CMT 的托管环境在基于 JTA 的事务中保持对“Last Resource”(最后资源)、非 XA 兼容资源的支持,尽管 JTA 规范并未实际要求这一点。有关非 XA 兼容“Last Resource”(最后资源)含义的更多信息可以在 Red Hat 的文档中找到。事实上,Red Hat 的 JBoss 项目 Narayana 就是一个这样的 LGPL 开源实现。Narayana 将此称为“Last Resource Commit Optimization”(LRCO)(最后资源提交优化)。更多详细信息请参见此处

然而,无论您是在支持“Last Resource”(最后资源)的开源 JTA 事务管理实现的独立环境中使用 Pivotal GemFire,还是在托管环境(例如 WAS 等 Java EE AS)中使用,Spring Data for Pivotal GemFire 都能为您提供支持。

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

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

#1 独立于您的 Spring [Boot] 和/或 [Data for Pivotal GemFire] 应用,且必须成功完成。

2) 参考 Pivotal GemFire 文档中的步骤 5,当使用 @EnableGemFireAsLastResource 注解时,Spring Data for Pivotal GemFire 的注解支持将尝试为您设置 GemFireCachecopyOnRead 属性。

但是,如果 SDG 的自动配置在这方面不成功,那么您必须在 <gfe:cache><gfe:client-cache> XML 元素中显式设置 copy-on-read 属性,或者在 JavaConfig 中将 CacheFactoryBean 类的 copyOnRead 属性设置为 true。例如:

ClientCache XML

使用 XML 设置 copy-on-read (客户端)
<gfe:client-cache ... copy-on-read="true"/>

ClientCache JavaConfig

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

  ClientCacheFactoryBean gemfireCache = new ClientCacheFactoryBean();

  gemfireCache.setCopyOnRead(true);

  return gemfireCache;
}

Peer Cache XML

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

Peer Cache JavaConfig

使用 JavaConfig 设置 copyOnRead (服务器)
@Bean
CacheFactoryBean gemfireCache() {

  CacheFactoryBean gemfireCache = new CacheFactoryBean();

  gemfireCache.setCopyOnRead(true);

  return gemfireCache;
}
显式设置 copy-on-read 属性或 copyOnRead 属性实际上不是必需的。启用事务管理会自动处理读取时的复制。

3) 此时,您可以跳过 Pivotal GemFire 文档中的步骤 6-8,让 Spring Data Geode 发挥它的魔力。您只需使用 Spring Data for Pivotal GemFire 的 @EnableGemFireAsLastResource 注解来注解您的 Spring @Configuration 类,Spring 的 事务管理 基础设施和 Spring Data for Pivotal GemFire 的 @EnableGemFireAsLastResource 注解配置的组合就能完成这项工作。

配置看起来像这样...​

@Configuration
@EnableGemFireAsLastResource
@EnableTransactionManagement(order = 1)
class GeodeConfiguration {
  ...
}

唯一的要求是...​

3.1) @EnableGemFireAsLastResource 注解必须声明在与 Spring 的 @EnableTransactionManagement 注解相同的 Spring @Configuration 类上。

3.2) @EnableTransactionManagement 注解的 order 属性必须显式设置为一个非 Integer.MAX_VALUEInteger.MIN_VALUE 的整数值 (默认为 Integer.MAX_VALUE)。

当然,您应该知道,当使用 JTA 事务时,您还需要配置 Spring 的 JtaTransactionManager,如下所示:

@Bean
public JtaTransactionManager transactionManager(UserTransaction userTransaction) {

   JtaTransactionManager transactionManager = new JtaTransactionManager();

   transactionManager.setUserTransaction(userTransaction);

   return transactionManager;
}
本地、缓存事务管理一节中的配置适用于此处。Spring Data for Pivotal GemFire 的 GemfireTransactionManager 适用于"仅本地"的缓存事务,而适用于"全局"JTA 事务。因此,在这种情况下,您配置 SDG 的 GemfireTransactionManager。您需要配置 Spring 的 JtaTransactionManager,如上所示。

有关将 Spring 的事务管理与 JTA 结合使用的更多详细信息,请参阅此处

实际上,Spring Data for Pivotal GemFire 的 @EnableGemFireAsLastResource 注解导入的配置包含 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
  }

  ...
}

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

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

当然,#3 是您的应用的职责。

确实,如果配置了适当的日志记录,您将看到正确的事件顺序...​

事务日志输出
2017-Jun-22 11:11:37 TRACE TransactionInterceptor - Getting transaction for [example.app.service.MessageService.send]

2017-Jun-22 11:11:37 TRACE GemFireAsLastResourceConnectionAcquiringAspect - Acquiring {data-store-name} Connection
from {data-store-name} JCA ResourceAdapter registered at [gfe/jca]

2017-Jun-22 11:11:37 TRACE MessageService - PRODUCER [ Message :
[{ @type = example.app.domain.Message, id= MSG0000000000, message = SENT }],
JSON : [{"id":"MSG0000000000","message":"SENT"}] ]

2017-Jun-22 11:11:37 TRACE TransactionInterceptor - Completing transaction for [example.app.service.MessageService.send]

2017-Jun-22 11:11:37 TRACE GemFireAsLastResourceConnectionClosingAspect - Closed {data-store-name} Connection @ [Reference [...]]

有关使用 Pivotal GemFire 缓存级别事务的更多详细信息,请参阅此处

有关在 JTA 事务中使用 Pivotal GemFire 的更多详细信息,请参阅此处

有关将 Pivotal GemFire 配置为“Last Resource”的更多详细信息,请参阅此处

7.5. TransactionEventListener

在使用事务时,可能希望注册一个监听器,以在事务提交之前或之后执行某些操作,或在回滚发生后执行操作。

Spring Data for Pivotal GemFire 通过 @TransactionalEventListener 注解,使创建在事务特定阶段调用的监听器变得容易。用 @TransactionalEventListener 注解的方法(如下所示)将在指定 phase 期间收到从事务方法发布的事件通知。

事务提交后事件监听器
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handleAfterCommit(MyEvent event) {
    // do something after transaction is committed
}

为了调用上述方法,您必须在事务内部发布一个事件,如下所示:

发布事务性事件
@Service
class MyTransactionalService {

  @Autowired
  private final ApplicationEventPublisher applicationEventPublisher;

  @Transactional
  public <Return-Type> someTransactionalServiceMethod() {

    // Perform business logic interacting with and accessing multiple transactional resources atomically, then...

    applicationEventPublisher.publishEvent(new MyEvent(...));
  }

  ...
}

@TransactionalEventListener 注解允许您指定事件处理方法将被调用的事务 phase。选项包括:AFTER_COMMITAFTER_COMPLETIONAFTER_ROLLBACKBEFORE_COMMIT。如果未指定,phase 默认为 AFTER_COMMIT。如果您希望即使没有事务存在时也调用监听器,可以将 fallbackExecution 设置为 true

7.6. 连续查询 (CQ)

Pivotal GemFire 提供了一个强大的功能,称为连续查询 (或 CQ)。

简而言之,CQ 允许开发者创建并注册 OQL 查询,然后在添加到 Pivotal GemFire 的新数据匹配查询条件时自动收到通知。Spring Data for Pivotal GemFire 通过 org.springframework.data.gemfire.listener 包及其监听器容器为 CQ 提供了专用支持;其功能和命名与 Spring Framework 中的 JMS 集成非常相似;实际上,熟悉 Spring 中 JMS 支持的用户应该会感到非常熟悉。

基本上,Spring Data for Pivotal GemFire 允许 POJO 上的方法成为 CQ 的端点。只需定义查询并指定应调用哪个方法以便在有匹配时收到通知。Spring Data for Pivotal GemFire 会处理其余的事情。这与 Java EE 的消息驱动 bean 风格非常相似,但基于 Pivotal GemFire,无需实现任何基类或接口。

目前,连续查询仅在 Pivotal GemFire 的客户端/服务器拓扑中受支持。此外,使用的客户端池需要启用订阅。请参阅 Pivotal GemFire 文档以获取更多信息。

7.6.1. 连续查询监听器容器

Spring Data for Pivotal GemFire 通过使用 SDG 的 ContinuousQueryListenerContainer 来简化 CQ 事件的创建、注册、生命周期和分派,ContinuousQueryListenerContainer 为用户完成了所有繁重的工作。熟悉 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 命名空间来声明事件监听器容器并自动注册监听器。完整的 beans 定义如下所示:

<!-- 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/Region 配置也在 cache.xml 中定义。这使得您的可插拔组件能够专注于应用逻辑,而不是 DataSources 或其他协作者的位置或创建。

然而,如果您正在启动一个全新的项目,建议您直接在 Spring 中配置 Cache、Region 和其他 Pivotal GemFire 可插拔组件。这可以避免继承 Declarable 接口或本节介绍的基类。

有关此方法的更多信息,请参阅以下侧边栏。

消除 Declarable 组件

开发者可以像配置区域中提到的那样,完全通过 Spring 配置自定义类型。这样,开发者无需实现 Declarable 接口,并且可以利用 Spring IoC 容器的所有特性(不仅是依赖注入,还有生命周期和实例管理)。

作为使用 Spring 配置 Declarable 组件的一个示例,考虑以下声明(取自 Declarable Javadoc):

<cache-loader>
   <class-name>com.company.app.DBLoader</class-name>
   <parameter name="URL">
     <string>jdbc://12.34.56.78/mydb</string>
   </parameter>
</cache-loader>

为了简化参数解析、转换和对象初始化,Spring Data for Pivotal GemFire 提供了一个基类(WiringDeclarableSupport),它允许 Pivotal GemFire 用户对象通过一个模板 bean 定义进行注入,或者在缺少模板的情况下,通过 Spring IoC 容器执行自动注入。要利用此特性,用户对象需要扩展 WiringDeclarableSupport,该类在初始化过程中会自动定位声明的 BeanFactory 并执行注入。

为什么需要基类?

当前的 Pivotal GemFire 版本中没有对象工厂的概念,并且声明的类型会直接实例化和使用。换句话说,没有简单的方法可以在 Pivotal GemFire 之外管理对象的创建。

7.7.1. 使用模板 bean 定义进行配置

使用时,WiringDeclarableSupport 会首先尝试定位一个现有的 bean 定义,并将其用作注入模板。除非另行指定,否则组件类名将用作隐式的 bean 定义名称。

在这种情况下,看看我们的 DBLoader 声明会是什么样子:

class DBLoader extends WiringDeclarableSupport implements CacheLoader {

  private DataSource dataSource;

  public void setDataSource(DataSource dataSource){
    this.dataSource = dataSource;
  }

  public Object load(LoaderHelper helper) { ... }
}
<cache-loader>
   <class-name>com.company.app.DBLoader</class-name>
   <!-- no parameter is passed (use the bean's implicit name, which is the class name) -->
</cache-loader>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
    http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
">

  <bean id="dataSource" ... />

  <!-- template bean definition -->
  <bean id="com.company.app.DBLoader" abstract="true" p:dataSource-ref="dataSource"/>
</beans>

在上述场景中,由于未指定参数,因此使用了 ID/名称为 com.company.app.DBLoader 的 bean 作为由 Pivotal GemFire 创建的实例进行注入的模板。对于 bean 名称使用不同约定的情况,可以在 Pivotal GemFire 配置中传入 bean-name 参数:

<cache-loader>
   <class-name>com.company.app.DBLoader</class-name>
   <!-- pass the bean definition template name as parameter -->
   <parameter name="bean-name">
     <string>template-bean</string>
   </parameter>
</cache-loader>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
    http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
">

  <bean id="dataSource" ... />

   <!-- template bean definition -->
   <bean id="template-bean" abstract="true" p:dataSource-ref="dataSource"/>

</beans>
模板 bean 定义不必在 XML 中声明。允许使用任何格式(Groovy、注解等)。

7.7.2. 使用自动注入和注解进行配置

默认情况下,如果没有找到 bean 定义,WiringDeclarableSupport自动注入声明的实例。这意味着,除非实例提供了任何依赖注入的元数据,容器将查找对象的 setter 方法并尝试自动满足这些依赖。然而,开发者也可以使用 JDK 5 注解为自动注入过程提供额外信息。

强烈建议阅读 Spring 文档中关于支持的注解和启用因素的专门章节,以获取更多信息。

例如,上述假设的 DBLoader 声明可以通过以下方式注入 Spring 配置的 DataSource

class DBLoader extends WiringDeclarableSupport implements CacheLoader {

  // use annotations to 'mark' the needed dependencies
  @javax.inject.Inject
  private DataSource dataSource;

  public Object load(LoaderHelper helper) { ... }
}
<cache-loader>
   <class-name>com.company.app.DBLoader</class-name>
   <!-- no need to declare any parameters since the class is auto-wired -->
</cache-loader>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
    http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
">

     <!-- enable annotation processing -->
     <context:annotation-config/>

</beans>

通过使用 JSR-330 注解,CacheLoader 代码得到了简化,因为 DataSource 的位置和创建已外部化,并且用户代码只关注加载过程。DataSource 可以是事务性的、延迟创建的、在多个对象之间共享的,或从 JNDI 中检索的。这些方面可以轻松通过 Spring 容器配置和更改,而无需修改 DBLoader 代码。

7.8. 支持 Spring 缓存抽象

Spring Data for Pivotal GemFire 提供了 Spring 缓存抽象的一个实现,将 Pivotal GemFire 定位为 Spring 缓存基础设施中的一个缓存提供者

要将 Pivotal GemFire 用作后端实现,即 Spring 缓存抽象中的"缓存提供者",只需将 GemfireCacheManager 添加到您的配置中即可:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:cache="http://www.springframework.org/schema/cache"
       xmlns:gfe="https://www.springframework.org/schema/gemfire"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
    http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/cache https://www.springframework.org/schema/cache/spring-cache.xsd
    https://www.springframework.org/schema/gemfire https://www.springframework.org/schema/gemfire/spring-gemfire.xsd
">

  <!-- enable declarative caching -->
  <cache:annotation-driven/>

  <gfe:cache id="gemfire-cache"/>

  <!-- declare GemfireCacheManager; must have a bean ID of 'cacheManager' -->
  <bean id="cacheManager" class="org.springframework.data.gemfire.cache.GemfireCacheManager"
      p:cache-ref="gemfire-cache">

</beans>
如果使用默认的缓存 bean 名称(即 "gemfireCache"),则 CacheManager bean 定义上的 cache-ref 属性不是必需的,例如不带显式 ID 的 <gfe:cache>

当声明了 GemfireCacheManager(单例)bean 实例并启用了声明性缓存(无论是通过 XML 中的 <cache:annotation-driven/> 还是 JavaConfig 中的 Spring @EnableCaching 注解),Spring 缓存注解(例如 @Cacheable)将标识那些将使用 Pivotal GemFire Regions 在内存中缓存数据的“缓存”。

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

举个例子,假设您有一个客户服务应用,其中包含一个执行缓存的 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;
  }
}

当然,您可以自由选择任何您喜欢的 Region 类型(例如 REPLICATE, PARTITION, LOCAL 等)。

关于Spring 缓存抽象的更多详细信息,请再次参考文档

8. 使用 Pivotal GemFire 序列化

为了提高 Pivotal GemFire 内存数据网格的整体性能,Pivotal GemFire 支持一种称为 PDX 的专用序列化协议,该协议比标准 Java 序列化更快,结果更紧凑,并且可以在各种语言平台(Java、C++ 和 .NET)上透明工作。

有关详细信息,请参阅PDX 序列化功能PDX 序列化内部机制

本章讨论了 Spring Data for Pivotal GemFire 简化和改进 Java 中 Pivotal GemFire 自定义序列化的各种方式。

8.1. 注入反序列化实例

序列化对象包含瞬时数据的情况相当常见。瞬时数据通常取决于特定时间点所在的系统或环境。例如,DataSource 是环境特定的。序列化此类信息是无用的,甚至可能危险,因为它仅限于特定的 VM 或机器。对于此类情况,Spring Data for Pivotal GemFire 提供了一个特殊的 Instantiator,它在反序列化过程中为 Pivotal GemFire 创建的每个新实例执行注入。

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

Spring 用户可能会觉得这种方法类似于 @Configurable)。WiringInstantiator 的工作方式类似于 WiringDeclarableSupport,首先尝试定位 bean 定义作为注入模板,否则回退到自动注入。

有关注入功能的更多详细信息,请参阅上一节(注入 Declarable 组件)。

要使用 SDG 的 Instantiator,请将其声明为一个 bean,如下例所示:

<bean id="instantiator" class="org.springframework.data.gemfire.serialization.WiringInstantiator">
  <!-- DataSerializable type -->
  <constructor-arg>org.pkg.SomeDataSerializableClass</constructor-arg>
  <!-- type id -->
  <constructor-arg>95</constructor-arg>
</bean>

在 Spring 容器启动期间,一旦初始化完成,Instantiator 默认会向 Pivotal GemFire 序列化系统注册自己,并在反序列化过程中对 Pivotal GemFire 创建的所有 SomeDataSerializableClass 实例执行注入。

8.2. 自动生成自定义 Instantiators

对于数据密集型应用,随着数据的流入,每台机器上可能会创建大量实例。Pivotal GemFire 使用反射创建新类型,但在某些场景下,这可能会非常耗时。一如既往,进行性能分析以量化是否是这种情况是很好的。对于此类情况,Spring Data for Pivotal GemFire 允许自动生成 Instantiator 类,这些类使用默认构造函数实例化新类型,而无需使用反射。以下示例展示了如何创建一个 instantiator:

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

上面的定义自动生成了两个类的两个 InstantiatorsCustomTypeACustomTypeB),并将它们以用户 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%。要使领域类符合此类优化条件,需要满足一系列约束:

  • 它不能是 private 类。

  • 它不能是非静态的内部类。

  • 它不能是 CGLib 代理类。

  • Spring Data 要使用的构造函数不能是 private 的。

如果符合上述任何标准,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 identifier 属性是 final 的,但在构造函数中设置为 null。该类暴露了一个 withId(…) 方法,用于设置标识符,例如当实例插入数据存储并生成了标识符时。原始的 Person 实例保持不变,因为它创建了一个新的实例。对于其他由存储管理但可能需要为持久化操作更改的属性,通常也采用相同的模式。
2 firstnamelastname 属性是普通的不可变属性,可能通过 getter 暴露。
3 age 属性是基于 birthday 属性的一个不可变但派生的属性。根据所示设计,数据库中的值将覆盖默认值,因为 Spring Data 使用唯一声明的构造函数。即使预期应该优先使用计算值,重要的是这个构造函数也接受 age 作为参数(可能忽略它),否则属性填充步骤将尝试设置 age 字段,但由于它是不可变的且没有 wither 方法而失败。
4 comment 属性是可变的,通过直接设置其字段来填充。
5 remarks 属性是可变的,通过直接设置 comment 字段或通过调用 setter 方法来填充。
6 该类为对象创建暴露了工厂方法和构造函数。这里的核心思想是使用工厂方法而不是额外的构造函数,以避免通过 @PersistenceConstructor 进行构造函数消除歧义的需要。相反,属性的默认值处理在工厂方法内部完成。

9.1.3. 一般建议

  • 尝试坚持使用不可变对象 — 不可变对象创建起来非常简单,因为实例化对象只是调用其构造函数的问题。此外,这避免了您的领域对象充斥着允许客户端代码操纵对象状态的 setter 方法。如果您需要这些方法,最好将它们设置为包保护级别,以便只能由有限数量的共存类型调用。仅通过构造函数进行实例化比属性填充快 30%。

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

  • 使用工厂方法代替重载构造函数以避免 @PersistenceConstructor — 为了获得最佳性能,需要一个包含所有参数的构造函数,我们通常希望暴露更多应用用例特定的构造函数,这些构造函数省略了诸如自动生成标识符等内容。使用静态工厂方法来暴露所有参数构造函数的这些变体是一种成熟的模式。

  • 确保您遵循允许使用生成的 instantiator 和属性访问器类的约束 — 

  • 对于要生成的标识符,仍应使用 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 注解有助于区分多个可能可用的带参数构造函数,明确标记使用哪个被标注的构造函数来构建实体。在没有构造函数或只有一个构造函数的应用程序域类中,可以省略该注解。

除了将实体存储在顶级 Regions 中之外,实体也可以存储在子 Regions 中,如下例所示:

@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 配置注解结合使用时,可以在本地缓存中生成 Regions,无论应用程序是客户端还是对等节点。

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

在 SDG 注解配置模型中使用这些特定类型的 Region 映射注解,可以避免在配置中显式定义这些 Regions。

9.3. Repository 映射

作为通过在实体类上使用 @Region 注解来指定实体存储在哪个 Region 中的替代方案,您也可以在实体的 Repository 接口上指定 @Region 注解。有关更多详细信息,请参见Spring Data for Pivotal GemFire Repositories

然而,假设您想将 Person 记录存储在多个 Pivotal GemFire Regions 中(例如,PeopleCustomers)。那么您可以按如下方式定义相应的 Repository 接口扩展:

@Region("People")
public interface PersonRepository extends GemfireRepository<Person, String> {
…
}

@Region("Customers")
public interface CustomerRepository extends GemfireRepository<Person, String> {
...
}

然后,单独使用每个 Repository,您可以将实体存储在多个 Pivotal GemFire Regions 中,如下例所示:

@Service
class CustomerService {

  CustomerRepository customerRepo;

  PersonRepository personRepo;

  Customer update(Customer customer) {
    customerRepo.save(customer);
    personRepo.save(customer);
    return customer;
  }

您甚至可以将 update 服务方法包装在 Spring 管理的事务中,无论是本地缓存事务还是全局事务。

9.4. MappingPdxSerializer

Spring Data for Pivotal GemFire 提供了一个自定义的 PdxSerializer 实现,名为 MappingPdxSerializer,它使用 Spring Data 映射元数据来自定义实体序列化。

该序列化器还允许您使用 Spring Data 的 EntityInstantiator 抽象来定制实体实例化。默认情况下,序列化器使用 ReflectionEntityInstantiator,它使用映射实体的持久化构造函数。持久化构造函数可以是默认构造函数、唯一声明的构造函数,或者显式标注有 @PersistenceConstructor 的构造函数。

为了为构造函数参数提供参数,序列化器会从提供的 PdxReader 中读取具有命名构造函数参数的字段,这些字段使用 Spring 的 @Value 注解明确标识,如下例所示:

示例 4. 在实体构造函数参数上使用 @Value
public class Person {

  public Person(@Value("#root.thing") String firstName, @Value("bean") String lastName) {
    …
  }
}

以这种方式标注的实体类会将 PdxReader 中的“thing”字段读取并作为构造函数参数 firstname 的参数值传递。lastName 的值是一个名为“bean”的 Spring bean。

除了 EntityInstantiators 提供的自定义实例化逻辑和策略外,MappingPdxSerializer 还提供了远超 Pivotal GemFire 自身 ReflectionBasedAutoSerializer 的功能。

虽然 Pivotal GemFire 的 ReflectionBasedAutoSerializer 可以方便地使用 Java 反射来填充实体,并使用正则表达式来识别应由序列化器处理(序列化和反序列化)的类型,但它与 MappingPdxSerializer 不同,无法执行以下操作:

  • 按实体字段或属性名称和类型注册自定义 PdxSerializer 对象。

  • 方便地识别 ID 属性。

  • 自动处理只读属性。

  • 自动处理瞬态属性。

  • 允许以一种无 null 且类型安全的方式进行更健壮的类型过滤(例如,不限于仅使用正则表达式表达类型)。

现在我们更详细地探讨 MappingPdxSerializer 的每个特性。

9.4.1. 自定义 PdxSerializer 注册

MappingPdxSerializer 使您能够根据实体的字段或属性名称和类型注册自定义 PdxSerializers

例如,假设您定义了一个表示 User 的实体类型,如下所示:

package example.app.security.auth.model;

public class User {

  private String name;

  private Password password;

  ...
}

虽然用户的姓名可能不需要任何特殊的逻辑来序列化其值,但另一方面,序列化密码可能需要额外的逻辑来处理该字段或属性的敏感性。

也许您想在网络上(客户端和服务器之间)发送密码值时保护它,超出 TLS 本身提供的保护,并且您只想存储加盐的哈希值。使用 MappingPdxSerializer 时,您可以注册一个自定义 PdxSerializer 来处理用户的密码,如下所示:

示例 5. 按 POJO 字段/属性类型注册自定义 PdxSerializers
Map<?, PdxSerializer> customPdxSerializers = new HashMap<>();

customPdxSerializers.put(Password.class, new SaltedHashPasswordPdxSerializer());

mappingPdxSerializer.setCustomPdxSerializers(customPdxSerializers);

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

然而,假设您只想在 User 对象上定制 Passwords 的序列化。为此,您可以通过指定 Class 字段或属性的完全限定名称来为 User 类型注册自定义 PdxSerializer,如下例所示:

示例 6. 按 POJO 字段/属性名称注册自定义 PdxSerializers
Map<?, PdxSerializer> customPdxSerializers = new HashMap<>();

customPdxSerializers.put("example.app.security.auth.model.User.password", new SaltedHashPasswordPdxSerializer());

mappingPdxSerializer.setCustomPdxSerializers(customPdxSerializers);

请注意使用完全限定的字段或属性名称(即 example.app.security.auth.model.User.password)作为自定义 PdxSerializer 的注册键。

您可以使用更符合逻辑的代码片段来构建注册键,例如:User.class.getName().concat(".password");。我们推荐使用此方法,而不是前面示例所示的方法。前面示例试图尽可能明确地说明注册的语义。

9.4.2. 映射 ID 属性

与 Pivotal GemFire 的 ReflectionBasedAutoSerializer 类似,SDG 的 MappingPdxSerializer 也能够确定实体的标识符。然而,MappingPdxSerializer 是通过使用 Spring Data 的映射元数据来实现的,特别是通过查找使用 Spring Data 的 @Id 注解指定为标识符的实体属性。或者,任何未显式标注有 @Id 但名为“id”的字段或属性,也被指定为实体的标识符。

例如

class Customer {

  @Id
  Long id;

  ...
}

在这种情况下,在序列化期间调用 PdxSerializer.toData(..) 方法时,Customerid 字段会使用 PdxWriter.markIdentifierField(:String) 在 PDX 类型元数据中被标记为标识符字段。

9.4.3. 映射只读属性

当您的实体定义了只读属性时会发生什么?

首先,了解什么是“只读”属性很重要。如果您按照 JavaBeans 规范(Spring 遵循该规范)定义 POJO,您可能会定义一个带有只读属性的 POJO,如下所示:

package example;

class ApplicationDomainType {

  private AnotherType readOnly;

  public AnotherType getReadOnly() [
    this.readOnly;
  }

  ...
}

readOnly 属性是只读的,因为它没有提供 setter 方法,只有 getter 方法。在这种情况下,readOnly 属性(不要与 readOnly DomainType 字段混淆)被认为是只读的。

因此,在反序列化期间调用 PdxSerializer.fromData(:Class<ApplicationDomainType>, :PdxReader) 方法填充 ApplicationDomainType 实例时,MappingPdxSerializer 不会尝试为此属性设置值,特别是当 PDX 序列化字节中存在该值时。

这在某些情况下很有用,例如您可能正在返回某个实体类型的视图或投影,并且只想设置可写状态。也许实体的视图或投影基于授权或其他条件。关键是,您可以根据应用程序的使用场景和需求适当地利用此特性。如果您希望该字段或属性始终可写,只需定义一个 setter 方法即可。

9.4.4. 映射瞬态属性

同样,当您的实体定义了 transient 属性时会发生什么?

您可能期望实体的 transient 字段或属性在序列化实体时不会被序列化到 PDX。这正是实际发生的情况,与 Pivotal GemFire 自身的 ReflectionBasedAutoSerializer 不同,后者会序列化通过 Java Reflection 从对象访问到的所有内容。

MappingPdxSerializer 不会序列化任何被标记为瞬态的字段或属性,无论是在类实例字段上使用 Java 自带的 transient 关键字,还是在字段或属性上使用 @Transient Spring Data 注解。

例如,您可以定义一个包含瞬态字段和属性的实体,如下所示:

package example;

class Process {

  private transient int id;

  private File workingDirectory;

  private String name;

  private Type type;

  @Transient
  public String getHostname() {
    ...
  }

  ...
}

无论是 Processid 字段还是可读的 hostname 属性都不会写入 PDX。

9.4.5. 按类类型过滤

与 Pivotal GemFire 的 ReflectionBasedAutoSerializer 类似,SDG 的 MappingPdxSerializer 允许您过滤要序列化和反序列化的对象类型。

然而,与使用复杂正则表达式来表达序列化器处理哪些类型的 Pivotal GemFire 的 ReflectionBasedAutoSerializer 不同,SDG 的 MappingPdxSerializer 使用更强大的 java.util.function.Predicate 接口和 API 来表达类型匹配条件。

如果您喜欢使用正则表达式,可以使用 Java 的 正则表达式支持来实现一个 Predicate

Java 的 Predicate 接口的优点在于,您可以使用方便且合适的 API 方法来组合 Predicates,包括:and(:Predicate)or(:Predicate)negate()

以下示例展示了 Predicate API 的实际应用:

Predicate<Class<?>> customerTypes =
  type -> Customer.class.getPackage().getName().startsWith(type.getName()); // Include all types in the same package as `Customer`

Predicate includedTypes = customerTypes
  .or(type -> User.class.isAssignble(type)); // Additionally, include User sub-types (e.g. Admin, Guest, etc)

mappingPdxSerializer.setIncludeTypeFilters(includedTypes);

mappingPdxSerializer.setExcludeTypeFilters(
  type -> !Reference.class.getPackage(type.getPackage()); // Exclude Reference types
传递给您的 Predicate 的任何 Class 对象都保证不为 null

SDG 的 MappingPdxSerializer 支持包含和排除类类型过滤器。

排除类型过滤

默认情况下,SDG 的 MappingPdxSerializer 会注册预定义的 Predicates,用于过滤或排除以下包中的类型:

  • java.*

  • com.gemstone.gemfire.*

  • org.apache.geode.*

  • org.springframework.*

此外,在调用 PdxSerializer.toData(:Object, :PdxWriter) 方法时,MappingPdxSerializer 会过滤 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<?>>)

当存在包含类型过滤器时,当类类型未被隐式排除,或者当类类型被显式包含(两者中返回 true 的那个)时,MappingPdxSerializer 会决定是否反/序列化该类类型的实例。然后,该类类型的实例将被适当地序列化或反序列化。

例如,如前所示,当显式注册 Predicate<Class<Principal>> 类型的过滤器时,它会抵消 java.* 包类型的隐式排除类型过滤器。

10. Spring Data for Pivotal GemFire Repositories

Spring Data for Pivotal GemFire 提供使用 Spring Data Repository 抽象来轻松地将实体持久化到 Pivotal GemFire 中并执行查询的支持。Repository 编程模型的通用介绍请参见此处

10.1. Spring XML 配置

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

示例 7. 在 XML 中引导 Spring Data for Pivotal GemFire Repositories
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:gfe-data="https://www.springframework.org/schema/data/gemfire"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
    http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
    https://www.springframework.org/schema/data/gemfire https://www.springframework.org/schema/data/gemfire/spring-data-gemfire.xsd
">

  <gfe-data:repositories base-package="com.example.acme.repository"/>

</beans>

前面的配置片段会在配置的基础包下查找接口,并为这些接口创建由 SimpleGemFireRepository 支持的 Repository 实例。

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

10.2. Spring 基于 Java 的配置

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

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

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

您可能更倾向于使用类型安全的 basePackageClasses 属性,而不是使用 basePackages 属性。basePackageClasses 允许您通过仅指定一个应用程序 Repository 接口类型来指定包含所有应用程序 Repository 类的包。可以考虑在每个包中创建一个特殊的无操作标记类或接口,其唯一目的就是标识此属性引用的应用程序 Repositories 的位置。

除了 basePackagesbasePackageClasses 属性之外,与 Spring 的 @ComponentScan 注解类似,@EnableGemfireRepositories 注解还基于 Spring 的 ComponentScan.Filter 类型提供包含和排除过滤器。您可以使用 filterType 属性按不同方面进行过滤,例如应用程序 Repository 类型是否标注有特定注解或是否扩展特定类类型等。有关更多详细信息,请参阅 FilterType Javadoc

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

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

使用 Pivotal GemFire 时需要自定义 Repository 实现的一个例子是执行 join 操作。SDG Repositories 不支持 join。对于 Pivotal GemFire 的 PARTITION Region,join 必须在 collocated 的 PARTITION Regions 上执行,因为 Pivotal GemFire 不支持“分布式”join。此外,Equi-Join OQL 查询必须在 Pivotal GemFire 函数内部执行。有关 Pivotal GemFire Equi-Join Queries 的更多详细信息,请参见此处

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

10.3. 执行 OQL 查询

Spring Data for Pivotal GemFire Repositories 允许定义查询方法,以便轻松地对托管实体映射到的 Region 执行 Pivotal GemFire OQL 查询,如下例所示:

示例 9. 示例 Repository
@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. 查询方法支持的关键字
关键字 示例 逻辑结果

大于

findByAgeGreaterThan(int age)

x.age > $1

大于等于

findByAgeGreaterThanEqual(int age)

x.age >= $1

小于

findByAgeLessThan(int age)

x.age < $1

小于等于

findByAgeLessThanEqual(int age)

x.age ⇐ $1

不为空

findByFirstnameNotNull()

x.firstname =! NULL

为空

findByFirstnameNull()

x.firstname = NULL

在集合中

findByFirstnameIn(Collection<String> x)

x.firstname IN SET $1

不在集合中

findByFirstnameNotIn(Collection<String> x)

x.firstname NOT IN SET $1

忽略大小写

findByFirstnameIgnoreCase(String firstName)

x.firstname.equalsIgnoreCase($1)

(无关键字)

findByFirstname(String name)

x.firstname = $1

模糊匹配

findByFirstnameLike(String name)

x.firstname LIKE $1

不等于

findByFirstnameNot(String name)

x.firstname != $1

为 True

findByActiveIsTrue()

x.active = true

为 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

@Hint

OQL 查询索引提示

String[] (例如 @Hint({ "IdIdx", "TxDateIdx" }))

IMPORT

@Import

限定特定于应用程序的类型。

String (例如 @Import("org.example.app.domain.Type"))

LIMIT

@Limit

限制返回的查询结果集。

Integer (例如 @Limit(10);默认为 Integer.MAX_VALUE)

TRACE

@Trace

启用 OQL 查询特定调试。

不适用

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

示例 10. 示例 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

当 OQL 注解扩展与 @Query 注解结合使用时,Spring Data for Pivotal GemFire 的 Repository 扩展会谨慎处理,以免产生冲突的声明。

再例如,假设您在 CustomerRepository 中定义了一个带有原始 @Query 注解的查询方法,如下所示:

示例 11. CustomerRepository
public interface CustomerRepository extends GemfireRepository<Customer, Long> {

  @Trace
  @Limit(10)
  @Hint("CustomerIdx")
  @Import("org.example.app.domain.Customer")
  @Query("<TRACE> <HINT 'ReputationIdx'> SELECT DISTINCT * FROM /Customers c WHERE c.reputation > $1 ORDER BY c.reputation DESC LIMIT 5")
  List<Customer> findDistinctCustomersByReputationGreaterThanOrderByReputationDesc(Integer reputation);

}

前面的查询方法会生成以下 OQL 查询:

IMPORT org.example.app.domain.Customer; <TRACE> <HINT 'ReputationIdx'> SELECT DISTINCT * FROM /Customers x WHERE x.reputation > $1 ORDER BY c.reputation DESC LIMIT 5

@Limit(10) 注解不会覆盖原始查询中显式定义的 LIMIT。同样,@Hint("CustomerIdx") 注解也不会覆盖原始查询中显式定义的 HINT。最后,@Trace 注解是多余的,没有额外效果。

考虑到可能有大量客户具有相同的信誉值,这会降低索引的效率,因此 ReputationIdx 索引可能不是最明智的选择。请明智地选择索引和其他优化,因为不当或选择不佳的索引可能会因为维护索引的开销而对您的性能产生相反的影响。ReputationIdx 仅用于示例目的。

10.5. 查询后处理

得益于使用 Spring Data Repository 抽象,定义特定于数据存储的查询(例如 OQL)的查询方法约定既简单又方便。然而,有时仍然希望检查甚至可能修改从 Repository 查询方法生成的查询。

自 2.0.x 版本以来,Spring Data for Pivotal GemFire 包含了 o.s.d.gemfire.repository.query.QueryPostProcessor 函数式接口。该接口的大致定义如下:

示例 12. QueryPostProcessor
package org.springframework.data.gemfire.repository.query;

import org.springframework.core.Ordered;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.query.QueryMethod;
import ...;

@FunctionalInterface
interface QueryPostProcessor<T extends Repository, QUERY> extends Ordered {

  QUERY postProcess(QueryMethod queryMethod, QUERY query, Object... arguments);

}

还提供了额外的默认方法,允许您组合 QueryPostProcessor 实例,类似于 java.util.function.Function.andThen(:Function)java.util.function.Function.compose(:Function) 的工作方式。

此外,QueryPostProcessor 接口实现了 org.springframework.core.Ordered 接口,当在 Spring 容器中声明和注册多个 QueryPostProcessors 并用于为一组生成的查询方法查询创建处理管道时,这非常有用。

最后,QueryPostProcessor 接受与类型参数 TQUERY 对应的类型参数。类型 T 扩展了 Spring Data Commons 标记接口 org.springframework.data.repository.Repository。我们将在本节后面进一步讨论。在 Spring Data for Pivotal GemFire 的情况下,所有 QUERY 类型参数参数的类型都是 java.lang.String

将查询定义为 QUERY 类型很有用,因为这个 QueryPostProcessor 接口可能会移植到 Spring Data Commons,因此必须处理不同数据存储(如 JPA、MongoDB 或 Redis)的所有形式的查询。

您可以实现此接口,以便在调用应用程序 Repository 接口方法时,接收带有从该方法生成的查询的回调。

例如,您可能希望记录所有应用程序 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 始终在 Spring ApplicationContext 中可能已声明和注册的其他应用程序定义的 QueryPostProcessors 之后执行,您可以通过覆盖 o.s.core.Ordered.getOrder() 方法来设置 order 属性,如下例所示:

示例 18. 定义 order 属性
class LoggingQueryPostProcessor implements QueryPostProcessor<Repository, String> {

  @Override
  int getOrder() {
    return 1;
  }
}

class CustomerQueryPostProcessor implements QueryPostProcessor<CustomerRepository, String> {

  @Override
  int getOrder() {
    return 0;
  }
}

这确保您在 LoggingQueryPostProcessor 记录查询之前,始终能看到由其他 QueryPostProcessors 应用的后处理效果。

您可以在 Spring ApplicationContext 中定义任意数量的 QueryPostProcessors,并按任意顺序将它们应用于所有或特定的应用程序 Repository 接口,并且通过使用提供给 postProcess(..) 方法回调的参数,您可以做到所需的粒度。

11. Function 执行的注解支持

Spring Data for Pivotal GemFire 包含注解支持,以简化 Pivotal GemFire Function 执行的使用。

在底层,Pivotal GemFire API 提供了用于实现和注册部署在 Pivotal GemFire 服务器上的 Pivotal GemFire Functions 的类,这些 Functions 可以由其他对等成员应用程序或从缓存客户端远程调用。

Functions 可以在集群中的多个 Pivotal GemFire 服务器上并行、分布式执行,使用 map-reduce 模式聚合结果并发送回调用者。Functions 也可以指定在单个服务器或 Region 上运行。Pivotal GemFire API 支持使用各种预定义的作用域定位 Function 并远程执行:在 Region 上、在成员上(按组)、在服务器上等。远程 Functions 的实现和执行,与任何 RPC 协议一样,需要一些样板代码。

Spring Data for Pivotal GemFire 秉承 Spring 的核心价值主张,旨在隐藏远程 Function 执行的机制,让您专注于核心 POJO 编程和业务逻辑。为此,Spring Data for Pivotal GemFire 引入了注解,以声明式地将 POJO 类的公共方法注册为 Pivotal GemFire Functions,并能够通过使用带注解的接口来调用已注册的 Functions(包括远程调用)。

11.1. 实现与执行

需要解决两个独立的问题:实现和执行。

第一个是 Function 实现(服务器端),它必须与 FunctionContext 交互以访问调用参数,与 ResultsSender 交互以发送结果,以及获取其他执行上下文信息。Function 实现通常会访问缓存和 Regions,并使用唯一的 ID 在 FunctionService 中注册。

调用函数的缓存客户端应用程序不依赖于具体实现。为了调用函数,应用程序实例化一个 Execution,提供函数 ID、调用参数以及定义其作用域的函数目标:Region、server、servers、member 或 members。如果函数产生结果,调用者使用 ResultCollector 来聚合和获取执行结果。在某些情况下,需要自定义 ResultCollector 实现,并且可以将其注册到 Execution 中。

此处使用的“Client”和“Server”是在函数执行的上下文中,其含义可能与 Pivotal GemFire 客户端-服务器拓扑中的客户端和服务器不同。虽然使用 ClientCache 实例的应用程序通常会在集群中的一个或多个 Pivotal GemFire 服务器上调用函数,但也可以在点对点 (P2P) 配置中执行函数,其中应用程序是托管对等 Cache 实例的集群成员。请记住,对等成员缓存应用程序受制于作为集群对等成员的所有限制。

11.2. 实现一个函数

使用 Pivotal GemFire API,FunctionContext 提供运行时调用上下文,其中包含客户端的调用参数和用于将结果发送回客户端的 ResultSender 实现。此外,如果函数在 Region 上执行,则 FunctionContext 实际上是 RegionFunctionContext 的一个实例,它提供额外信息,例如函数被调用的目标 Region、与 Execution 关联的任何过滤器(一组特定键)等等。如果 Region 是 PARTITION Region,函数应使用 PartitionRegionHelper 提取本地数据集。

通过使用 Spring,您可以编写一个简单的 POJO 并使用 Spring 容器将您的 POJO 的一个或多个公共方法绑定到函数。用作函数的目标 POJO 方法的签名通常必须符合客户端的执行参数。然而,在 Region 执行的情况下,也可以提供 Region 数据(如果 Region 是 PARTITION Region,数据通常保存在本地分区中)。

此外,如果应用了过滤器,函数可能需要该过滤器。这表明客户端和服务器共享调用参数的契约,但方法签名可能包含额外的参数,以传递由 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) { ... }

一般规则是,一旦解析了任何附加参数(即 Region 数据和过滤器),剩余的参数必须与预期的函数方法参数完全一致,包括顺序和类型。方法的返回类型必须是 void 或可序列化的类型(作为 java.io.SerializableDataSerializablePdxSerializable)。后者也是调用参数的要求。

为了便于单元测试,Region 数据通常应定义为 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 组件(例如缓存和 Region)共享 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 可以是 Region 的名称或 {data-store-javadoc}/org/apache/geode/security/ResourcePermission.Target.html[ResourcePermission.Target] 枚举值之一。最后,可选地,如果指定了 Target Region,则 Key 是该 Region 中的有效键。

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)以及(可选)过滤器集。通过使用 Spring Data for Pivotal GemFire,您只需定义一个由注解支持的接口。Spring 会为该接口创建一个动态代理,该代理使用 FunctionService 创建一个 Execution,调用该 Execution,并在必要时将结果强制转换为定义的返回类型。这种技术类似于 Spring Data for Pivotal GemFire 的 Repository 扩展的工作方式。因此,一些配置和概念应该很熟悉。

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

11.3.1. 用于函数执行的注解

为了支持客户端函数执行,提供了以下 SDG 函数注解:@OnRegion@OnServer@OnServers@OnMember@OnMembers。这些注解对应于 Pivotal GemFire 的 FunctionService 类提供的 Execution 实现。

每个注解都暴露了适当的属性。这些注解还提供一个可选的 resultCollector 属性,其值是 Spring Bean 的名称,该 Bean 实现了 ResultCollector 接口,用于该执行。

代理接口将所有声明的方法绑定到相同的执行配置。尽管单方法接口很常见,但接口中的所有方法都由同一个代理实例支持,因此都共享相同的配置。

以下列表显示了几个示例

@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。filter 参数是可选的。剩余的参数是可变参数 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 序列化),您还可以将 pdx-read-serialized 属性在 Pivotal GemFire 服务器的配置中设置为 true,如下所示

<gfe:cache ... pdx-read-serialized="true"/>

或者,您可以在 Pivotal GemFire 缓存客户端应用程序中将 pdx-read-serialized 属性设置为 true,如下所示

<gfe:client-cache ... pdx-read-serialized="true"/>

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

Pivotal GemFire 只序列化您通过使用 Pivotal GemFire 的 ReflectionBasedAutoSerializer 或通过使用“自定义”Pivotal GemFire PdxSerializer 明确配置(注册)的应用程序领域对象类型。如果您使用 Spring Data for Pivotal GemFire 的 Repository 扩展,您甚至可能希望考虑使用 Spring Data for Pivotal GemFire 的 MappingPdxSerializer,它使用实体映射元数据来确定从应用程序领域对象序列化到 PDX 实例的数据。

然而,不太明显的是,Pivotal GemFire 会自动处理 Java Enum 类型,无论它们是否被明确配置(即,使用正则表达式模式和 classes 参数注册到 ReflectionBasedAutoSerializer 中,或由“自定义”Pivotal GemFire PdxSerializer 处理),尽管 Java 枚举实现了 java.io.Serializable

因此,当您在注册和使用 Pivotal GemFire 函数(包括 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) {
 ...
}

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

有关自定义、组合的特定于应用程序的 Pivotal GemFire PdxSerializers 以及基于方法签名的适当 POJO 函数参数类型处理的良好示例,请参见 Spring Data for Pivotal GemFire 的 ClientCacheFunctionExecutionWithPdxIntegrationTest 类。

12. Apache Lucene 集成

Pivotal GemFire 集成了 Apache Lucene,让您可以使用 Lucene 查询对存储在 Pivotal GemFire 中的数据进行索引和搜索。基于搜索的查询还包括分页查询结果的功能。

此外,Spring Data for Pivotal GemFire 添加了对基于 Spring Data Commons 投影基础结构的查询投影的支持。此功能允许根据应用程序的需要将查询结果投影到一流的应用程序领域类型。

在运行任何基于 Lucene 搜索的查询之前,必须创建一个 Lucene Index。可以在 Spring (Data for Pivotal GemFire) XML 配置中创建 LuceneIndex,如下所示

<gfe:lucene-index id="IndexOne" fields="fieldOne, fieldTwo" region-path="/Example"/>

此外,Apache Lucene 允许按字段指定 分析器,并可以按如下示例进行配置

<gfe:lucene-index id="IndexTwo" lucene-service-ref="luceneService" region-path="/AnotherExample">
    <gfe:field-analyzers>
        <map>
            <entry key="fieldOne">
                <bean class="example.AnalyzerOne"/>
             </entry>
            <entry key="fieldTwo">
                <bean class="example.AnalyzerTwo"/>
             </entry>
        </map>
    </gfe:field-analyzers>
</gfe:lucene-index>

Map 可以指定为顶层 Bean 定义,并通过嵌套的 <gfe:field-analyzers> 元素的 ref 属性引用,如下所示:<gfe-field-analyzers ref="refToTopLevelMapBeanDefinition"/>

Spring Data for Pivotal GemFire 的 LuceneIndexFactoryBean API 和 SDG 的 XML 命名空间还允许在创建 LuceneIndex 时指定 org.apache.geode.cache.lucene.LuceneSerializerLuceneSerializer 允许您在对象被索引时配置对象如何转换为 Lucene 文档以用于索引。

以下示例展示了如何向 LuceneIndex 添加 LuceneSerializer

<bean id="MyLuceneSerializer" class="example.CustomLuceneSerializer"/>

<gfe:lucene-index id="IndexThree" lucene-service-ref="luceneService" region-path="/YetAnotherExample">
    <gfe:lucene-serializer ref="MyLuceneSerializer">
</gfe:lucene-index>

您也可以将 LuceneSerializer 指定为匿名、嵌套的 Bean 定义,如下所示

<gfe:lucene-index id="IndexThree" lucene-service-ref="luceneService" region-path="/YetAnotherExample">
    <gfe:lucene-serializer>
        <bean class="example.CustomLuceneSerializer"/>
    </gfe:lucene-serializer>
</gfe:lucene-index>

或者,您可以在 Spring Java 配置中,在 @Configuration 类中声明或定义 LuceneIndex,如下例所示

@Bean(name = "Books")
@DependsOn("bookTitleIndex")
PartitionedRegionFactoryBean<Long, Book> booksRegion(GemFireCache gemfireCache) {

    PartitionedRegionFactoryBean<Long, Book> peopleRegion =
        new PartitionedRegionFactoryBean<>();

    peopleRegion.setCache(gemfireCache);
    peopleRegion.setClose(false);
    peopleRegion.setPersistent(false);

    return peopleRegion;
}

@Bean
LuceneIndexFactoryBean bookTitleIndex(GemFireCache gemFireCache,
        LuceneSerializer luceneSerializer) {

    LuceneIndexFactoryBean luceneIndex = new LuceneIndexFactoryBean();

    luceneIndex.setCache(gemFireCache);
    luceneIndex.setFields("title");
    luceneIndex.setLuceneSerializer(luceneSerializer);
    luceneIndex.setRegionPath("/Books");

    return luceneIndex;
}

@Bean
CustomLuceneSerializer myLuceneSerialier() {
    return new CustomeLuceneSerializer();
}

Pivotal GemFire 的 Apache Lucene 集成和支持存在一些限制。

首先,LuceneIndex 只能在 Pivotal GemFire PARTITION Region 上创建。

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

为了帮助确保 Spring 容器中定义的所有声明的 LuceneIndexes 在其适用的 Region 创建之前创建,SDG 包括 org.springframework.data.gemfire.config.support.LuceneIndexRegionBeanFactoryPostProcessor。您可以使用 <bean class="org.springframework.data.gemfire.config.support.LuceneIndexRegionBeanFactoryPostProcessor"/> 在 XML 配置中注册此 Spring BeanFactoryPostProcessoro.s.d.g.config.support.LuceneIndexRegionBeanFactoryPostProcessor 只能在使用 SDG XML 配置时使用。有关 Spring 的 BeanFactoryPostProcessors 的更多详细信息可以在此处找到。

这些 Pivotal GemFire 限制可能会在未来版本中不再适用,这就是为什么 SDG LuceneIndexFactoryBean API 也直接接受对 Region 的引用,而不仅仅是 Region 路径。

当您希望在应用程序生命周期稍后阶段和需求允许的情况下,在具有数据的现有 Region 上定义 LuceneIndex 时,这更理想。在可能的情况下,SDG 努力遵循强类型对象。但是,目前,您必须使用 regionPath 属性来指定应用 LuceneIndex 的 Region。

此外,在上面的示例中,请注意 Books Region Bean 定义上存在 Spring 的 @DependsOn 注解。这在 Books Region Bean 和 bookTitleIndex LuceneIndex Bean 定义之间创建了依赖关系,确保 LuceneIndex 在其适用的 Region 创建之前创建。

现在我们有了 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 投影基础结构将查询结果应用于给定投影类型的实例。

此外,模板将分页的 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 投影基础结构唯一的限制是投影类型必须是一个接口。但是,可以扩展提供的 SDC 投影基础结构并提供一个自定义的 ProjectionFactory,该工厂使用 CGLIB 生成代理类作为投影实体。

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

12.2. 注解配置支持

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

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

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

@PartitionRegion("People")
class Person {

    Gender gender;

    @Index
    LocalDate birthDate;

    String firstName;

    @LuceneIndex;
    String lastName;

    ...
}

要启用此功能,您必须使用 SDG 的注解配置支持,特别是使用 @EnableEntityDefineRegions@EnableIndexing 注解,如下所示

@PeerCacheApplication
@EnableEntityDefinedRegions
@EnableIndexing
class ApplicationConfiguration {

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

鉴于我们之前对 Person 类的定义,SDG 注解配置支持会找到 Person 实体类定义,并确定人员存储在名为“People”的 PARTITION Region 中,并且 PersonbirthDate 上有一个 OQL Index,以及在 lastName 上有一个 LuceneIndex

13. 在 Pivotal GemFire 中引导 Spring ApplicationContext

通常,基于 Spring 的应用程序使用 Spring Data for Pivotal GemFire 的功能引导 Pivotal GemFire。通过指定使用 Spring Data for Pivotal GemFire XML 命名空间的 <gfe:cache/> 元素,会在与您的应用程序相同的 JVM 进程中创建并初始化一个嵌入式 Pivotal GemFire 对等 Cache 实例,并使用默认设置。

然而,有时需要(也许是您的 IT 组织强加的要求)通过提供的 Pivotal GemFire 工具套件(可能使用 Gfsh)来完全管理和操作 Pivotal GemFire。使用 Gfsh,Pivotal GemFire 会引导您的 Spring ApplicationContext,而不是相反。Pivotal GemFire 会执行引导并托管您的应用程序,而不是由应用程序服务器或使用 Spring Boot 的 Java main 类。

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

13.1. 使用 Pivotal GemFire 引导由 Gfsh 启动的 Spring Context

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

初始化器通过使用 Pivotal GemFire 原生 cache.xml 的最小片段,在 initializer 元素内声明。为了引导 Spring ApplicationContext,需要一个 cache.xml 文件,这很像引导配置了组件扫描的 Spring ApplicationContext 需要一个最小的 Spring XML 配置片段(例如 <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>

然后,在 Gfsh 中启动 Pivotal GemFire 服务器时,将正确配置和构建的 CLASSPATH 以及 cache.xml 文件(如前所示)指定为命令行选项,命令行将如下所示:

gfsh>start server --name=ExampleServer --log-level=config ...
    --classpath="/path/to/application/classes.jar:/path/to/spring-data-geode-<major>.<minor>.<maint>.RELEASE.jar"
    --cache-xml-file="/path/to/geode/cache.xml"

application-context.xml 可以是任何有效的 Spring 配置元数据,包括所有 SDG XML 命名空间元素。这种方法的唯一限制是不能使用 SDG XML 命名空间来配置 Pivotal GemFire 缓存。换句话说,<gfe:cache/> 元素的任何属性(如 cache-xml-locationproperties-refcritical-heap-percentagepdx-serializer-reflock-lease 等)都不能指定。如果使用了,这些属性将被忽略。

原因是 Pivotal GemFire 本身在初始化器被调用之前就已经创建并初始化了缓存。因此,缓存已经存在,并且由于它是“单例”,它不能被重新初始化或对其任何配置进行增强。

13.2. 延迟布线 Pivotal GemFire 组件

Spring Data for Pivotal GemFire 已经通过使用 SDG 的 WiringDeclarableSupport 类提供了对在 cache.xml 中由 Pivotal GemFire 声明和创建的 Pivotal GemFire 组件(例如 CacheListenersCacheLoadersCacheWriters 等)进行自动布线的支持,如 使用自动布线和注解的配置 中所述。然而,这仅在 Spring 进行引导时(即 Spring 引导 Pivotal GemFire 时)有效。

当你的 Spring ApplicationContext 由 Pivotal GemFire 引导时,这些 Pivotal GemFire 应用程序组件不会被注意到,因为 Spring ApplicationContext 尚不存在。Spring ApplicationContext 要等到 Pivotal GemFire 调用初始化器块时才会创建,而初始化器块仅在所有其他 Pivotal GemFire 组件(缓存、Region 等)已经创建和初始化之后发生。

为了解决这个问题,引入了一个新的 LazyWiringDeclarableSupport 类。这个新类能够感知 Spring ApplicationContext。这个抽象基类的意图是,任何实现类都会注册自己,以便在初始化器被调用后由 Pivotal GemFire 最终创建的 Spring 容器进行配置。本质上,这使得你的 Pivotal GemFire 应用程序组件有机会使用 Spring 容器中定义的 Spring bean 进行配置和自动布线。

为了让你的 Pivotal GemFire 应用程序组件能够被 Spring 容器自动布线,你应该创建一个扩展 LazyWiringDeclarableSupport 的应用程序类,并为需要作为 Spring bean 依赖项提供的任何类成员添加注解,类似于以下示例:

public class UserDataSourceCacheLoader extends LazyWiringDeclarableSupport
    implements CacheLoader<String, User> {

  @Autowired
  private DataSource userDataSource;

  ...
}

如上面的 CacheLoader 示例所示,你可能(虽然很少见)需要在 Pivotal GemFire cache.xml 中定义一个 Region 和一个 CacheListener 组件。CacheLoader 可能需要访问应用程序的 Repository(或者可能是在 Spring ApplicationContext 中定义的 JDBC DataSource),以便在启动时将 Users 加载到 Pivotal GemFire 的 REPLICATE Region 中。

注意

以这种方式将 Pivotal GemFire 和 Spring 容器的不同生命周期混合在一起时要小心。并非所有用例和场景都受支持。Pivotal GemFire 的 cache.xml 配置将类似于以下内容(这来自 SDG 的测试套件):

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

  <region name="Users" refid="REPLICATE">
    <region-attributes initial-capacity="101" load-factor="0.85">
      <key-constraint>java.lang.String</key-constraint>
      <value-constraint>org.springframework.data.gemfire.repository.sample.User</value-constraint>
      <cache-loader>
        <class-name>
          org.springframework.data.gemfire.support.SpringContextBootstrappingInitializerIntegrationTest$UserDataStoreCacheLoader
        </class-name>
      </cache-loader>
    </region-attributes>
  </region>

  <initializer>
    <class-name>org.springframework.data.gemfire.support.SpringContextBootstrappingInitializer</class-name>
    <parameter name="basePackages">
      <string>org.springframework.data.gemfire.support.sample</string>
    </parameter>
  </initializer>

</cache>

14. 示例应用程序

示例应用程序现已在 Spring Pivotal GemFire Examples 仓库中维护。

Spring Data for Pivotal GemFire 项目还包含一个示例应用程序。该示例应用程序名为“Hello World”,演示了如何在 Spring 应用程序中配置和使用 Pivotal GemFire。在运行时,该示例提供了一个 shell,允许你对数据网格运行各种命令。它为不熟悉基本组件或 Spring 和 Pivotal GemFire 概念的开发人员提供了绝佳的起点。

该示例随分发包捆绑,并且是基于 Maven 的。你可以将其导入任何支持 Maven 的 IDE(例如 Spring Tool Suite)或从命令行运行它们。

14.1. Hello World

“Hello World”示例应用程序演示了 Spring Data for Pivotal GemFire 项目的核心功能。它引导 Pivotal GemFire,对其进行配置,对缓存执行任意命令,并在应用程序退出时将其关闭。可以同时启动应用程序的多个实例,它们可以协同工作,无需用户干预即可共享数据。

在 Linux 下运行
如果你在启动 Pivotal GemFire 或示例时遇到网络问题,请尝试将以下系统属性 java.net.preferIPv4Stack=true 添加到命令行(例如,-Djava.net.preferIPv4Stack=true)。对于另一种(全局)解决方案(尤其是在 Ubuntu 上),请参阅 SGF-28

14.1.1. 启动和停止示例

“Hello World”示例应用程序被设计为一个独立的 Java 应用程序。它包含一个 main 类,可以从你的 IDE(在 Eclipse 或 STS 中,通过 Run As/Java Application)或从命令行通过 Maven 使用 mvn exec:java 启动。如果类路径设置正确,你还可以直接对生成的 Artifact 使用 java 命令。

要停止示例,请在命令行中键入 exit 或按 Ctrl+C 停止 JVM 并关闭 Spring 容器。

14.1.2. 使用示例

启动后,示例会创建一个共享数据网格,并允许你对其发出命令。输出应类似于以下内容:

INFO: Created {data-store-name} Cache [Spring {data-store-name} World] v. X.Y.Z
INFO: Created new cache region [myWorld]
INFO: Member xxxxxx:50694/51611 connecting to region [myWorld]
Hello World!
Want to interact with the world ? ...
Supported commands are:

get <key> - retrieves an entry (by key) from the grid
put <key> <value> - puts a new entry into the grid
remove <key> - removes an entry (by key) from the grid
...

例如,要向网格中添加新项目,可以使用以下命令:

-> Bold Section qName:emphasis level:5, chunks:[put 1 unu] attrs:[role:bold]
INFO: Added [1=unu] to the cache
null
-> Bold Section qName:emphasis level:5, chunks:[put 1 one] attrs:[role:bold]
INFO: Updated [1] from [unu] to [one]
unu
-> Bold Section qName:emphasis level:5, chunks:[size] attrs:[role:bold]
1
-> Bold Section qName:emphasis level:5, chunks:[put 2 two] attrs:[role:bold]
INFO: Added [2=two] to the cache
null
-> Bold Section qName:emphasis level:5, chunks:[size] attrs:[role:bold]
2

可以同时运行多个实例。启动后,新的 VM 会自动看到现有的 region 及其信息,如下例所示:

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 缓存、一个 region,并出于说明目的,定义了一个充当日志记录器的 CacheListener

主要的 bean 是 HelloWorldCommandProcessor,它们依赖于 GemfireTemplate 与分布式 fabric 交互。这两个类都使用注解来定义它们的依赖关系和生命周期回调。

资源

除了本参考文档外,还有许多其他资源可以帮助你学习如何在 Spring Framework 中使用 {data-store-product-name}。本节列出了这些额外的第三方资源。

附录

附录 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

确定用于创建 finder 查询的策略。有关详细信息,请参阅“[repositories.query-methods.query-lookup-strategies]”。默认为 create-if-not-found

named-queries-location

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

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 Seq, List, Map, Set

Vavr 集合类型。有关详细信息,请参阅 [repositories.collections-and-iterables.vavr]

Future<T>

一个 Future。期望方法使用 @Async 进行注解,并且需要启用 Spring 的异步方法执行能力。

CompletableFuture<T>

Java 8 的 CompletableFuture。期望方法使用 @Async 进行注解,并且需要启用 Spring 的异步方法执行能力。

ListenableFuture

一个 org.springframework.util.concurrent.ListenableFuture。期望方法使用 @Async 进行注解,并且需要启用 Spring 的异步方法执行能力。

Slice

一块固定大小的数据,并带有是否还有更多数据的指示。需要一个 Pageable 方法参数。

Page<T>

一个 Slice,带有额外信息,例如总结果数。需要一个 Pageable 方法参数。

GeoResult<T>

一个结果条目,带有额外信息,例如到参考位置的距离。

GeoResults<T>

一个 GeoResult<T> 列表,带有额外信息,例如到参考位置的平均距离。

GeoPage<T>

一个带有 GeoResult<T>Page,例如到参考位置的平均距离。

Mono<T>

一个 Project Reactor 的 Mono,使用响应式仓库发射零个或一个元素。期望查询方法最多返回一个结果。如果没有找到结果,则返回 Mono.empty()。多于一个结果会触发 IncorrectResultSizeDataAccessException

Flux<T>

一个 Project Reactor 的 Flux,使用响应式仓库发射零个、一个或多个元素。返回 Flux 的查询也可以发射无限数量的元素。

Single<T>

一个 RxJava 的 Single,使用响应式仓库发射单个元素。期望查询方法最多返回一个结果。如果没有找到结果,则返回 Mono.empty()。多于一个结果会触发 IncorrectResultSizeDataAccessException

Maybe<T>

一个 RxJava 的 Maybe,使用响应式仓库发射零个或一个元素。期望查询方法最多返回一个结果。如果没有找到结果,则返回 Mono.empty()。多于一个结果会触发 IncorrectResultSizeDataAccessException

Flowable<T>

一个 RxJava 的 Flowable,使用响应式仓库发射零个、一个或多个元素。返回 Flowable 的查询也可以发射无限数量的元素。

附录 E: Spring Data for Pivotal GemFire Schema


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