© 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 Repositories
-
gfe-data
XML 命名空间支持注册 Pivotal GemFire Functions。参见配置 Function 服务 -
顶层
<disk-store>
元素已添加到 SDG 的gfe
XML 命名空间,以允许在 Regions 以及支持持久备份或溢出的其他 Pivotal GemFire 组件之间共享持久化存储。参见[bootstrap-diskstore]<*-region>
元素不再允许嵌套的<disk-store>
元素。 -
嵌套的
<*-region>
元素支持 Pivotal GemFire Sub-Regions。 -
已添加
<local-region>
元素来配置 Local Region。 -
支持 Pivotal GemFire 7.0 中重新设计的 WAN Gateway。
3.2. 1.3 版本新增特性
-
升级到 Spring Framework 3.2.8。
-
升级到 Spring Data Commons 1.7.1。
-
支持 Pivotal GemFire Functions 的注解。现在可以使用注解声明和注册编写为 POJO 的 Functions。此外,Function 执行被定义为带注解的接口,类似于 Spring Data Repositories 的工作方式。参见Function 执行的注解支持。
-
向 SDG XML 命名空间添加了
<datasource>
元素,以简化与 Pivotal GemFire 数据网格建立基本客户端连接。 -
向 SDG 的
gfe-data
XML 命名空间添加了<json-region-autoproxy>
元素,以支持 Pivotal GemFire 7.0 中引入的 JSON 特性,使 Spring AOP 能够在 Region 数据访问操作上自动执行必要的转换。 -
升级到 Pivotal GemFire 7.0.1 并添加了对新
AsyncEventQueue
属性的 XML 命名空间支持。 -
添加了在 Regions 上设置订阅兴趣策略的支持。
-
支持 Function 执行的 void 返回类型。有关完整详细信息,请参见Function 执行的注解支持。
-
支持 Local Regions 的持久化。参见Local Region。
-
支持在 Pivotal GemFire Client Cache 上设置条目的存活时间 (TTL) 和空闲时间 (TTI)。参见配置 Pivotal GemFire ClientCache
-
支持使用单个 Pivotal GemFire 集群同时在 tc Server 内部运行多个基于 Spring Data for Pivotal GemFire 的 Web 应用。
-
使用 SDG 的
gfe
XML 命名空间支持所有 Cache Region 定义上的concurrency-checks-enabled
。参见[bootstrap:region:common:attributes] -
支持客户端 Local Regions 上的
CacheLoaders
和CacheWriters
。 -
支持在 Pivotal GemFire Cache Sub-Regions 上注册
CacheListeners
、AsyncEventQueues
和GatewaySenders
。 -
支持 Regions 中的 PDX 持久化键。
-
当使用
colocated-with
属性指定搭配时,支持在 Spring 上下文中正确创建 Partition Region bean。 -
使用 SDG
gfe
XML 命名空间中恰当的嵌套<*-region>
元素语法全面支持 Cache Sub-Regions。
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 中引导 SpringApplicationContext
的支持。参见在 Pivotal GemFire 中引导 Spring ApplicationContext -
添加了将应用域对象和实体持久化到多个 Pivotal GemFire Cache Regions 的支持。参见实体映射
-
添加了将应用域对象和实体持久化到 Pivotal GemFire Cache Sub-Regions 的支持,避免在 Sub-Regions 唯一可识别但名称相同时发生冲突。参见实体映射
-
对所有 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 注解的 Function 实现。 -
使 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 新的基于集群的配置服务的支持。
-
使 'auto-reconnect' 功能能够在 Spring 配置的 Pivotal GemFire Server 中使用。
-
允许创建并发和并行的
AsyncEventQueues
和GatewaySenders
。 -
添加了对 Pivotal GemFire 8 Region 数据压缩的支持。
-
添加了属性以设置
DiskStore
使用率的关键和警告百分比。 -
支持将
EventSubstitutionFilters
添加到GatewaySenders
的能力。
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 的集群配置服务中配置的现有 Regions 上添加 Spring 定义的CacheListeners
、CacheLoaders
和CacheWriters
的支持。 -
向
SpringContextBootstrappingInitializer
添加了 Spring JavaConfig 支持。 -
在
SpringContextBootstrappingInitializer
中添加了对自定义ClassLoaders
的支持,以加载 Spring 定义的 Bean 类。 -
添加了对
LazyWiringDeclarableSupport
重新初始化和完全替代WiringDeclarableSupport
的支持。 -
向
<gfe:pool>
元素添加了locators
和servers
属性,允许使用 Spring 的属性占位符配置可变 Locator 和 Server 端点列表。 -
支持将
<gfe-data:datasource>
元素与非 Spring 配置的 Pivotal GemFire Server 一起使用。 -
添加了多索引定义和创建支持。
-
添加了对 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-id
和durable-client-timeout
属性。 -
使
GemfirePersistentProperty
现在可以正确处理其他非实体、标量类类型(例如BigDecimal
和BigInteger
)。 -
防止使用 SDG 定义的
Pools
的Regions
在Pools
销毁之前被销毁。 -
处理了定义为 Repository 查询方法的 Pivotal GemFire OQL 查询的大小写不敏感问题。
-
在 SDG 的 Spring Cache Abstraction 支持中,将
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
中添加了对挂起和恢复的支持。 -
在 Repositories 中添加了支持,以便在不存在 `@Id` 注解时使用 Bean 的
id
属性作为 Region 键。 -
使用 `@EnablePdx` 时,将
MappingPdxSerializer
用作默认的 Pivotal GemFire 序列化策略。 -
使
GemfireCacheManager
能够显式列出要在 Spring 的 Caching Abstraction 中使用的 Region 名称。 -
配置了 Pivotal GemFire Caches、CacheServers、Locators、Pools、Regions、Indexes、DiskStores、过期、淘汰、统计信息、Mcast、HttpService、Auth、SSL、日志、系统属性。
-
在类路径上添加了多个 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 时轻松方便地表达客户端和服务器之间对键和值的 Interests。 -
在基于注解的配置模型中添加了对堆外内存、Redis Adapter 和 Pivotal GemFire 新安全框架的支持。
参考指南
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 Repositories 描述了如何创建和使用 Spring Data Repositories,通过基本的 CRUD 和简单查询操作访问存储在 Pivotal GemFire 中的数据。
-
Function 执行的注解支持 描述了如何使用注解创建和使用 Pivotal GemFire Functions,以在数据所在位置执行分布式计算。
-
持续查询 (CQ) 描述了如何使用 Pivotal GemFire 的持续查询 (CQ) 功能,根据在 Pivotal GemFire 的 OQL (对象查询语言) 中定义和注册的兴趣来处理事件流。
-
在 Pivotal GemFire 中引导 Spring ApplicationContext 描述了如何配置和引导使用
Gfsh
在 Pivotal GemFire Server 中运行的 SpringApplicationContext
。 -
示例应用 描述了分发版中提供的示例,以说明 Spring Data for Pivotal GemFire 中可用的各种功能。
5. 使用 Spring 容器引导 Pivotal GemFire
Spring Data for Pivotal GemFire 使用 Spring IoC 容器提供了对 Pivotal GemFire 内存数据网格 (IMDG) 的完整配置和初始化。该框架包含多个类,有助于简化 Pivotal GemFire 组件的配置,包括:Caches、Regions、索引、DiskStores、Functions、WAN 网关、持久化备份以及其他多个分布式系统组件,以最小的努力支持各种应用用例。
本节假定您对 Pivotal GemFire 有基本的熟悉度。欲了解更多信息,请参见 Pivotal GemFire 产品文档。 |
5.1. 使用 Spring 相较于 Pivotal GemFire cache.xml
的优势
Spring Data for Pivotal GemFire 的 XML 命名空间支持对 Pivotal GemFire 内存数据网格 (IMDG) 的完整配置。XML 命名空间是在 Spring 上下文中配置 Pivotal GemFire 的两种方式之一,以便在 Spring 容器内正确管理 Pivotal GemFire 的生命周期。在 Spring 上下文中配置 Pivotal GemFire 的另一种方式是使用基于注解的配置。
虽然对 Pivotal GemFire 原生 cache.xml
的支持出于遗留原因而保留,但鼓励使用 XML 配置的 Pivotal GemFire 应用开发者在 Spring XML 中完成所有配置,以利用 Spring 提供的诸多优势,例如模块化 XML 配置、属性占位符和覆盖、SpEL (Spring Expression Language) 和环境 profile。在 XML 命名空间的背后,Spring Data for Pivotal GemFire 大量使用了 Spring 的 `FactoryBean` 模式来简化 Pivotal GemFire 组件的创建、配置和初始化。
Pivotal GemFire 提供了几个回调接口,例如 CacheListener
、CacheLoader
和 CacheWriter
,允许开发者添加自定义事件处理器。使用 Spring 的 IoC 容器,您可以将这些回调配置为普通的 Spring bean 并将其注入到 Pivotal GemFire 组件中。这相比原生 cache.xml
是一个显著改进,原生 cache.xml
提供的配置选项相对有限,并且要求回调实现 Pivotal GemFire 的 `Declarable` 接口(参见装配 `Declarable` 组件了解如何在 Spring 容器中使用 `Declarables`)。
此外,像 Spring Tool Suite (STS) 这样的 IDE 为 Spring XML 命名空间提供了出色的支持,包括代码补全、弹出式注解和实时验证。
5.2. 使用核心命名空间
为了简化配置,Spring Data for Pivotal GemFire 提供了一个专用的 XML 命名空间来配置核心 Pivotal GemFire 组件。可以使用 Spring 标准的 `<bean>` 定义直接配置 Bean。然而,所有 Bean 属性都通过 XML 命名空间暴露,因此使用原始 Bean 定义的好处不大。
有关 Spring 中基于 XML Schema 的配置的更多信息,请参见 Spring Framework 参考文档的附录。 |
Spring Data Repository 支持使用一个单独的 XML 命名空间。有关如何配置 Spring Data for Pivotal GemFire Repositories 的更多信息,请参见Spring Data for Pivotal GemFire Repositories。 |
要使用 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 配置很有用,因为它避免了声明前缀。为此,请交换前面所示的命名空间前缀声明,如下例所示:
|
5.3. 使用数据访问命名空间
除了核心 XML 命名空间 (`gfe`) 外,Spring Data for Pivotal GemFire 还提供了数据访问 XML 命名空间 (`gfe-data`),其主要目的是简化 Pivotal GemFire 客户端应用的开发。该命名空间目前包含对 Pivotal GemFire Repositories 和 Function 执行的支持,以及一个 `<datasource>` 标签,提供了一种方便的方式连接到 Pivotal GemFire 集群。
5.3.1. 连接到 Pivotal GemFire 的简单方法
对于许多应用来说,使用默认值连接到 Pivotal GemFire 数据网格就已足够。Spring Data for Pivotal GemFire 的 `<datasource>` 标签提供了一种简单的数据访问方式。数据源创建一个 `ClientCache` 和连接 `Pool`。此外,它会查询集群服务器以获取所有现有的根 Regions,并为每个 Region 创建一个(空的)客户端 Region 代理。
<gfe-data:datasource>
<locator host="remotehost" port="1234"/>
</gfe-data:datasource>
<datasource>
标签在语法上类似于 <gfe:pool>
。它可以配置一个或多个嵌套的 `locator` 或 `server` 元素来连接到现有数据网格。此外,支持所有可用于配置 Pool 的属性。此配置会自动为连接到 Locator 的集群成员上定义的每个 Region 创建客户端 Region Bean,因此它们可以被 Spring Data 映射注解 (`GemfireTemplate`) 无缝引用,并自动注入到应用类中。
当然,您可以显式配置客户端 Regions。例如,如果您想在本地内存中缓存数据,如下例所示:
<gfe-data:datasource>
<locator host="remotehost" port="1234"/>
</gfe-data:datasource>
<gfe:client-region id="Example" shortcut="CACHING_PROXY"/>
5.4. 配置 Cache
要使用 Pivotal GemFire,您需要创建新缓存或连接到现有缓存。在当前版本的 Pivotal GemFire 中,每个 VM(更严格地说,每个 `ClassLoader`)只能有一个打开的缓存。在大多数情况下,缓存应该只创建一次。
本节描述了对等 `Cache` 成员的创建和配置,适用于对等网络 (P2P) 拓扑和缓存服务器。`Cache` 成员也可以用于独立应用和集成测试。然而,在典型的生产系统中,大多数应用进程充当缓存客户端,创建 `ClientCache` 实例。这在配置 Pivotal GemFire ClientCache和Client Region章节中进行了描述。 |
可以使用以下简单声明创建具有默认配置的对等 `Cache`:
<gfe:cache/>
在 Spring 容器初始化期间,任何包含此缓存定义的 ApplicationContext
都会注册一个 CacheFactoryBean
,该工厂 Bean 会创建一个名为 gemfireCache
的 Spring Bean,该 Bean 引用一个 Pivotal GemFire Cache
实例。此 Bean 引用现有 Cache
,或者(如果尚不存在)引用新创建的 Cache
。由于未指定其他属性,新创建的 Cache
将应用默认缓存配置。
所有依赖于 Cache
的 Spring Data for Pivotal GemFire 组件都遵循此命名约定,因此您无需明确声明 Cache
依赖项。如果您愿意,可以使用各种 SDG XML 命名空间元素提供的 cache-ref
属性来明确指定依赖项。此外,您还可以使用 id
属性覆盖缓存的 Bean 名称,如下所示:
<gfe:cache id="myCache"/>
Pivotal GemFire Cache
可以使用 Spring 完全配置。但是,也支持 Pivotal GemFire 的原生 XML 配置文件 cache.xml
。对于需要原生配置 Pivotal GemFire 缓存的情况,您可以通过使用 cache-xml-location
属性提供对 Pivotal GemFire XML 配置文件的引用,如下所示:
<gfe:cache id="cacheConfiguredWithNativeCacheXml" cache-xml-location="classpath:cache.xml"/>
在此示例中,如果需要创建缓存,它将使用位于 classpath 根目录下的名为 cache.xml
的文件进行配置。
该配置利用 Spring 的 Resource 抽象来查找文件。Resource 抽象允许使用各种搜索模式,具体取决于运行时环境或资源位置中指定的(如果有)前缀。 |
除了引用外部 XML 配置文件外,您还可以指定利用 Spring 的任何 Properties
支持功能的 Pivotal GemFire 系统 属性。
例如,您可以使用 util
命名空间中定义的 properties
元素直接定义 Properties
或从属性文件加载属性,如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:gfe="https://www.springframework.org/schema/gemfire"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
https://www.springframework.org/schema/gemfire https://www.springframework.org/schema/gemfire/spring-gemfire.xsd
http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd
">
<util:properties id="gemfireProperties" location="file:/path/to/gemfire.properties"/>
<gfe:cache properties-ref="gemfireProperties"/>
</beans>
建议使用属性文件将特定于环境的设置外部化到应用程序配置之外。
缓存设置仅在需要创建新缓存时应用。如果 VM 中已存在开放的缓存,则这些设置将被忽略。 |
5.4.1. 高级缓存配置
对于高级缓存配置,cache
元素提供了许多配置选项,这些选项作为属性或子元素公开,如下所示:
(1)
<gfe:cache
cache-xml-location=".."
properties-ref=".."
close="false"
copy-on-read="true"
critical-heap-percentage="90"
eviction-heap-percentage="70"
enable-auto-reconnect="false" (2)
lock-lease="120"
lock-timeout="60"
message-sync-interval="1"
pdx-serializer-ref="myPdxSerializer"
pdx-persistent="true"
pdx-disk-store="diskStore"
pdx-read-serialized="false"
pdx-ignore-unread-fields="true"
search-timeout="300"
use-bean-factory-locator="true" (3)
use-cluster-configuration="false" (4)
>
<gfe:transaction-listener ref="myTransactionListener"/> (5)
<gfe:transaction-writer> (6)
<bean class="org.example.app.gemfire.transaction.TransactionWriter"/>
</gfe:transaction-writer>
<gfe:gateway-conflict-resolver ref="myGatewayConflictResolver"/> (7)
<gfe:dynamic-region-factory/> (8)
<gfe:jndi-binding jndi-name="myDataSource" type="ManagedDataSource"/> (9)
</gfe:cache>
1 | 属性支持各种缓存选项。有关此示例中显示的任何内容的更多信息,请参阅 Pivotal GemFire 产品文档。close 属性决定了在 Spring 应用程序上下文关闭时是否应关闭缓存。默认值为 true 。但是,对于多个应用程序上下文使用同一缓存的用例(在 Web 应用程序中很常见),请将此值设置为 false 。 |
2 | 将 enable-auto-reconnect 属性设置为 true (默认值为 false )允许断开连接的 Pivotal GemFire 成员自动重新连接并重新加入 Pivotal GemFire 集群。有关更多详细信息,请参阅 Pivotal GemFire 产品文档。 |
3 | 将 use-bean-factory-locator 属性设置为 true (默认为 false )仅在同时使用 Spring (XML) 配置元数据和 Pivotal GemFire cache.xml 配置 Pivotal GemFire 缓存节点(客户端或对等节点)时适用。此选项允许在 cache.xml 中表示的 Pivotal GemFire 组件(例如 CacheLoader )与在 Spring 应用程序上下文中定义的 Bean(例如 DataSource )进行自动装配。此选项通常与 cache-xml-location 结合使用。 |
4 | 将 use-cluster-configuration 属性设置为 true (默认值为 false )使 Pivotal GemFire 成员能够从 Locator 检索通用、共享的基于集群的配置。有关更多详细信息,请参阅 Pivotal GemFire 产品文档。 |
5 | 使用 Bean 引用声明 TransactionListener 回调的示例。引用的 Bean 必须实现 TransactionListener。可以实现 TransactionListener 来处理与事务相关的事件(例如 afterCommit 和 afterRollback)。 |
6 | 使用内部 Bean 声明声明 TransactionWriter 回调的示例。该 Bean 必须实现 TransactionWriter。TransactionWriter 是一个可以否决事务的回调。 |
7 | 使用 Bean 引用声明 GatewayConflictResolver 回调的示例。引用的 Bean 必须实现 https://gemfire-98-javadocs.docs.pivotal.io//org/apache/geode/cache/util/GatewayConflictResolver.html [GatewayConflictResolver]。GatewayConflictResolver 是一个 Cache 级别的插件,用于决定如何处理源自其他系统并通过 WAN 网关到达的事件。 |
8 | 启用 Pivotal GemFire 的 DynamicRegionFactory,它提供分布式 Region 创建服务。 |
9 | 声明 JNDI 绑定以将外部 DataSource 加入 Pivotal GemFire 事务。 |
启用 PDX 序列化
前面的示例包含许多与 Pivotal GemFire 的增强型序列化框架 PDX 相关的属性。虽然对 PDX 的完整讨论超出了本参考指南的范围,但重要的是要注意,通过注册 PdxSerializer
来启用 PDX,这通过设置 pdx-serializer
属性指定。
Pivotal GemFire 提供了一个使用 Java Reflection 的实现类(org.apache.geode.pdx.ReflectionBasedAutoSerializer
)。但是,开发者通常会提供自己的实现。属性的值只是对实现 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 集群对等 Cache
成员的 Pivotal GemFire 应用程序时,不应启用“自动重新连接”。
此限制的主要原因是大多数 Pivotal GemFire 应用程序使用对 Pivotal GemFire Cache
或 Region 的引用来执行数据访问操作。这些引用由 Spring 容器“注入”到应用程序组件(例如 Repositories)中供应用程序使用。当对等成员被强制从集群的其余部分断开连接时(可能是因为对等成员变得无响应,或者网络分区将一个或多个对等成员分隔成一个过小的组,无法作为一个独立的分布式系统运行),对等成员会关闭,并且所有 Pivotal GemFire 组件引用(缓存、Region 等)都会变得无效。
基本上,当前在每个对等成员中执行的强制断开处理逻辑会彻底拆毁系统。JGroups 堆栈关闭,分布式系统进入关机状态,最后缓存被关闭。实际上,所有内存引用都会失效并丢失。
从分布式系统断开连接后,对等成员进入“重新连接”状态,并周期性地尝试重新加入分布式系统。如果对等成员成功重新连接,该成员将从现有成员重建其分布式系统的“视图”,并接收新的分布式系统 ID。此外,所有缓存、Region 和其他 Pivotal GemFire 组件都会重建。因此,所有旧的引用(可能已由 Spring 容器注入到应用程序中)现在都已陈旧且不再有效。
Pivotal GemFire 不保证(即使在使用 Pivotal GemFire 公共 Java API 时)应用程序缓存、Region 或其他组件引用会由重新连接操作自动刷新。因此,Pivotal GemFire 应用程序必须注意刷新自己的引用。
不幸的是,也无法收到断开连接事件以及随后的重新连接事件的通知。如果可以,您将有一种清晰的方式知道何时调用 ConfigurableApplicationContext.refresh()
(如果应用程序这样做甚至适用),这就是为什么不建议将此 Pivotal GemFire 的“功能”用于对等 Cache
应用程序的原因。
有关“自动重新连接”的更多信息,请参阅 Pivotal GemFire 产品文档。
使用基于集群的配置
Pivotal GemFire 的集群配置服务是一种便捷的方式,让任何加入集群的对等成员通过 Locator 维护的共享持久化配置获得集群的“一致视图”。使用基于集群的配置可确保对等成员加入时其配置与 Pivotal GemFire 分布式系统兼容。
Spring Data for Pivotal GemFire 的此功能(将 use-cluster-configuration
属性设置为 true
)的工作方式与 cache-xml-location
属性相同,不同之处在于 Pivotal GemFire 配置元数据的来源通过网络从 Locator 获取,而不是来自本地文件系统中的原生 cache.xml
文件。
所有 Pivotal GemFire 原生配置元数据,无论是来自 cache.xml
还是来自集群配置服务,都会在任何 Spring (XML) 配置元数据之前应用。因此,Spring 的配置用于“增强”原生 Pivotal GemFire 配置元数据,并且最有可能特定于应用程序。
同样,要启用此功能,请在 Spring XML 配置中指定以下内容:
<gfe:cache use-cluster-configuration="true"/>
虽然某些 Pivotal GemFire 工具(例如 Gfsh)在进行类似模式的更改时会“记录”其操作(例如 gfsh>create region --name=Example --type=PARTITION ),但 Spring Data for Pivotal GemFire 的配置元数据不会被记录。直接使用 Pivotal GemFire 公共 Java API 时也是如此,它也不会被记录。 |
有关 Pivotal GemFire 集群配置服务的更多信息,请参阅 产品文档。
5.4.2. 配置 Pivotal GemFire CacheServer
Spring Data for Pivotal GemFire 包含对配置 CacheServer 的专用支持,允许通过 Spring 容器进行完整配置,如下例所示:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:gfe="https://www.springframework.org/schema/gemfire"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
https://www.springframework.org/schema/gemfire https://www.springframework.org/schema/gemfire/spring-gemfire.xsd
">
<gfe:cache/>
<!-- Example depicting serveral Pivotal GemFire CacheServer configuration options -->
<gfe:cache-server id="advanced-config" auto-startup="true"
bind-address="localhost" host-name-for-clients="localhost" port="${gemfire.cache.server.port}"
load-poll-interval="2000" max-connections="22" max-message-count="1000" max-threads="16"
max-time-between-pings="30000" groups="test-server">
<gfe:subscription-config eviction-type="ENTRY" capacity="1000" disk-store="file://${java.io.tmpdir}"/>
</gfe:cache-server>
<context:property-placeholder location="classpath:cache-server.properties"/>
</beans>
前面的配置显示了 cache-server
元素和许多可用选项。
此配置没有硬编码端口,而是使用 Spring 的 context 命名空间来声明一个 property-placeholder 。属性占位符读取一个或多个属性文件,然后在运行时用值替换属性占位符。这样做允许管理员更改值而无需修改主应用程序配置。Spring 还提供了 SpEL 和 环境抽象,以支持将特定于环境的属性从主代码库中外部化,从而简化跨多台机器的部署。 |
为避免初始化问题,由 Spring Data for Pivotal GemFire 启动的 CacheServer 会在 Spring 容器完全初始化后启动。这样做允许在服务器开始接受连接之前,声明式定义的潜在 Region、监听器、写入器或实例化器完全初始化并注册。在以编程方式配置这些元素时请记住这一点,因为服务器可能会在您的组件启动之前启动,因此客户端连接时可能无法立即看到它们。 |
5.4.3. 配置 Pivotal GemFire ClientCache
除了定义 Pivotal GemFire 对等 Cache
外,Spring Data for Pivotal GemFire 还支持在 Spring 容器中定义 Pivotal GemFire ClientCache
。ClientCache
的配置和使用类似于 Pivotal GemFire 对等 Cache,并由 org.springframework.data.gemfire.client.ClientCacheFactoryBean
提供支持。
使用默认配置的最简单的 Pivotal GemFire 缓存客户端定义如下:
<beans>
<gfe:client-cache/>
</beans>
client-cache
支持许多与 Cache 元素相同的选项。但是,与完全功能的对等 Cache
成员不同,缓存客户端通过 Pool 连接到远程缓存服务器。默认情况下,会创建一个 Pool 来连接到运行在 localhost
并监听端口 40404
的服务器。所有客户端 Region 都会使用默认 Pool,除非 Region 配置为使用特定的 Pool。
Pool 可以使用 pool
元素定义。此客户端 Pool 可用于通过一个或多个 Locator 直接配置与单个实体或整个缓存的连接。
例如,要自定义 client-cache
使用的默认 Pool,开发者需要定义一个 Pool 并将其连接到缓存定义,如下例所示:
<beans>
<gfe:client-cache id="myCache" pool-name="myPool"/>
<gfe:pool id="myPool" subscription-enabled="true">
<gfe:locator host="${gemfire.locator.host}" port="${gemfire.locator.port}"/>
</gfe:pool>
</beans>
<client-cache>
元素还有一个 ready-for-events
属性。如果该属性设置为 true
,则客户端缓存初始化将包含对 ClientCache.readyForEvents()
的调用。
客户端 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。对于任何客户端的、仅本地的 Region 都是如此,正如 Pivotal GemFire 的 ClientRegionShortcut
(所有 LOCAL_*
快捷方式)所定义的那样。
但是,如果客户端 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-cache
的 DEFAULT
pool 是从 locatorPool
初始化的,如 pool-name
属性所指定。没有 Spring Data for Pivotal GemFire 定义的 gemfirePool
,因为两个 Pool 都已明确标识(命名)—— 分别为 locatorPool
和 serverPool
。
“Example” Region 明确引用并专门使用 serverPool
。AnotherExample
Region 使用 Pivotal GemFire 的 DEFAULT
Pool,该 Pool 又根据客户端缓存 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 来存储和检索缓存中的数据。org.apache.geode.cache.Region
是一个扩展 java.util.Map
的接口,并允许使用熟悉的键值语义进行基本数据访问。Region
接口被连接到需要它的应用程序类中,以便实际的 Region 类型与编程模型解耦。通常,每个 Region 与一个域对象相关联,类似于关系数据库中的表。
Pivotal GemFire 实现了以下类型的 Region:
-
REPLICATE - 数据在集群中定义该 Region 的所有缓存成员之间复制。这提供了非常高的读取性能,但写入需要更长时间进行复制。
-
PARTITION - 数据在集群中定义该 Region 的许多缓存成员之间分区到桶(分片)。这提供了高读取和写入性能,适用于单个节点无法容纳的大型数据集。
-
LOCAL - 数据仅存在于本地节点上。
-
Client - 从技术上讲,客户端 Region 是一个 LOCAL Region,充当集群中缓存服务器上托管的 REPLICATE 或 PARTITION Region 的 PROXY。它可以保存本地创建或获取的数据。或者,它可以是空的。本地更新会同步到缓存服务器。此外,客户端 Region 可以订阅事件以与源自访问同一服务器 Region 的远程进程的更改保持同步。
有关各种 Region 类型及其功能以及配置选项的更多信息,请参阅 Pivotal GemFire 关于 Region 类型 的文档。
5.5.1. 使用外部配置的 Region
要引用已在 Pivotal GemFire 原生 cache.xml
文件中配置的 Region,请使用 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 不存在,将抛出初始化异常。要配置新的 Region,请继续阅读下面的相应部分。 |
在前面的示例中,由于未明确定义缓存名称,因此使用了默认命名约定(gemfireCache
)。或者,可以使用 cache-ref
属性引用缓存 Bean:
<gfe:cache id="myCache"/>
<gfe:lookup-region id="ordersRegion" name="Orders" cache-ref="myCache"/>
lookup-region
允许您检索现有、预配置的 Region,而无需暴露 Region 语义或设置基础设施。
5.5.2. 自动 Region 查找
当您在 <gfe:cache>
元素上使用 cache-xml-location
属性时,auto-region-lookup
允许您将 Pivotal GemFire 原生 cache.xml
文件中定义的所有 Region 导入到 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"/>
)将特定 Region 作为 Bean 引用到 Spring 容器中,或者您可以使用以下方式选择导入 cache.xml
中定义的所有 Region:
<gfe:auto-region-lookup/>
Spring Data for Pivotal GemFire 会自动为 cache.xml
中定义的所有尚未通过显式 <gfe:lookup-region>
Bean 声明添加到 Spring 容器的 Pivotal GemFire Region 创建 Bean。
重要的是要认识到,Spring Data for Pivotal GemFire 使用 Spring 的 BeanPostProcessor 在缓存创建和初始化后对其进行后处理,以确定在 Spring ApplicationContext
中添加为 Bean 的 Pivotal GemFire 中定义的 Region。
您可以像注入 Spring ApplicationContext
中定义的任何其他 Bean 一样注入这些“自动查找”的 Region,但有一个例外:您可能需要定义与“gemfireCache” Bean 的 depends-on
关联,如下所示:
package example;
import ...
@Repository("appDao")
@DependsOn("gemfireCache")
public class ApplicationDao extends DaoSupport {
@Resource(name = "Parent")
private Region<?, ?> parent;
@Resource(name = "/Parent/Child")
private Region<?, ?> child;
...
}
前面的示例仅适用于使用 Spring 的 component-scan
功能时。
如果您使用 Spring XML 配置声明组件,则应执行以下操作:
<bean class="example.ApplicationDao" depends-on="gemfireCache"/>
这样做可以确保在使用 <gfe:auto-region-lookup>
元素时,Pivotal GemFire 缓存和 cache.xml
中定义的所有 Region 都会在任何带有自动装配引用的组件之前创建。
5.5.3. 配置 Region
Spring Data for Pivotal GemFire 通过以下元素为配置任何类型的 Region 提供了全面的支持:
-
LOCAL Region:
<local-region>
-
PARTITION Region:
<partitioned-region>
-
REPLICATE Region:
<replicated-region>
-
Client Region:
<client-region>
有关 Region 类型 的全面描述,请参阅 Pivotal GemFire 文档。
通用 Region 属性
下表列出了所有 Region 类型可用的属性:
名称 | 值 | 描述 |
---|---|---|
cache-ref |
Pivotal GemFire Cache bean 引用 |
定义 Pivotal GemFire Cache 的 Bean 的名称(默认为 'gemfireCache')。 |
cloning-enabled |
boolean (default: |
当 |
close |
boolean (default: |
决定 Region 是否应在关闭时关闭。 |
concurrency-checks-enabled |
boolean (default: |
决定成员是否执行检查,以为分布式 Region 的并发或乱序更新提供一致处理。 |
data-policy |
请参阅 Pivotal GemFire 的 数据策略。 |
Region 的数据策略。请注意,并非所有数据策略都适用于每种 Region 类型。 |
destroy |
boolean (default: |
决定 Region 是否应在关闭时销毁。 |
disk-store-ref |
配置的磁盘存储的名称。 |
通过 |
disk-synchronous |
boolean (default: |
决定磁盘存储写入是否同步。 |
id |
任何有效的 Bean 名称。 |
如果未指定 |
ignore-if-exists |
boolean (default: |
如果 Region 已存在于缓存中,则忽略此 Bean 定义,从而导致查找而不是创建。 |
ignore-jta |
boolean (default: |
决定此 Region 是否参与 JTA (Java Transaction API) 事务。 |
index-update-type |
|
决定索引在条目创建时是同步更新还是异步更新。 |
initial-capacity |
integer (default: 16) |
Region 条目数的初始内存分配。 |
key-constraint |
任何有效的、完全限定的 Java 类名。 |
预期的键类型。 |
load-factor |
float (default: .75) |
设置用于存储 Region 条目的底层 |
name |
任何有效的 Region 名称。 |
Region 的名称。如果未指定,则假定为 |
persistent |
*boolean (default: |
决定 Region 是否将条目持久化到本地磁盘(磁盘存储)。 |
shortcut |
请参阅 https://gemfire-98-javadocs.docs.pivotal.io//org/apache/geode/cache/RegionShortcut.html |
此 Region 的 |
statistics |
boolean (default: |
决定 Region 是否报告统计信息。 |
template |
Region 模板的名称。 |
通过其中一个 |
value-constraint |
任何有效的、完全限定的 Java 类名。 |
预期的值类型。 |
CacheListener
实例
CacheListener
实例注册到 Region 以处理 Region 事件,例如条目创建、更新、销毁等。CacheListener
可以是实现 CacheListener
接口的任何 Bean。Region 可以有多个监听器,使用包含在 *-region
元素中的 cache-listener
元素声明。
以下示例声明了两个 CacheListener
。第一个引用了一个命名的、顶级 Spring Bean。第二个是一个匿名的内部 Bean 定义。
<bean id="myListener" class="org.example.app.geode.cache.SimpleCacheListener"/>
<gfe:replicated-region id="regionWithListeners">
<gfe:cache-listener>
<!-- nested CacheListener bean reference -->
<ref bean="myListener"/>
<!-- nested CacheListener bean definition -->
<bean class="org.example.app.geode.cache.AnotherSimpleCacheListener"/>
</gfe:cache-listener>
</gfe:replicated-region>
以下示例使用 cache-listener
元素的替代形式,带有 ref
属性。这样做可以在定义单个 CacheListener
时进行更简洁的配置。
注意:XML 命名空间只允许一个 cache-listener
元素,因此必须使用前面示例中所示的样式或下面示例中的样式。
<beans>
<gfe:replicated-region id="exampleReplicateRegionWithCacheListener">
<gfe:cache-listener ref="myListener"/>
</gfe:replicated-region>
<bean id="myListener" class="example.CacheListener"/>
</beans>
在 cache-listener 元素中使用 ref 和嵌套声明是违法的。这两个选项是互斥的,在同一元素中使用两者会导致异常。 |
Bean 引用约定
|
CacheLoader 和 CacheWriter
与 cache-listener
类似,XML 命名空间提供了 cache-loader
和 cache-writer
元素,用于为 Region 注册这些 Pivotal GemFire 组件。
CacheLoader
在缓存未命中时被调用,以允许从外部数据源(例如数据库)加载条目。CacheWriter
在条目创建或更新之前被调用,以允许将条目同步到外部数据源。主要区别在于,Pivotal GemFire 每个 Region 最多支持一个 CacheLoader
和一个 CacheWriter
实例。但是,可以使用任一声明样式。
以下示例声明了一个带有 CacheLoader
和 CacheWriter
的 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 文档中的 CacheLoader
和 CacheWriter
。
5.5.4. 压缩
Pivotal GemFire Region 也可以压缩,以减少 JVM 内存消耗和压力,从而可能避免全局 GC。为 Region 启用压缩后,存储在内存中的所有 Region 值都会被压缩,而键和索引保持未压缩。新值在放入 Region 时被压缩,所有值在从 Region 读取时会自动解压缩。值在持久化到磁盘或通过网络发送到其他对等成员或客户端时不会被压缩。
以下示例显示了一个启用压缩的 Region:
<beans>
<gfe:replicated-region id="exampleReplicateRegionWithCompression">
<gfe:compressor>
<bean class="org.apache.geode.compression.SnappyCompressor"/>
</gfe:compressor>
</gfe:replicated-region>
</beans>
有关更多信息,请参阅 Pivotal GemFire 关于 Region 压缩 的文档。
5.5.5. Off-Heap(堆外内存)
Pivotal GemFire Region 也可以配置为将 Region 值存储在堆外内存中,这部分 JVM 内存不受垃圾回收 (GC) 的影响。通过避免昂贵的 GC 周期,您的应用程序可以将更多时间花在重要的事情上,例如处理请求。
使用堆外内存就像声明要使用的内存量然后启用您的 Region 使用堆外内存一样简单,如下配置所示:
<util:properties id="gemfireProperties">
<prop key="off-heap-memory-size">200G</prop>
</util:properties>
<gfe:cache properties-ref="gemfireProperties"/>
<gfe:partitioned-region id="ExampleOffHeapRegion" off-heap="true"/>
您可以通过使用 <gfe:cache>
元素设置以下 Pivotal GemFire 配置属性来控制堆外内存管理的其它方面:
<gfe:cache critical-off-heap-percentage="90" eviction-off-heap-percentage"80"/>
Pivotal GemFire 的 ResourceManager
将使用这两个阈值(critical-off-heap-percentage
和 eviction-off-heap-percentage
)来更有效地管理堆外内存,其方式与 JVM 管理堆内存的方式非常相似。Pivotal GemFire ResourceManager
将通过逐出旧数据来防止缓存消耗过多的堆外内存。如果堆外管理器无法跟上,则 ResourceManager
会拒绝向缓存添加数据,直到堆外内存管理器释放了足够的内存。
有关更多信息,请参阅 Pivotal GemFire 关于 管理堆和堆外内存 的文档。
特别是,请阅读 管理堆外内存 部分。
5.5.6. 子 Region
Spring Data for Pivotal GemFire 也支持子 Region,允许 Region 按层次结构关系排列。
例如,Pivotal GemFire 允许存在 /Customer/Address
Region 和不同的 /Employee/Address
Region。此外,子 Region 可以有自己的子 Region 和配置。子 Region 不继承其父 Region 的属性。Region 类型可以混合匹配,但受 Pivotal GemFire 约束。子 Region 自然被声明为 Region 的子元素。子 Region 的 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>
请注意,子 Region 不允许使用 Monospaced ([id])
属性。子 Region 使用 Bean 名称创建(在这种情况下分别为 /Customer/Address 和 /Employee/Address)。因此,它们可以通过使用 Region 的完整路径名注入到需要它们的其他应用程序 Bean 中,例如 GemfireTemplate
。Region 的完整路径名也应在 OQL 查询字符串中使用。
5.5.7. Region 模板
Spring Data for Pivotal GemFire 也支持 Region 模板。
此功能允许开发人员定义一次通用的 Region 配置和属性,并在 Spring ApplicationContext
中声明的许多 Region Bean 定义中重用该配置。
Spring Data for Pivotal GemFire 在其命名空间中包含五个 Region 模板标签:
标签名称 | 描述 |
---|---|
|
定义通用的泛型 Region 属性。扩展 XML 命名空间中的 |
|
定义通用的“本地”Region 属性。扩展 XML 命名空间中的 |
|
定义通用的“分区”Region 属性。扩展 XML 命名空间中的 |
|
定义通用的“复制”Region 属性。扩展 XML 命名空间中的 |
|
定义通用的“客户端”Region 属性。扩展 XML 命名空间中的 |
除了这些标签之外,具体的 <gfe:*-region>
元素(以及抽象的 <gfe:*-region-template>
元素)都有一个 template
属性,用于定义 Region 继承其配置的 Region 模板。Region 模板甚至可以继承其他 Region 模板。
以下示例展示了一种可能的配置。
<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>
Region 模板也适用于子区域 (Sub-Regions)。请注意,“TemplateBasedPartitionRegion”扩展了“PartitionRegionTemplate”,后者扩展了“ExtendedRegionTemplate”,而后者又扩展了“BaseRegionTemplate”。后续继承的 Region bean 定义中定义的属性和子元素会覆盖父类中的定义。
模板工作原理
Spring Data for Pivotal GemFire 在解析 Spring ApplicationContext
配置元数据时应用 Region 模板,因此,Region 模板必须按照继承顺序声明。换句话说,父模板必须在子模板之前定义。这样做可以确保应用正确的配置,尤其是在元素属性或子元素被覆盖的情况下。
同样重要的是要记住,Region 类型只能从其他类型相似的 Region 继承。例如,<gfe:replicated-region> 不可能从 <gfe:partitioned-region-template> 继承。 |
Region 模板是单继承的。 |
关于 Region、子区域和查找的注意事项
以前,Spring Data for Pivotal GemFire XML 命名空间中的 replicated-region
、partitioned-region
、local-region
和 client-region
元素的基本属性之一是在尝试创建 Region 之前先执行查找。这样做是为了处理 Region 可能已经存在的情况,例如 Region 是在导入的 Pivotal GemFire 本地 cache.xml
配置文件中定义的。因此,先执行查找是为了避免任何错误。这是设计使然,并且可能会改变。
此行为已更改,默认行为现在是先创建 Region。如果 Region 已经存在,则创建逻辑会快速失败并抛出适当的异常。然而,就像 CREATE TABLE IF NOT EXISTS …
DDL 语法一样,Spring Data for Pivotal GemFire <gfe:*-region>
XML 命名空间元素现在包含一个 ignore-if-exists
属性,它通过在尝试创建 Region 之前先按名称查找现有 Region 来恢复旧的行为。如果按名称找到现有 Region 并且 ignore-if-exists
设置为 true
,则忽略 Spring 配置中定义的 Region bean 定义。
Spring 团队强烈建议 replicated-region 、partitioned-region 、local-region 和 client-region XML 命名空间元素仅严格用于定义新的 Region。当这些元素定义的 Region 已经存在且 Region 元素首先执行查找时可能出现的一个问题是,如果您在应用程序配置中为驱逐、过期、订阅等定义了不同的 Region 语义和行为,那么 Region 定义可能不匹配,并可能表现出与应用程序所需行为相反的行为。更糟糕的是,您可能想将 Region 定义为分布式 Region(例如,PARTITION ),而实际上现有 Region 定义仅是本地的。 |
推荐实践 - 仅使用 replicated-region 、partitioned-region 、local-region 和 client-region XML 命名空间元素来定义新的 Region。 |
考虑以下 Pivotal GemFire 本地 cache.xml
配置文件。
<?xml version="1.0" encoding="UTF-8"?>
<cache xmlns="https://geode.apache.org/schema/cache"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://geode.apache.org/schema/cache https://geode.apache.org/schema/cache/cache-1.0.xsd"
version="1.0">
<region name="Customers" refid="REPLICATE">
<region name="Accounts" refid="REPLICATE">
<region name="Orders" refid="REPLICATE">
<region name="Items" refid="REPLICATE"/>
</region>
</region>
</region>
</cache>
此外,考虑您可能定义了一个应用程序 DAO,如下所示:
public class CustomerAccountDao extends GemDaoSupport {
@Resource(name = "Customers/Accounts")
private Region customersAccounts;
...
}
在这里,我们在应用程序 DAO 中注入对 Customers/Accounts
Region 的引用。因此,开发人员在 Spring XML 配置元数据中为这些 Region 中的部分或全部定义 bean 是不罕见的,如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:gfe="https://www.springframework.org/schema/gemfire"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
https://www.springframework.org/schema/gemfire https://www.springframework.org/schema/gemfire/spring-gemfire.xsd
">
<gfe:cache cache-xml-location="classpath:cache.xml"/>
<gfe:lookup-region name="Customers/Accounts"/>
<gfe:lookup-region name="Customers/Accounts/Orders"/>
</beans>
Customers/Accounts
和 Customers/Accounts/Orders
Region 在 Spring 容器中分别作为 Customers/Accounts
和 Customers/Accounts/Orders
bean 被引用。使用 lookup-region
元素和相应语法(前面描述过)的好处是,它允许您直接引用子区域 (Sub-Region),而无需不必要地为父 Region(在此例中为 Customers
)定义 bean。
考虑以下不良示例,它将配置元数据语法更改为使用嵌套格式。
<gfe:lookup-region name="Customers">
<gfe:lookup-region name="Accounts">
<gfe:lookup-region name="Orders"/>
</gfe:lookup-region>
</gfe:lookup-region>
现在考虑另一个不良示例,它使用顶层 replicated-region
元素以及设置了 ignore-if-exists
属性来首先执行查找。
<gfe:replicated-region name="Customers" persistent="true" ignore-if-exists="true">
<gfe:replicated-region name="Accounts" persistent="true" ignore-if-exists="true">
<gfe:replicated-region name="Orders" persistent="true" ignore-if-exists="true"/>
</gfe:replicated-region>
</gfe:replicated-region>
在 Spring ApplicationContext
中定义的 Region bean 包含以下内容:{ "Customers", "/Customers/Accounts", "/Customers/Accounts/Orders" }
。这意味着先前示例中所示的依赖注入引用(即 @Resource(name = "Customers/Accounts")
)现在已损坏,因为实际上没有定义名为 Customers/Accounts
的 bean。因此,您不应按前面两个示例所示配置 Region。
Pivotal GemFire 在引用父 Region 和子区域 (Sub-Regions) 时非常灵活,无论是否带有开头的正斜杠。例如,父级可以引用为 /Customers
或 Customers
,子级可以引用为 /Customers/Accounts
或 Customers/Accounts
。然而,Spring Data for Pivotal GemFire 在根据 Region 命名 bean 时非常具体。它总是使用正斜杠 (/) 来表示子区域(例如,/Customers/Accounts
)。
因此,您应该使用前面所示的非嵌套 lookup-region
语法,或者使用开头的正斜杠 (/) 定义直接引用,如下所示:
<gfe:lookup-region name="/Customers/Accounts"/>
<gfe:lookup-region name="/Customers/Accounts/Orders"/>
前面使用嵌套的 replicated-region
元素引用子区域的示例显示了前面提到的问题。Customers、Accounts 和 Orders Region 和子区域是持久的还是非持久的?它们是非持久的,因为这些 Region 是在 Pivotal GemFire 本地 cache.xml
配置文件中定义为 REPLICATE
的,并且在缓存 bean 初始化之前就已存在(一旦处理了 <gfe:cache>
元素)。
5.5.8. 数据驱逐(带溢出)
根据各种约束,每个 Region 可以有一个驱逐策略,用于从内存中驱逐数据。目前,在 Pivotal GemFire 中,驱逐适用于最近最少使用的条目(也称为 LRU)。被驱逐的条目要么被销毁,要么被分页到磁盘(称为“溢出到磁盘”)。
Spring Data for Pivotal GemFire 通过使用嵌套的 eviction
元素支持 PARTITION Region、REPLICATE Region 以及客户端和本地 Region 的所有驱逐策略(条目计数、内存和堆使用)。
例如,要配置一个 PARTITION Region,使其在内存大小超过 512 MB 时溢出到磁盘,您可以指定以下配置:
<gfe:partitioned-region id="examplePartitionRegionWithEviction">
<gfe:eviction type="MEMORY_SIZE" threshold="512" action="OVERFLOW_TO_DISK"/>
</gfe:partitioned-region>
复制品不能使用 local destroy 驱逐,因为这会使它们失效。有关更多信息,请参阅 Pivotal GemFire 文档。 |
配置 Region 进行溢出时,您应该通过 disk-store
元素配置存储以获得最大效率。
有关驱逐策略的详细说明,请参阅 Pivotal GemFire 文档中关于驱逐的部分。
5.5.9. 数据过期
Pivotal GemFire 允许您控制条目在缓存中存在多长时间。过期是由经过的时间驱动的,与驱逐不同,驱逐是由条目计数、堆或内存使用驱动的。一旦条目过期,就无法再从缓存中访问它。
Pivotal GemFire 支持以下过期类型:
-
存活时间 (TTL):对象在最后创建或更新后可以在缓存中保留的秒数。对于条目,在创建和放置 (put) 操作时计数器设置为零。Region 计数器在创建 Region 和条目计数器重置时重置。
-
空闲超时 (TTI):对象在最后访问后可以在缓存中保留的秒数。对象的空闲超时计数器在其 TTL 计数器重置时也会重置。此外,条目的空闲超时计数器在其通过获取 (get) 操作或
netSearch
访问时也会重置。Region 的空闲超时计数器在其某个条目的空闲超时重置时也会重置。
这些都可以应用于 Region 本身或 Region 中的条目。Spring Data for Pivotal GemFire 提供了 <region-ttl>
、<region-tti>
、<entry-ttl>
和 <entry-tti>
Region 子元素,用于指定超时值和过期操作。
以下示例显示了一个设置了过期值的 PARTITION
Region。
<gfe:partitioned-region id="examplePartitionRegionWithExpiration">
<gfe:region-ttl timeout="30000" action="INVALIDATE"/>
<gfe:entry-tti timeout="600" action="LOCAL_DESTROY"/>
</gfe:replicated-region>
有关过期策略的详细说明,请参阅 Pivotal GemFire 文档中关于过期的部分。
基于注解的数据过期
借助 Spring Data for Pivotal GemFire,您可以在单个 Region 条目值上(或者换句话说,直接在应用程序域对象上)定义过期策略和设置。例如,您可以在基于 Session 的应用程序域对象上定义过期策略,如下所示:
@Expiration(timeout = "1800", action = "INVALIDATE")
public class SessionBasedApplicationDomainObject {
...
}
您还可以通过使用 @IdleTimeoutExpiration
和 @TimeToLiveExpiration
注解分别为空闲超时 (TTI) 和存活时间 (TTL) 过期在 Region 条目上指定特定于过期类型的设置,如下示例所示:
@TimeToLiveExpiration(timeout = "3600", action = "LOCAL_DESTROY")
@IdleTimeoutExpiration(timeout = "1800", action = "LOCAL_INVALIDATE")
@Expiration(timeout = "1800", action = "INVALIDATE")
public class AnotherSessionBasedApplicationDomainObject {
...
}
当指定了多种过期注解类型时,@IdleTimeoutExpiration
和 @TimeToLiveExpiration
都优先于通用 @Expiration
注解,如前例所示。@IdleTimeoutExpiration
和 @TimeToLiveExpiration
不会互相覆盖。相反,当配置了不同的 Region 条目过期策略(例如 TTL 和 TTI)时,它们是互补的。
所有基于
|
Spring Data for Pivotal GemFire 的 @Expiration
注解支持是通过 Pivotal GemFire 的 CustomExpiry
接口实现的。有关更多详细信息,请参阅 Pivotal GemFire 文档中关于配置数据过期的部分。
Spring Data for Pivotal GemFire 的 AnnotationBasedExpiration
类(和 CustomExpiry
实现)负责处理 SDG @Expiration
注解,并在请求时适当地为 Region 条目过期应用过期策略配置。
要使用 Spring Data for Pivotal GemFire 配置特定的 Pivotal GemFire Region,以便将过期策略适当地应用于用基于 @Expiration
的注解标记的应用程序域对象,您必须:
-
在 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。 -
(可选)使用 Spring Data for Pivotal GemFire 的一个
@Expiration
注解(@Expiration
、@IdleTimeoutExpiration
或@TimeToLiveExpiration
)为存储在 Region 中的应用程序域对象标记过期策略和自定义设置。 -
(可选)在某些应用程序域对象根本没有使用 Spring Data for Pivotal GemFire 的
@Expiration
注解标记,但 Pivotal GemFire Region 配置为使用 SDG 的自定义AnnotationBasedExpiration
类来确定存储在 Region 中的对象的过期策略和设置的情况下,您可以通过以下方式在AnnotationBasedExpiration
bean 上设置“默认”过期属性:
<bean id="defaultExpirationAttributes" class="org.apache.geode.cache.ExpirationAttributes">
<constructor-arg value="600"/>
<constructor-arg value="#{T(org.apache.geode.cache.ExpirationAction).DESTROY}"/>
</bean>
<bean id="ttiExpiration" class="org.springframework.data.gemfire.expiration.AnnotationBasedExpiration"
factory-method="forIdleTimeout">
<constructor-arg ref="defaultExpirationAttributes"/>
</bean>
<gfe:partitioned-region id="Example" persistent="false">
<gfe:custom-entry-tti ref="ttiExpiration"/>
</gfe:partitioned-region>
您可能已经注意到,Spring Data for Pivotal GemFire 的 @Expiration
注解使用 String
作为属性类型,而不是(也许更合适地)使用强类型——例如,对于 'timeout' 使用 int
,对于 'action' 使用 SDG 的 ExpirationActionType
。这是为什么呢?
好吧,这就要引入 Spring Data for Pivotal GemFire 的另一个特性了,它利用了 Spring 的核心基础设施来提供配置便利:属性占位符和 Spring 表达式语言 (SpEL) 表达式。
例如,开发人员可以使用属性占位符在 @Expiration
注解属性中指定过期“超时时间”和“操作”,如下示例所示:
@TimeToLiveExpiration(timeout = "${geode.region.entry.expiration.ttl.timeout}"
action = "${geode.region.entry.expiration.ttl.action}")
public class ExampleApplicationDomainObject {
...
}
然后,在您的 Spring XML 配置或 JavaConfig 中,您可以声明以下 bean:
<util:properties id="expirationSettings">
<prop key="geode.region.entry.expiration.ttl.timeout">600</prop>
<prop key="geode.region.entry.expiration.ttl.action">INVALIDATE</prop>
...
</util:properties>
<context:property-placeholder properties-ref="expirationProperties"/>
当多个应用程序域对象可能共享相似的过期策略时,以及当您希望外部化配置时,这都非常方便。
然而,您可能希望通过运行系统的状态来确定更动态的过期配置。这就是 SpEL 的强大之处所在,实际上也是推荐的方法。您不仅可以引用 Spring 容器中的 bean 并访问 bean 属性、调用方法等,而且过期“超时时间”和“操作”的值可以是强类型的。考虑以下示例(它以前面的示例为基础):
<util:properties id="expirationSettings">
<prop key="geode.region.entry.expiration.ttl.timeout">600</prop>
<prop key="geode.region.entry.expiration.ttl.action">#{T(org.springframework.data.gemfire.expiration.ExpirationActionType).DESTROY}</prop>
<prop key="geode.region.entry.expiration.tti.action">#{T(org.apache.geode.cache.ExpirationAction).INVALIDATE}</prop>
...
</util:properties>
<context:property-placeholder properties-ref="expirationProperties"/>
然后,在您的应用程序域对象上,您可以按如下方式定义超时时间和操作:
@TimeToLiveExpiration(timeout = "@expirationSettings['geode.region.entry.expiration.ttl.timeout']"
action = "@expirationSetting['geode.region.entry.expiration.ttl.action']")
public class ExampleApplicationDomainObject {
...
}
您可以想象,“expirationSettings”bean 可以是一个比简单的 java.util.Properties
实例更有趣、更有用的对象。在前面的示例中,properties
元素(expirationSettings
)使用 SpEL 将操作值基于实际的 ExpirationAction
枚举类型,这使得如果枚举类型发生变化,可以快速识别出故障。
作为一个示例,所有这些都在 Spring Data for Pivotal GemFire 测试套件中得到了演示和测试。有关更多详细信息,请参阅源代码。
5.5.10. 数据持久化
Region 可以是持久的。Pivotal GemFire 确保您放入配置为持久化的 Region 中的所有数据都被写入磁盘,以便下次重建 Region 时可以恢复。这样做可以使数据在机器或进程故障后,甚至在有序关机和随后的 Pivotal GemFire 数据节点重启后都能恢复。
要使用 Spring Data for Pivotal GemFire 启用持久化,请在任何 <*-region>
元素上将 persistent
属性设置为 true
,如下示例所示:
<gfe:partitioned-region id="examplePersitentPartitionRegion" persistent="true"/>
持久化也可以通过设置 data-policy
属性来配置。为此,请将属性值设置为 Pivotal GemFire 的 DataPolicy 设置之一,如下示例所示:
<gfe:partitioned-region id="anotherExamplePersistentPartitionRegion" data-policy="PERSISTENT_PARTITION"/>
DataPolicy
必须与 Region 类型匹配,并且如果 persistent
属性也已显式设置,则必须与该属性一致。如果 persistent
属性设置为 false
,但指定了持久化的 DataPolicy
(例如 PERSISTENT_REPLICATE
或 PERSISTENT_PARTITION
),则会抛出初始化异常。
为了在持久化 Region 时获得最大效率,您应该通过 disk-store
元素配置存储。DiskStore
通过使用 disk-store-ref
属性引用。此外,Region 可以同步或异步执行磁盘写入。以下示例显示了一个同步的 DiskStore
:
<gfe:partitioned-region id="yetAnotherExamplePersistentPartitionRegion" persistent="true"
disk-store-ref="myDiskStore" disk-synchronous="true"/>
这将在配置 DiskStore中进一步讨论。
5.5.11. 订阅策略
Pivotal GemFire 允许配置对等 (P2P) 事件消息传递,以控制 Region 接收的条目事件。Spring Data for Pivotal GemFire 提供了 <gfe:subscription/>
子元素,用于将 REPLICATE
和 PARTITION
Region 的订阅策略设置为 ALL
或 CACHE_CONTENT
。以下示例显示了一个订阅策略设置为 CACHE_CONTENT
的 region:
<gfe:partitioned-region id="examplePartitionRegionWithCustomSubscription">
<gfe:subscription type="CACHE_CONTENT"/>
</gfe:partitioned-region>
5.5.12. 本地 Region
Spring Data for Pivotal GemFire 提供了专用的 local-region
元素来创建本地 Region。顾名思义,本地 Region 是独立的,意味着它们不与其他分布式系统成员共享数据。除此之外,所有通用的 Region 配置选项都适用。
以下示例显示了一个最小声明(同样,此示例依赖于 Spring Data for Pivotal GemFire XML 命名空间命名约定来连接缓存)。
<gfe:local-region id="exampleLocalRegion"/>
在前面的示例中,创建了一个本地 Region(如果同名 Region 不存在)。Region 的名称与 bean ID(exampleLocalRegion
)相同,并且该 bean 假定存在一个名为 gemfireCache
的 Pivotal GemFire 缓存。
5.5.13. 复制 Region
一种常见的 Region 类型是 REPLICATE
Region 或“复制品”。简而言之,当 Region 配置为 REPLICATE
时,托管该 Region 的每个成员都会在本地存储该 Region 条目的副本。对 REPLICATE
Region 的任何更新都会分发到该 Region 的所有副本。创建复制品时,它会经历一个初始化阶段,在此阶段它会发现其他复制品并自动复制所有条目。当一个复制品正在初始化时,您仍然可以继续使用其他复制品。
REPLICATE Region 可使用所有通用配置选项。Spring Data for Pivotal GemFire 提供了 replicated-region
元素。以下示例显示了一个最小声明:
<gfe:replicated-region id="exampleReplica"/>
有关更多详细信息,请参阅 Pivotal GemFire 文档中关于分布式和复制 Region 的部分。
5.5.14. 分区 Region
Spring Data for Pivotal GemFire XML 命名空间也支持 PARTITION
Region。
引用 Pivotal GemFire 文档:
“分区 Region 是一种 Region,其中数据在托管该 Region 的对等服务器之间划分,以便每个对等服务器存储数据的一个子集。使用分区 Region 时,应用程序会看到 Region 的逻辑视图,看起来像一个包含 Region 中所有数据的单个映射。对此映射的读写操作会被透明地路由到托管操作目标条目的对等服务器。Pivotal GemFire 将哈希码域划分为桶 (bucket)。每个桶被分配给一个特定的对等服务器,但随时可以迁移到另一个对等服务器,以改善整个集群的资源利用率。”
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 文档中关于分区 Region 的部分。
分区 Region 属性
下表快速概述了 PARTITION
Region 特有的配置选项。这些选项是对前面描述的通用 Region 配置选项的补充。
名称 | 值 | 描述 |
---|---|---|
copies |
0..4 |
每个分区的副本数,用于高可用性。默认情况下不创建副本,意味着没有冗余。每个副本以额外存储为代价提供额外的备份。 |
colocated-with |
有效的 region 名称。 |
与新创建的 |
local-max-memory |
正整数。 |
Region 在此进程中使用的最大内存量(以兆字节为单位)。 |
total-max-memory |
任意整数值。 |
Region 在所有进程中使用的最大内存量(以兆字节为单位)。 |
partition-listener |
bean 名称。 |
此 region 用于处理分区事件的 |
partition-resolver |
bean 名称。 |
此 region 用于自定义分区的 |
recovery-delay |
任意 long 值。 |
在另一个成员崩溃后,现有成员在满足冗余之前等待的延迟(以毫秒为单位)。-1(默认值)表示在故障后不恢复冗余。 |
startup-recovery-delay |
任意 long 值。 |
新成员在满足冗余之前等待的延迟(以毫秒为单位)。-1 表示添加新成员不会触发冗余恢复。默认是在添加新成员时立即恢复冗余。 |
5.5.15. 客户端 Region
Pivotal GemFire 支持各种部署拓扑来管理和分发数据。Pivotal GemFire 拓扑的主题超出了本文档的范围。然而,快速回顾一下,Pivotal GemFire 支持的拓扑可以分为:对等 (p2p)、客户端-服务器和广域网 (WAN)。在后两种配置中,声明连接到缓存服务器的客户端 Region 很常见。
Spring Data for Pivotal GemFire 通过其 客户端缓存 元素:client-region
和 pool
,为每种配置提供了专用支持。顾名思义,client-region
定义了一个客户端 Region,而 pool
定义了一个连接池 (Pool),供各种客户端 Region 使用和共享。
以下示例显示了一个典型的客户端 Region 配置。
<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>
与其他 Region 类型一样,client-region
支持 CacheListener
实例以及 CacheLoader
和 CacheWriter
。它还需要一个连接池 (Pool
) 来连接到一组 Locators 或服务器。每个客户端 Region 可以有自己的连接池,也可以共享同一个连接池。如果未指定连接池,则使用“DEFAULT”连接池。
在前面的示例中,连接池配置了一个 Locator。Locator 是一个单独的进程,用于在分布式系统中发现缓存服务器和对等数据成员,推荐用于生产系统。还可以通过使用 server 元素将连接池配置为直接连接到一个或多个缓存服务器。 |
有关在客户端,尤其是在连接池上设置的完整选项列表,请参阅 Spring Data for Pivotal GemFire 模式(“Spring Data for Pivotal GemFire 模式”)和 Pivotal GemFire 文档中关于客户端-服务器配置的部分。
客户端兴趣
为了最小化网络流量,每个客户端可以单独定义自己的“兴趣”策略,向 Pivotal GemFire 指示它实际需要的数据。在 Spring Data for Pivotal GemFire 中,“兴趣”可以为每个客户端 Region 单独定义。支持基于键和基于正则表达式的兴趣类型。
以下示例显示了基于键和基于正则表达式的 interest
类型。
<gfe:client-region id="Example" pool-name="myPool">
<gfe:key-interest durable="true" result-policy="KEYS">
<bean id="key" class="java.lang.String">
<constructor-arg value="someKey"/>
</bean>
</gfe:key-interest>
<gfe:regex-interest pattern=".*" receive-values="false"/>
</gfe:client-region>
特殊的键 ALL_KEYS
表示为所有键注册了“兴趣”。通过使用正则表达式 ".\*"
也可以实现同样的效果。
<gfe:*-interest>
键和正则表达式元素支持三个属性:durable
、receive-values
和 result-policy
。
durable
表示当客户端连接到集群中的一个或多个服务器时为客户端创建的“兴趣”策略和订阅队列是否在客户端会话之间保持。如果客户端离开后又回来,在客户端断开连接期间,服务器上为该客户端维护的 durable
订阅队列会得以保留。当客户端重新连接时,客户端会接收到在断开与集群服务器的连接期间发生的任何事件。
集群中服务器上的订阅队列会为客户端中定义的、且为该连接池启用了“订阅”的每个连接池保持。订阅队列用于存储(并可能合并)发送到客户端的事件。如果订阅队列是 durable
的,它将在客户端会话(即连接)之间持久存在,可能长达指定的超时时间。如果客户端在给定的时间范围内没有返回,则客户端连接池订阅队列将被销毁,以减少集群中服务器的资源消耗。如果订阅队列不是 durable
的,它将在客户端断开连接时立即销毁。您需要决定客户端是应该接收断开连接期间发生的事件,还是只需要在重新连接后接收最新的事件。
receive-values
属性指示创建和更新事件是否接收条目值。如果为 true
,则接收值。如果为 false
,则仅接收失效 (invalidation) 事件。
最后,result-policy
是一个枚举类型,包含:KEYS
、KEYS_VALUE
和 NONE
。默认值是 KEYS_VALUES
。result-policy
控制客户端首次连接以初始化本地缓存时的初始转储 (initial dump),本质上是为客户端播种 (seeding) 与兴趣策略匹配的所有条目的事件。
正如前面提到的,如果没有在连接池上启用订阅,客户端的兴趣注册就没有多大用处。实际上,在没有启用订阅的情况下尝试兴趣注册是一个错误。以下示例展示了如何执行此操作:
<gfe:pool ... subscription-enabled="true">
...
</gfe:pool>
除了 subscription-enabled
,您还可以设置 subscription-ack-interval
、subscription-message-tracking-timeout
和 subscription-redundancy
。subscription-redundancy
用于控制集群中的服务器应该维护多少个订阅队列的副本。如果冗余度大于一,并且“主”订阅队列(即服务器)发生故障,那么“次”订阅队列将接管,从而在 HA 场景中防止客户端丢失事件。
除了连接池设置外,服务器端 Region 使用一个附加属性 enable-subscription-conflation
来控制发送到客户端的事件的合并 (conflation)。这也有助于进一步最小化网络流量,在应用程序只关心条目最新值的情况下非常有用。然而,当应用程序保留发生事件的时间序列时,合并会阻碍这种用例。默认值是 false
。以下示例显示了服务器上的 Region 配置,客户端包含一个对应的客户端 [CACHING_]PROXY
Region,该 Region 对此服务器 Region 中的键感兴趣。
<gfe:partitioned-region name="ServerSideRegion" enable-subscription-conflation="true">
...
</gfe:partitioned-region>
为了控制客户端断开与集群中服务器的连接后保持“durable”订阅队列的时间(以秒为单位),请在 <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 组件来通知 (advise) 适当的、被代理的 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
。
此外,默认情况下,配置的 Region 的 getAll()
和 values()
Region 操作结果会被转换。这是通过在本地内存中创建一个并行数据结构来实现的。这可能会给大型集合带来显著的开销,因此如果您想禁用这些 Region 操作的自动转换,请将 convert-returned-collections
设置为 false
。
某些 Region 操作(特别是那些使用 Pivotal GemFire 专有 Region.Entry 类型的操作,例如:entries(boolean) 、entrySet(boolean) 和 getEntry() )不是 AOP 通知的目标。此外,entrySet() 方法(返回一个 Set<java.util.Map.Entry<?, ?>> )也不受影响。 |
以下示例配置展示了如何设置 pretty-print
和 convert-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 (Object Query Language) 查询的性能。
在 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 命名空间),index
Bean 声明不像 Pivotal GemFire 的原生 cache.xml
那样绑定到特定的 Region。相反,它们是顶层元素,类似于 <gfe:cache>
元素。这允许您在任何 Region 上声明任意数量的索引,无论它们是刚刚创建的还是已经存在的 — 相对于 Pivotal GemFire 的原生 cache.xml
格式,这是一个显著的改进。
一个 Index
必须有一个名称。您可以使用 name
属性为 Index
指定一个显式名称。否则,index
Bean 定义的 Bean 名称(即 id
属性的值)将被用作 Index
名称。
expression
和 from
子句构成 Index
的主要组成部分,它们标识要索引的数据(即 from
子句中标识的 Region)以及用于索引数据的条件(即 expression
)。expression
应该基于应用程序域对象字段,这些字段用于查询和查找存储在 Region 中的对象的应用程序定义的 OQL 查询的谓词中。
考虑以下示例,它有一个 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 查找/查询方法会生成并运行以下 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 的一项特性。
Index
的 type
可以是 Spring Data for Pivotal GemFire 的 IndexType
枚举定义的三个枚举值之一:FUNCTIONAL
、HASH
和 PRIMARY_KEY
。
每个枚举值对应于 QueryService
create[|Key|Hash]Index
方法中的一个,在实际创建(或“定义”——您可以在下一节找到更多关于“定义”索引的信息)Index
时会调用这些方法。例如,如果 IndexType
是 PRIMARY_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 在 Spring 容器初始化时处理 Index
Bean 定义来提前创建索引外,您还可以通过使用 define
属性来在创建所有应用程序索引之前先对其进行定义,如下所示
<gfe:index id="myDefinedIndex" expression="someField" from="/SomeRegion" define="true"/>
当 define
设置为 true
时(默认值为 false
),它实际上不会立即创建 Index
。所有“已定义”的 Index 会在 Spring ApplicationContext
“刷新”时,或者换句话说,当 Spring 容器发布 ContextRefreshedEvent
时,一次性全部创建。Spring Data for Pivotal GemFire 将自己注册为一个 ApplicationListener
,监听 ContextRefreshedEvent
。当事件触发时,Spring Data for Pivotal GemFire 会调用 QueryService.createDefinedIndexes()
。
一次性定义和创建索引可以提高创建索引的速度和效率。
有关更多详细信息,请参阅“一次创建多个索引”。
5.6.2. IgnoreIfExists
和 Override
Spring Data for Pivotal GemFire 的两个 Index
配置选项值得特别提及:ignoreIfExists
和 override
。
这些选项分别对应于 Spring Data for Pivotal GemFire XML 命名空间中 <gfe:index>
元素上的 ignore-if-exists
和 override
属性。
在使用这些选项中的任何一个之前,请务必完全理解您正在做什么。这些选项可能会影响应用程序在运行时消耗的性能和资源(例如内存)。因此,SDG 默认禁用(设置为 false )这两个选项。 |
这些选项仅在 Spring Data for Pivotal GemFire 中可用,用于解决 Pivotal GemFire 的已知限制。Pivotal GemFire 没有等效的选项或功能。 |
每个选项在行为上存在显著差异,并且完全取决于抛出的 Pivotal GemFire Index
异常类型。这也意味着如果未抛出 Pivotal GemFire Index 类型的异常,这两个选项都不会产生任何影响。这些选项旨在专门处理 Pivotal GemFire 的 IndexExistsException
和 IndexNameConflictException
实例,这些异常可能由于各种有时不明确的原因而发生。这些异常的起因如下
-
当尝试创建
Index
时,如果存在另一个定义相同但名称不同的Index
,则会抛出IndexExistsException
。 -
当尝试创建
Index
时,如果存在另一个名称相同但定义可能不同的Index
,则会抛出IndexNameConflictException
。
Spring Data for Pivotal GemFire 的默认行为是始终快速失败。因此,默认情况下不会“处理”Index
异常。这些 Index
异常会被包装在 SDG GemfireIndexException
中并重新抛出。如果您希望 Spring Data for Pivotal GemFire 为您处理它们,可以将这些 Index
Bean 定义选项中的任何一个设置为 true
。
IgnoreIfExists
总是优先于 Override
,这主要是因为它消耗的资源较少,原因很简单,它在这两种异常情况下都只返回“已存在”的 Index
。
IgnoreIfExists
行为
当抛出 IndexExistsException
并且 ignoreIfExists
设置为 true
时(或 <gfe:index ignore-if-exists="true">
),本来会由这个 index
Bean 定义或声明创建的 Index
会被简单地忽略,并返回已存在的 Index
。
返回已存在的 Index
几乎没有影响,因为 index
Bean 定义是相同的,这是由 Pivotal GemFire 本身(而非 SDG)确定的。
然而,这也意味着从 Pivotal GemFire 的角度来看(即通过 QueryService.getIndexes()
),实际上不存在您的 index
Bean 定义或声明中指定的“名称”的 Index
。因此,在使用查询提示(尤其是引用被忽略的应用程序 Index
的查询提示)编写 OQL 查询语句时,应小心谨慎。这些查询提示需要更改。
当抛出 IndexNameConflictException
并且 ignoreIfExists
设置为 true
时(或 <gfe:index ignore-if-exists="true">
),本来会由这个 index
Bean 定义或声明创建的 Index
也会被忽略,并且再次返回“已存在”的 Index
,就像抛出 IndexExistsException
时一样。
然而,当抛出 IndexNameConflictException
时,返回已存在的 Index
并忽略应用程序定义的 Index
存在更大的风险。对于 IndexNameConflictException
,冲突索引的名称虽然相同,但定义可能不同。这种情况可能对应用程序特定的 OQL 查询产生影响,您可能假设索引是专门为应用程序数据访问模式和查询而定义的。然而,如果名称相似的索引定义不同,情况可能并非如此。因此,您应该验证您的 Index
名称。
当被忽略的 Index 的定义与现有 Index 的定义显著不同时,SDG 会尽最大努力通知用户。然而,为了让 SDG 实现这一点,它必须能够找到现有 Index ,这是通过 Pivotal GemFire API(唯一可用的方法)查找的。 |
Override
行为
当抛出 IndexExistsException
并且 override
设置为 true
时(或 <gfe:index override="true">
),Index
会被有效地重命名。请记住,当存在多个定义相同但名称不同的索引时,会抛出 IndexExistsExceptions
。
Spring Data for Pivotal GemFire 只能通过使用 Pivotal GemFire 的 API 来实现这一点,首先移除现有 Index
,然后使用新名称重新创建 Index
。移除或后续创建的调用都可能失败。无法原子地执行这两个操作并在其中任何一个失败时回滚这个联合操作。
然而,如果成功,您将面临与使用 ignoreIfExists
选项时相同的问题。任何使用查询提示引用旧 Index
名称的现有 OQL 查询语句都必须更改。
当抛出 IndexNameConflictException
并且 override
设置为 true
时(或 <gfe:index override="true">
),现有 Index
可能被重新定义。我们说“可能”是因为在抛出 IndexNameConflictException
时,名称相同、已存在的 Index
的定义和名称可能完全相同。
如果是这样,SDG 会智能地返回现有 Index
的原样,即使在 override
模式下也是如此。这种行为没有危害,因为名称和定义完全相同。当然,SDG 只有在能够找到现有 Index
时才能实现这一点,这取决于 Pivotal GemFire 的 API。如果找不到,则不会发生任何事情,并且会抛出一个包装了 IndexNameConflictException
的 SDG GemfireIndexException
。
然而,当现有 Index
的定义不同时,SDG 会尝试使用 index
Bean 定义中指定的 Index
定义来重新创建 Index
。请确保这是您想要的行为,并确保 index
Bean 定义符合您的期望和应用程序要求。
IndexNameConflictExceptions
实际上如何发生?
抛出 IndexExistsExceptions
并非罕见,尤其是在使用多种配置源配置 Pivotal GemFire 时(Spring Data for Pivotal GemFire、Pivotal GemFire Cluster Config、Pivotal GemFire 原生 cache.xml
、API 等)。您绝对应该偏好一种配置方法并坚持使用它。
然而,IndexNameConflictException
何时会被抛出呢?
一个特定情况是定义在 PARTITION
Region (PR) 上的 Index
。当 Index
定义在 PARTITION
Region (例如,X
) 上时,Pivotal GemFire 会将 Index
定义(和名称)分发到集群中托管相同 PARTITION
Region (即 "X") 的其他对等成员。将此 Index
定义分发给对等成员并由其创建的过程是按需进行的(即由托管相同 PR 的对等成员进行),并且是异步执行的。
在此时间窗口内,Pivotal GemFire 可能无法通过调用 API(例如 QueryService.getIndexes()
、QueryService.getIndexes(:Region)
,甚至 QueryService.getIndex(:Region, indexName:String)
)来识别这些待处理的 PR Indexes
。
因此,SDG 或其他 Pivotal GemFire 缓存客户端应用程序(不涉及 Spring)要想确定,唯一的方法就是尝试创建 Index
。如果创建失败并抛出 IndexNameConflictException
或 IndexExistsException
,应用程序就知道存在问题。这是因为 QueryService
的 Index
创建会等待待处理的 Index
定义,而其他 Pivotal GemFire API 调用则不会。
无论如何,SDG 会尽最大努力尝试通知您发生了什么或正在发生什么,并告知您纠正措施。考虑到所有 Pivotal GemFire QueryService.createIndex(..)
方法都是同步的阻塞操作,在抛出这些索引类型异常中的任何一个之后,Pivotal GemFire 的状态应该是一致且可访问的。因此,SDG 可以检查系统的状态,并根据您的配置采取相应的行动。
在所有其他情况下,SDG 采用快速失败的策略。
5.7. 配置 DiskStore
Spring Data for Pivotal GemFire 通过 disk-store
元素支持 DiskStore
的配置和创建,如下例所示
<gfe:disk-store id="Example" auto-compact="true" max-oplog-size="10"
queue-size="50" time-interval="9999">
<gfe:disk-dir location="/disk/location/one" max-size="20"/>
<gfe:disk-dir location="/disk/location/two" max-size="20"/>
</gfe:disk-store>
Region 使用 DiskStore
实例进行文件系统持久化备份,并溢出被驱逐的条目,同时也为 WAN 网关提供持久化备份。多个 Pivotal GemFire 组件可以共享同一个 DiskStore
。此外,可以为一个 DiskStore
定义多个文件系统目录,如上例所示。
有关 DiskStore
实例的 持久化和溢出 以及配置选项的完整说明,请参阅 Pivotal GemFire 的文档。
5.8. 配置快照服务
Spring Data for Pivotal GemFire 通过使用 Pivotal GemFire 的快照服务 来支持缓存和 Region 快照。开箱即用的快照服务支持提供了几个方便的功能,以简化使用 Pivotal GemFire 的 缓存 和 Region 快照服务 API。
正如 Pivotal GemFire 文档 所述,快照允许您保存并在以后重新加载缓存的数据,这对于在环境之间移动数据非常有用,例如将数据从生产环境移动到预演或测试环境,以便在受控环境中重现数据相关问题。您可以将 Spring Data for Pivotal GemFire 的快照服务支持与 Spring 的 Bean 定义配置文件 结合使用,以便根据需要加载特定于环境的快照数据。
Spring Data for Pivotal GemFire 对 Pivotal GemFire 快照服务的支持始于 <gfe-data>
XML 命名空间中的 <gfe-data:snapshot-service>
元素。
例如,您可以使用几个快照导入和一个数据导出定义来定义要加载和保存的缓存范围快照,如下所示
<gfe-data:snapshot-service id="gemfireCacheSnapshotService">
<gfe-data:snapshot-import location="/absolute/filesystem/path/to/import/fileOne.snapshot"/>
<gfe-data:snapshot-import location="relative/filesystem/path/to/import/fileTwo.snapshot"/>
<gfe-data:snapshot-export
location="/absolute/or/relative/filesystem/path/to/export/directory"/>
</gfe-data:snapshot-service>
您可以根据需要定义任意数量的导入和导出。您只可以定义导入,也可以只定义导出。文件位置和目录路径可以是绝对路径,也可以是相对于 Spring Data for Pivotal GemFire 应用程序(即 JVM 进程的工作目录)的相对路径。
上面的例子非常简单,本例中定义的快照服务引用了默认名称为 gemfireCache
的 Pivotal GemFire 缓存实例(如配置缓存中所述)。如果您将缓存 Bean 定义的名称设置为非默认值,可以使用 cache-ref
属性按名称引用缓存 Bean,如下所示
<gfe:cache id="myCache"/>
...
<gfe-data:snapshot-service id="mySnapshotService" cache-ref="myCache">
...
</gfe-data:snapshot-service>
您还可以通过指定 region-ref
属性为特定 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 接口以及 组合 (Composite) 软件设计模式。
简而言之,组合 (Composite) 软件设计模式允许您组合多个相同类型的对象,并将集合视为该对象类型的单个实例——这是一个强大且有用的抽象。
ComposableSnapshotFilter
有两个工厂方法,and
和 or
。它们允许您分别使用 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>
然后,您可以使用 or
将 activesUsersSinceFilter
与另一个过滤器组合,如下所示
<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
类用于导入和导出:ImportSnapshotApplicationEvent
和 ExportSnapshotApplicationEvent
。
这两个应用程序事件可以针对整个 Pivotal GemFire 缓存或单个 Pivotal GemFire Region。这些类中的构造函数接受一个可选的 Region 路径名(例如 /Example
)以及零个或多个 SnapshotMetadata
实例。
SnapshotMetadata
数组会覆盖由 <gfe-data:snapshot-import>
和 <gfe-data:snapshot-export>
子元素定义的快照元数据,后者用于快照应用程序事件未明确提供 SnapshotMetadata
的情况。每个单独的 SnapshotMetadata
实例都可以定义自己的 location
和 filters
属性。
Spring ApplicationContext
中定义的所有快照服务 Bean 都会接收导入和导出快照应用程序事件。但是,只有匹配的快照服务 Bean 才会处理导入和导出事件。
如果定义的快照服务 Bean 是 RegionSnapshotService
并且其 Region 引用(由 region-ref
属性确定)与快照应用程序事件指定的 Region 路径名匹配,则 Region 上的 [Import|Export]SnapshotApplicationEvent
匹配。
基于缓存的 [Import|Export]SnapshotApplicationEvent
(即没有 Region 路径名的快照应用程序事件)会触发所有快照服务 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 的 调度 服务定期触发快照应用程序事件。
5.9. 配置函数服务
Spring Data for Pivotal GemFire 为实现、注册和执行 Pivotal GemFire Function 提供 注解 支持。
Spring Data for Pivotal GemFire 还提供 XML 命名空间支持,用于注册 Pivotal GemFire Functions 以进行远程函数执行。
有关 Function 执行框架的更多信息,请参阅 Pivotal GemFire 的 文档。
Pivotal GemFire Function 被声明为 Spring Bean,并且必须实现 org.apache.geode.cache.execute.Function
接口或扩展 org.apache.geode.cache.execute.FunctionAdapter
。
命名空间使用熟悉的模式声明 Function,如下例所示
<gfe:function-service>
<gfe:function>
<bean class="example.FunctionOne"/>
<ref bean="function2"/>
</gfe:function>
</gfe:function-service>
<bean id="function2" class="example.FunctionTwo"/>
5.10. 配置 WAN 网关
WAN 网关提供了一种跨地理位置同步 Pivotal GemFire 分布式系统的方法。Spring Data for Pivotal GemFire 提供 XML 命名空间支持,用于配置 WAN 网关,如下例所示。
5.10.1. Pivotal GemFire 7.0 中的 WAN 配置
在以下示例中,通过向 Region 添加子元素 (gateway-sender
和 gateway-sender-ref
) 来为 PARTITION
Region 配置 GatewaySenders
。GatewaySender
可以注册 EventFilters
和 TransportFilters
。
以下示例还展示了 AsyncEventQueue
的示例配置,该配置也必须自动装配到 Region 中(未显示)
<gfe:partitioned-region id="region-with-inner-gateway-sender" >
<gfe:gateway-sender remote-distributed-system-id="1">
<gfe:event-filter>
<bean class="org.springframework.data.gemfire.example.SomeEventFilter"/>
</gfe:event-filter>
<gfe:transport-filter>
<bean class="org.springframework.data.gemfire.example.SomeTransportFilter"/>
</gfe:transport-filter>
</gfe:gateway-sender>
<gfe:gateway-sender-ref bean="gateway-sender"/>
</gfe:partitioned-region>
<gfe:async-event-queue id="async-event-queue" batch-size="10" persistent="true" disk-store-ref="diskstore"
maximum-queue-memory="50">
<gfe:async-event-listener>
<bean class="example.AsyncEventListener"/>
</gfe:async-event-listener>
</gfe:async-event-queue>
<gfe:gateway-sender id="gateway-sender" remote-distributed-system-id="2">
<gfe:event-filter>
<ref bean="event-filter"/>
<bean class="org.springframework.data.gemfire.example.SomeEventFilter"/>
</gfe:event-filter>
<gfe:transport-filter>
<ref bean="transport-filter"/>
<bean class="org.springframework.data.gemfire.example.SomeTransportFilter"/>
</gfe:transport-filter>
</gfe:gateway-sender>
<bean id="event-filter" class="org.springframework.data.gemfire.example.AnotherEventFilter"/>
<bean id="transport-filter" class="org.springframework.data.gemfire.example.AnotherTransportFilter"/>
GatewaySender
的另一端是对应的 GatewayReceiver
,用于接收网关事件。GatewayReceiver
也可以配置 EventFilters
和 TransportFilters
,如下所示
<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 schema 进行配置,使用 gfe-data
XML schema 进行数据访问。有关更多详细信息,请参阅“使用 Spring 容器引导 Pivotal GemFire”。
截至 SDG 2.0,基于注解的配置模型尚不支持 Pivotal GemFire 的 WAN 组件和拓扑的配置。 |
与 Spring Boot 一样,Spring Data for Pivotal GemFire 的基于注解的配置模型设计为一种观点明确、约定优于配置的方法来使用 Pivotal GemFire。事实上,这种基于注解的配置模型也受到了 Spring Boot 以及其他几个 Spring 和 Spring Data 项目的共同启发。
通过遵循约定,所有注解都为其所有配置属性提供了合理且明智的默认值。给定注解属性的默认值直接对应于 Pivotal GemFire 为相同配置属性提供的默认值。
其目的是让您通过在 Spring @Configuration
或 @SpringBootApplication
类上声明相应的注解来启用 Pivotal GemFire 功能或嵌入式服务,而无需不必要地配置大量属性才能使用该功能或服务。
再次强调,快速且尽可能轻松地上手是主要目标。
然而,如果您需要定制 Pivotal GemFire 的配置元数据和行为,选项是存在的,并且 Spring Data for Pivotal GemFire 的基于注解的配置会默默地退让。您只需指定希望调整的配置属性。此外,正如我们稍后将在本文档中看到的那样,有几种方法可以使用注解配置 Pivotal GemFire 功能或嵌入式服务。
您可以在 org.springframework.data.gemfire.config.annotation
包中找到所有新的 SDG Java Annotations
。
6.2. 使用 Spring 配置 Pivotal GemFire 应用程序
像所有通过使用@SpringBootApplication
注解应用类来开始的Spring Boot应用程序一样,一个Spring Boot应用程序可以通过声明以下三个主要注解中的任何一个,轻松地成为Pivotal GemFire缓存应用程序:
-
@ClientCacheApplication
-
@PeerCacheApplication
-
@CacheServerApplication
这三个注解是Spring应用程序开发人员在使用Pivotal GemFire时的起点。
要理解这些注解背后的意图,您必须明白Pivotal GemFire可以创建两种类型的缓存实例:客户端缓存(client cache)或对等缓存(peer cache)。
您可以使用ClientCache
实例将Spring Boot应用程序配置为Pivotal GemFire缓存客户端,该客户端可以与现有的Pivotal GemFire服务器集群通信,用于管理应用程序的数据。客户端-服务器拓扑是使用Pivotal GemFire时最常见的系统架构,您只需使用@ClientCacheApplication
注解您的Spring Boot应用程序,就可以使其成为一个带有ClientCache
实例的缓存客户端。
另外,Spring Boot应用程序可以是Pivotal GemFire集群的一个对等成员(peer member)。也就是说,应用程序本身只是管理数据的服务器集群中的另一台服务器。当您使用@PeerCacheApplication
注解您的应用类时,Spring Boot应用程序会创建一个“嵌入式”的对等Cache
实例。
进一步来说,对等缓存应用程序也可以充当CacheServer
,允许缓存客户端连接并执行数据访问操作。这可以通过使用@CacheServerApplication
替代@PeerCacheApplication
注解应用类来实现,它会创建一个对等Cache
实例以及允许缓存客户端连接的CacheServer
。
Pivotal GemFire服务器默认不一定是缓存服务器。也就是说,服务器仅仅因为是服务器,并不一定会被设置为服务缓存客户端。Pivotal GemFire服务器可以是集群的对等成员(数据节点),管理数据而不服务任何客户端,而集群中的其他对等成员除了管理数据外,确实也被设置为服务客户端。还可以将集群中的某些对等成员设置为非数据节点,称为数据访问器(data accessors),它们不存储数据,但充当代理为客户端提供服务作为CacheServers 。Pivotal GemFire支持许多不同的拓扑结构和集群安排,但这超出了本文档的范围。 |
举例来说,如果您想创建一个Spring Boot缓存客户端应用程序,请从以下内容开始:
ClientCache
应用程序@SpringBootApplication
@ClientCacheApplication
class ClientApplication { .. }
或者,如果您想创建一个带有嵌入式对等Cache
实例的Spring Boot应用程序,其中您的应用程序将是Pivotal GemFire形成的集群(分布式系统)中的服务器和对等成员,请从以下内容开始:
Cache
应用程序@SpringBootApplication
@PeerCacheApplication
class ServerApplication { .. }
或者,您可以使用@CacheServerApplication
注解替代@PeerCacheApplication
来同时创建一个嵌入式对等Cache
实例和一个运行在localhost
上并监听默认缓存服务器端口40404
的CacheServer
,如下所示:
Cache
应用程序带CacheServer
@SpringBootApplication
@CacheServerApplication
class ServerApplication { .. }
6.3. 客户端/服务器应用程序详解
客户端连接和与Pivotal GemFire集群中的服务器通信有多种方式。最常见和推荐的方法是使用Pivotal GemFire Locators。
缓存客户端可以连接到Pivotal GemFire集群中的一个或多个Locators,而不是直接连接到CacheServer 。使用Locators而不是直接CacheServer 连接的优点是,Locators提供了客户端所连接集群的元数据。这些元数据包括哪些服务器包含感兴趣的数据或哪些服务器负载最小等信息。与Locator结合使用的客户端Pool 还提供了CacheServer 崩溃时的故障转移能力。通过在客户端Pool 中启用PARTITION Region (PR) 单跳(single-hop)功能,客户端可以直接路由到包含客户端请求和需要的数据的服务器。 |
Locators也是集群中的对等成员。Locators实际上构成了Pivotal GemFire节点集群的主体。也就是说,由Locator连接的所有节点都是集群中的对等节点,新成员使用Locators加入集群并查找其他成员。 |
默认情况下,当创建ClientCache
实例时,Pivotal GemFire会设置一个连接到运行在localhost
上、监听端口40404
的CacheServer
的“DEFAULT”Pool
。CacheServer
监听端口40404
,接受所有系统网卡的连接。您无需做任何特殊配置来使用客户端-服务器拓扑结构。只需使用@CacheServerApplication
注解您的服务器端Spring Boot应用程序,使用@ClientCacheApplication
注解您的客户端Spring Boot应用程序,即可开始使用。
如果您愿意,甚至可以使用Gfsh的start server
命令启动服务器。您的Spring Boot @ClientCacheApplication
无论服务器如何启动,仍然可以连接到服务器。然而,您可能更倾向于使用Spring Data for Pivotal GemFire的方法来配置和启动服务器,因为一个正确注解的Spring Boot应用类更加直观且易于调试。
作为应用程序开发人员,您无疑会希望自定义Pivotal GemFire设置的“DEFAULT”Pool
,以便可能连接到一个或多个Locators,示例如下:
ClientCache
应用程序@SpringBootApplication
@ClientCacheApplication(locators = {
@Locator(host = "boombox" port = 11235),
@Locator(host = "skullbox", port = 12480)
})
class ClientApplication { .. }
除了locators
属性外,@ClientCacheApplication
注解还有一个servers
属性。servers
属性可用于指定一个或多个嵌套的@Server
注解,如果需要,允许缓存客户端直接连接到一个或多个服务器。
您可以使用locators 或servers 属性,但不能同时使用两者(这是由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缓存应用程序,您还可以创建Pivotal GemFire Locator应用程序。
Pivotal GemFire Locator是一个JVM进程,它允许节点作为对等成员加入Pivotal GemFire集群。Locators还使客户端能够发现集群中的服务器。Locator为客户端提供元数据,以均匀平衡集群成员之间的负载,实现单跳数据访问操作,以及其他功能。
关于Locators的完整讨论超出了本文档的范围。鼓励读者阅读Pivotal GemFire的用户指南,以获取关于Locators及其在集群中作用的更多详细信息。
要配置和引导一个独立的Locator进程,请执行以下操作:
@SpringBootApplication
@LocatorApplication(port = 12345)
class LocatorApplication { ... }
您可以在集群中启动多个Locators。唯一的要求是成员名称在集群中必须是唯一的。使用@LocatorApplication
注解的name
属性 accordingly 命名集群中的Locator成员。另外,您可以在Spring Boot的application.properties
中设置spring.data.gemfire.locator.name
属性。
此外,如果您在同一台机器上分叉多个Locators,必须确保每个Locator都在唯一的端口上启动。设置port
注解属性或spring.data.gemfire.locator.port
属性。
然后,您可以在由Locator或Locators加入的集群中启动一个或多个Pivotal GemFire对等缓存成员,这些成员也用Spring配置和引导,如下所示:
CacheServer
应用程序@SpringBootApplication
@CacheServerApplication(locators = "localhost[12345]")
class ServerApplication { ... }
同样,您可以启动任意数量的ServerApplication
类,它们都加入到上面的Locator中。只需确保成员具有唯一的名称。
@LocatorApplication
用于配置和引导独立的Pivotal GemFire Locator应用程序进程。该进程只能是Locator,不能是其他。如果您尝试使用缓存实例启动Locator,SDG将抛出错误。
如果您想同时启动一个缓存实例以及一个嵌入式Locator,则应该改用@EnableLocator
注解。
在开发过程中启动嵌入式Locator很方便。然而,强烈建议您在生产环境中运行独立的Locator进程以实现高可用性。如果集群中的所有Locators都宕机,那么集群将保持完整,但是,将无法有新成员加入集群,这对于线性扩展以满足需求非常重要。
有关更多详细信息,请参阅关于配置嵌入式Locator的部分。
6.5. 使用 Configurers
进行运行时配置
设计基于注解的配置模型的另一个目标是保留注解属性中的类型安全。例如,如果配置属性可以表示为int
(如端口号),那么该属性的类型就应该是int
。
不幸的是,这不利于在运行时进行动态和可解析的配置。
Spring的一个更好的特性是能够在配置Spring容器中的Bean时,在配置元数据的属性或属性中使用属性占位符和SpEL表达式。然而,这将要求所有注解属性都必须是String
类型,从而牺牲了类型安全,这是不可取的。
因此,Spring Data for Pivotal GemFire借鉴了Spring中另一种常用的模式:Configurers
。Spring Web MVC中提供了许多不同的Configurer
接口,包括org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer
。
Configurers
设计模式使应用程序开发人员能够在启动时接收回调,以自定义组件或Bean的配置。框架会回调用户提供的代码,在运行时调整配置。此模式的一个更常见用途是根据应用程序的运行时环境提供条件配置。
Spring Data for Pivotal GemFire提供了几个Configurer
回调接口,用于在运行时、在注解创建的Spring管理的Bean初始化之前,自定义基于注解的配置元数据的不同方面:
-
CacheServerConfigurer
-
ClientCacheConfigurer
-
ContinuousQueryListenerContainerConfigurer
-
DiskStoreConfigurer
-
IndexConfigurer
-
PeerCacheConfigurer
-
PoolConfigurer
-
RegionConfigurer
-
GatewayReceiverConfigurer
-
GatewaySenderConfigurer
例如,您可以使用CacheServerConfigurer
和ClientCacheConfigurer
分别自定义您的Spring Boot CacheServer
和ClientCache
应用程序使用的端口号。
考虑以下来自服务器应用程序的示例:
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组件(例如,ClientCache
实例使用ClientCacheFactoryBean
创建和配置)的FactoryBean
的引用。
SDG FactoryBeans 是SDG公共API的一部分,如果您没有提供这种新的基于注解的配置模型,您将在Spring的基于Java的容器配置中使用它们。实际上,注解本身也使用了这些相同的FactoryBeans 进行配置。因此,本质上,注解是一个外观(facade),提供了一个额外的抽象层以方便使用。 |
考虑到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
类简化为以下内容:
@ClientCacheApplication
类@SpringBootApplication
@ClientCacheApplication
@EnablePools(pools = {
@EnablePool(name = "Venus"),
@EnablePool(name = "Saturn"),
@EnablePool(name = "Neptune")
})
class ClientApplication { .. }
同样,@CacheServerApplication
类变为以下内容:
@CacheServerApplication
类@SpringBootApplication
@CacheServerApplication(name = "SpringServerApplication")
@EnableCacheServers(servers = {
@EnableCacheServer(name = "Venus"),
@EnableCacheServer(name = "Saturn"),
@EnableCacheServer(name = "Neptune")
})
class ServerApplication { .. }
前面的示例说明了为什么“命名”基于注解的Bean很重要(除了在某些情况下是必需的之外)。这样做可以实现在Spring容器中通过XML、属性和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
文件中设置嵌套属性:
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
类,如下所示:
CacheServer
应用程序@SpringBootApplication
@CacheServerApplication
@EnableLocator
class ServerApplication { .. }
@EnableLocator
注解会在运行在localhost
上、监听默认Locator端口10334
的Spring Pivotal GemFire CacheServer
应用程序中启动一个嵌入式Locator。您可以使用相应的注解属性自定义嵌入式Locator绑定的host
(绑定地址)和port
。
另外,您可以通过在application.properties
中设置相应的spring.data.gemfire.locator.host
和spring.data.gemfire.locator.port
属性来设置@EnableLocator
属性。
然后,您可以通过连接到此Locator来启动其他启用了Spring Boot @CacheServerApplication
的应用程序,如下所示:
CacheServer
应用程序@SpringBootApplication
@CacheServerApplication(locators = "localhost[10334]")
class ServerApplication { .. }
您甚至可以将前面显示的两个应用类合并到一个类中,并使用您的IDE创建不同的运行配置文件配置,以便通过使用Java系统属性以稍微修改的配置启动同一类的不同实例,如下所示:
CacheServer
应用程序@SpringBootApplication
@CacheServerApplication(locators = "localhost[10334]")
public class ServerApplication {
public static void main(String[] args) {
SpringApplication.run(ServerApplication.class);
}
@EnableLocator
@Profile("embedded-locator")
static class Configuration { }
}
然后,对于每个运行配置文件,您可以设置和更改以下系统属性:
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是集群中的另一个对等成员或节点,负责集群的“管理”。管理包括创建Regions
、Indexes
、DiskStores
等,以及监控集群组件的运行时操作和行为。
Manager允许启用JMX的客户端(如Gfsh shell工具)连接到Manager以管理集群。也可以使用JDK提供的工具(如JConsole或JVisualVM)连接到Manager,因为它们也是启用JMX的客户端。
也许您也想将前面显示的Spring @CacheServerApplication
启用为Manager。为此,请使用@EnableManager
注解您的Spring @Configuration
或@SpringBootApplication
类。
默认情况下,Manager绑定到localhost
,监听默认的Manager端口1099
。Manager的多个方面可以使用注解属性或相应的属性进行配置。
以下示例展示了如何在Java中创建嵌入式Manager:
CacheServer
应用程序@SpringBootApplication
@CacheServerApplication(locators = "localhost[10334]")
public class ServerApplication {
public static void main(String[] args) {
SpringApplication.run(ServerApplication.class);
}
@EnableLocator
@EnableManager
@Profile("embedded-locator-manager")
static class Configuration { }
}
通过前面的类,您甚至可以使用Gfsh连接到小型集群并进行管理,如下所示:
$ gfsh
_________________________ __
/ _____/ ______/ ______/ /____/ /
/ / __/ /___ /_____ / _____ /
/ /__/ / ____/ _____/ / / / /
/______/_/ /______/_/ /_/ 1.2.1
Monitor and Manage {data-store-name}
gfsh>connect
Connecting to Locator at [host=localhost, port=10334] ..
Connecting to Manager at [host=10.99.199.5, port=1099] ..
Successfully connected to: [host=10.99.199.5, port=1099]
gfsh>list members
Name | Id
---------------------- | ----------------------------------------------------
SpringCacheServerOne | 10.99.199.5(SpringCacheServerOne:14842)<ec><v0>:1024
SpringCacheServerTwo | 10.99.199.5(SpringCacheServerTwo:14844)<v1>:1025
SpringCacheServerThree | 10.99.199.5(SpringCacheServerThree:14846)<v2>:1026
由于我们还启用了嵌入式Locator,我们可以通过Locator间接连接到Manager。Locator允许JMX客户端连接并查找集群中的Manager。如果不存在Manager,Locator将承担Manager的角色。然而,如果不存在Locator,我们将需要使用以下命令直接连接到Manager:
connect
命令直接连接到Managergfsh>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 API以及Pulse监控Web应用程序。
然而,要使用这些Pivotal GemFire提供的Web应用程序中的任何一个,您必须在您的系统上安装Pivotal GemFire的完整版本,并且必须将GEODE_HOME
环境变量设置为您的安装目录。
要启用嵌入式HTTP服务器,请将@EnableHttpService
注解添加到任何带有@PeerCacheApplication
或@CacheServerApplication
注解的类上,如下所示:
CacheServer
应用程序@SpringBootApplication
@CacheServerApplication
@EnableHttpService
public class ServerApplication { .. }
默认情况下,嵌入式HTTP服务器监听端口7070
以处理HTTP客户端请求。当然,您可以使用注解属性或相应的配置属性根据需要调整端口。
请参考前面的链接以获取有关HTTP支持和提供的服务的更多详细信息。
6.7.4. 配置嵌入式 Memcached 服务器 (Gemcached)
Pivotal GemFire还实现了Memcached协议,能够服务Memcached客户端。也就是说,Memcached客户端可以连接到Pivotal GemFire集群,并执行Memcached操作,就好像集群中的Pivotal GemFire服务器是实际的Memcached服务器一样。
要启用嵌入式Memcached服务,请将@EnableMemcachedServer
注解添加到任何带有@PeerCacheApplication
或@CacheServerApplication
注解的类上,如下所示:
CacheServer
应用程序@SpringBootApplication
@CacheServerApplication
@EnabledMemcachedServer
public class ServerApplication { .. }
有关Pivotal GemFire的Memcached服务(称为“Gemcached”)的更多详细信息,请参见此处。
6.7.5. 配置嵌入式 Redis 服务器
Pivotal GemFire还实现了Redis服务器协议,这使得Redis客户端能够连接到Pivotal GemFire服务器集群并与其通信,以发出Redis命令。截至本文撰写之时,Pivotal GemFire中的Redis服务器协议支持仍处于实验阶段。
要启用嵌入式Redis服务,请将@EnableRedisServer
注解添加到任何带有@PeerCacheApplication
或@CacheServerApplication
注解的类上,如下所示:
CacheServer
应用程序@SpringBootApplication
@CacheServerApplication
@EnableRedisServer
public class ServerApplication { .. }
您必须在Spring [Boot] 应用程序的类路径上明确声明org.apache.geode:geode-redis 模块。 |
有关Pivotal GemFire的Redis适配器的更多详细信息,请参见此处。
6.8. 配置日志记录
通常,需要提高日志记录级别才能准确了解Pivotal GemFire正在做什么以及何时进行。
要启用日志记录,请使用@EnableLogging
注解您的应用类,并设置适当的属性或相关属性,如下所示:
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
注解您的应用类,如下所示:
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
注解你的应用程序类,如下所示
ClientCache
应用程序@SpringBootApplication
@ClientCacheApplication
@EnablePdx
public class ClientApplication { .. }
通常,应用程序的域对象类型要么实现 org.apache.geode.pdx.PdxSerializable
接口,要么你可以实现并注册 org.apache.geode.pdx.PdxSerializer
接口的非侵入式实现来处理所有需要序列化的应用程序域对象类型。
遗憾的是,Pivotal GemFire 只允许注册一个 PdxSerializer
,这意味着所有应用程序域对象类型都需要由一个 PdxSerializer
实例处理。然而,这是一个严重的反模式,也是一种难以维护的做法。
尽管 Pivotal GemFire 只能注册一个 PdxSerializer
实例,但为每个应用程序域对象类型创建一个单独的 PdxSerializer
实现是合理的。
通过使用 Composite Software Design Pattern,你可以提供 PdxSerializer
接口的实现,该实现聚合所有特定于应用程序域对象类型的 PdxSerializer
实例,但充当单个 PdxSerializer
实例并进行注册。
你可以在 Spring 容器中将此组合 PdxSerializer
声明为一个托管 bean,并在 @EnablePdx
注解中使用 serializerBeanName
属性按其 bean 名称引用此组合 PdxSerializer
。Spring Data for Pivotal GemFire 会代表你负责将其注册到 Pivotal GemFire。
以下示例展示如何创建自定义组合 PdxSerializer
PdxSerializer
的 Spring ClientCache
应用程序@SpringBootApplication
@ClientCacheApplication
@EnablePdx(serializerBeanName = "compositePdxSerializer")
public class ClientApplication {
@Bean
PdxSerializer compositePdxSerializer() {
return new CompositePdxSerializerBuilder()...
}
}
还可以将 Pivotal GemFire 的 org.apache.geode.pdx.ReflectionBasedAutoSerializer
声明为 Spring 上下文中的 bean 定义。
或者,你应该使用 Spring Data for Pivotal GemFire 更健壮的 org.springframework.data.gemfire.mapping.MappingPdxSerializer
,它使用 Spring Data 映射元数据和基础设施应用于序列化过程,比单独使用反射更高效。
PDX 的许多其他方面和特性可以通过 @EnablePdx
注解属性或相关的配置属性进行调整。
有关更多详细信息,请参阅 @EnablePdx
注解 Javadoc。
6.11. 配置 Pivotal GemFire 属性
虽然许多 gemfire.properties 属性在 SDG 基于注解的配置模型中方便地被封装和抽象化,但不太常用的 Pivotal GemFire 属性仍然可以通过 @EnableGemFireProperties
注解访问。
使用 @EnableGemFireProperties
注解你的应用程序类既方便,也是创建 gemfire.properties
文件或在启动应用程序时在命令行上将 Pivotal GemFire 属性设置为 Java 系统属性的良好替代方案。
我们建议在将应用程序部署到生产环境时,将这些 Pivotal GemFire 属性设置在 gemfire.properties 文件中。然而,在开发阶段,为了原型设计、调试和测试目的,根据需要单独设置这些属性可能会很方便。 |
一些你通常无需担心的不太常见的 Pivotal GemFire 属性示例包括但不限于:ack-wait-threshold
、disable-tcp
、socket-buffer-size
等等。
要单独设置任何 Pivotal GemFire 属性,请使用 @EnableGemFireProperties
注解你的应用程序类,并使用相应的属性设置你想更改的 Pivotal GemFire 属性(从 Pivotal GemFire 设置的默认值),如下所示
ClientCache
应用程序@SpringBootApplication
@ClientCacheApplication
@EnableGemFireProperties(conflateEvents = true, socketBufferSize = 16384)
public class ClientApplication { .. }
请记住,某些 Pivotal GemFire 属性是客户端特定的(例如,conflateEvents
),而另一些是服务器特定的(例如 distributedSystemId
、enableNetworkPartitionDetection
、enforceUniqueHost
、memberTimeout
、redundancyZone
等等)。
有关 Pivotal GemFire 属性的更多详细信息,请参见此处。
6.12. 配置区域 (Regions)
到目前为止,除了 PDX 之外,我们的讨论主要围绕配置 Pivotal GemFire 的管理功能展开:创建缓存实例、启动嵌入式服务、启用日志记录和统计信息、配置 PDX 以及使用 gemfire.properties
影响底层配置和行为。虽然所有这些配置选项都很重要,但它们都不直接与你的应用程序相关。换句话说,我们仍然需要一个地方来存储应用程序数据并使其普遍可用和可访问。
Pivotal GemFire 将缓存中的数据组织到区域 (Regions) 中。你可以将区域视为关系数据库中的一个表。通常,一个区域应该只存储一种类型的对象,这更有利于构建有效的索引和编写查询。我们稍后会介绍索引。
之前,Spring Data for Pivotal GemFire 用户需要通过编写非常详细的 Spring 配置元数据来显式定义和声明其应用程序用于存储数据的区域 (Regions),无论是通过 API 使用 SDG 的 FactoryBeans
结合 Spring 的基于 Java 的容器配置,还是使用XML。
以下示例演示如何用 Java 配置一个区域 bean
@Configuration
class GemFireConfiguration {
@Bean("Example")
PartitionedRegionFactoryBean exampleRegion(GemFireCache gemfireCache) {
PartitionedRegionFactoryBean<Long, Example> exampleRegion =
new PartitionedRegionFactoryBean<>();
exampleRegion.setCache(gemfireCache);
exampleRegion.setClose(false);
exampleRegion.setPersistent(true);
return exampleRegion;
}
...
}
以下示例演示如何用 XML 配置相同的区域 bean
<gfe:partitioned-region id="exampleRegion" name="Example" persistent="true">
...
</gfe:partitioned-region>
虽然 Java 或 XML 配置都不是很难指定,但任何一种都可能很麻烦,特别是如果应用程序需要大量区域时。许多基于关系数据库的应用程序可能有数百甚至数千个表。
手动定义和声明所有这些区域将既繁琐又容易出错。现在,有了更好的方法。
现在,你可以根据应用程序域对象(实体)本身定义和配置区域。除非你需要更精细的控制,否则不再需要显式地在 Spring 配置元数据中定义 Region
bean 定义。
为了简化区域创建,Spring Data for Pivotal GemFire 将 Spring Data Repositories 的使用与新的 @EnableEntityDefinedRegions
注解的基于注解的配置表达能力结合起来。
大多数 Spring Data 应用程序开发人员应该已经熟悉 Spring Data Repository 抽象以及 Spring Data for Pivotal GemFire 的实现/扩展,它经过专门定制,以优化 Pivotal GemFire 的数据访问操作。 |
首先,应用程序开发人员通过定义应用程序的域对象(实体)开始,如下所示
@Region("Books")
class Book {
@Id
private ISBN isbn;
private Author author;
private Category category;
private LocalDate releaseDate;
private Publisher publisher;
private String title;
}
接下来,通过扩展 Spring Data Commons org.springframework.data.repository.CrudRepository
接口来定义一个基本的 Books
仓库,如下所示
interface BookRepository extends CrudRepository<Book, ISBN> { .. }
org.springframe.data.repository.CrudRepository
是一个数据访问对象 (DAO),提供基本的数据访问操作 (CRUD) 并支持简单的查询(例如 findById(..)
)。你可以通过在仓库接口上声明查询方法来定义额外的、更复杂的查询(例如,List<BooK> findByAuthor(Author author);
)。
在底层,当 Spring 容器启动时,Spring Data for Pivotal GemFire 提供你的应用程序仓库接口的实现。只要你遵循约定,SDG 甚至会实现你定义的查询方法。
现在,当你定义 Book
类时,你还通过在实体类型上声明 Spring Data for Pivotal GemFire 映射注解 @Region
来指定 Book
实例映射(存储)到的区域 (Region)。当然,如果仓库接口(此处的 BookRepository
)的类型参数中引用的实体类型(此处的 Book
)没有用 @Region
注解,则名称将从实体类型的简单类名派生(此处的 Book
)。
Spring Data for Pivotal GemFire 使用映射上下文(其中包含应用程序中定义的所有实体的映射元数据)来确定运行时需要的所有区域。
要启用和使用此功能,请使用 @EnableEntityDefinedRegions
注解应用程序类,如下所示
@SpringBootApplication
@ClientCacheApplication
@EnableEntityDefinedRegions(basePackages = "example.app.domain")
@EnableGemfireRepositories(basePackages = "example.app.repo")
class ClientApplication { .. }
在使用 Spring Data Repositories 的应用程序中,从实体类创建区域非常有用。Spring Data for Pivotal GemFire 的 Repository 支持通过 @EnableGemfireRepositories 注解启用,如前例所示。 |
目前,扫描只会拾取显式使用 @Region 注解的实体类,并创建区域。如果实体类没有显式映射 @Region ,则不会创建区域。 |
默认情况下,@EnableEntityDefinedRegions
注解从声明 @EnableEntityDefinedRegions
注解的配置类所在的包开始递归扫描实体类。
然而,通常会通过设置 basePackages
属性来限制扫描范围,该属性包含应用程序实体类所在的包名。
或者,你可以使用类型更安全的 basePackageClasses
属性来指定扫描的包,方法是将该属性设置为包含实体类的包中的一个实体类型,或者使用专门创建的非实体占位符类来标识要扫描的包。
以下示例展示如何指定要扫描的实体类型
@SpringBootApplication
@ClientCacheApplication
@EnableGemfireRepositories
@EnableEntityDefinedRegions(basePackageClasses = {
example.app.books.domain.Book.class,
example.app.customers.domain.Customer.class
})
class ClientApplication { .. }
除了指定扫描的起点之外,与 Spring 的 @ComponentScan
注解类似,你可以指定 include
和 exclude
过滤器,它们具有与 org.springframework.context.annotation.ComponentScan.Filter
注解完全相同的语义。
有关更多详细信息,请参阅 @EnableEntityDefinedRegions
注解 Javadoc。
6.12.1. 配置特定类型的区域 (Regions)
Pivotal GemFire 支持许多不同类型的区域 (Regions)。每种类型对应于区域的 DataPolicy
,它精确地决定了区域中的数据将如何管理(即分发、复制等)。
其他配置设置(例如区域的 scope )也会影响数据如何管理。有关更多详细信息,请参阅 Pivotal GemFire 用户指南中的“存储和分发选项”。 |
当你使用通用 @Region
映射注解为应用程序域对象类型添加注解时,Spring Data for Pivotal GemFire 会决定创建哪种类型的区域。SDG 的默认策略在确定要创建的区域类型时会考虑缓存类型。
例如,如果你使用 @ClientCacheApplication
注解将应用程序声明为 ClientCache
,SDG 默认会创建一个客户端 PROXY
Region
。或者,如果你使用 @PeerCacheApplication
或 @CacheServerApplication
注解将应用程序声明为对等 Cache
,SDG 默认会创建一个服务器 PARTITION
Region
。
当然,必要时你总是可以覆盖默认设置。为了覆盖 Spring Data for Pivotal GemFire 应用的默认设置,引入了四种新的区域映射注解
-
@ClientRegion
-
@LocalRegion
-
@PartitionRegion
-
@ReplicateRegion
@ClientRegion
映射注解是客户端应用程序特有的。上面列出的所有其他区域映射注解只能在具有嵌入式对等 Cache
的服务器应用程序中使用。
客户端应用程序有时需要创建和使用仅本地区域 (Regions),例如为了聚合来自其他区域的数据,以便在本地分析数据并执行应用程序代表用户执行的某些功能。在这种情况下,数据不需要分发回服务器,除非其他应用程序需要访问结果。此区域甚至可能是临时的,使用后即丢弃,这可以通过对区域本身设置空闲超时 (TTI) 和存活时间 (TTL) 过期策略来实现。(有关过期策略的更多信息,请参见“配置过期”。)
区域级别的空闲超时 (TTI) 和存活时间 (TTL) 过期策略独立于条目级别的 TTI 和 TTL 过期策略,且与之不同。 |
在任何情况下,如果你想创建一个仅本地的客户端区域,其中数据不会分发回服务器上同名的对应区域,你可以声明 @ClientRegion
映射注解并将 shortcut
属性设置为 ClientRegionShortcut.LOCAL
,如下所示
ClientCache
应用程序@ClientRegion(shortcut = ClientRegionShortcut.LOCAL)
class ClientLocalEntityType { .. }
所有特定于区域类型的注解都提供了额外的属性,这些属性既是跨区域类型的通用属性,也是仅特定于该类型区域的属性。例如,PartitionRegion
注解中的 collocatedWith
和 redundantCopies
属性仅适用于服务器端的 PARTITION
区域。
有关 Pivotal GemFire 区域类型的更多详细信息,请参见此处。
6.12.2. 配置集群定义的区域 (Regions)
除了 @EnableEntityDefinedRegions
注解之外,Spring Data for Pivotal GemFire 还提供了反向注解 @EnableClusterDefinedRegions
。你可以选择不基于应用程序用例 (UC) 和需求(最常见和逻辑的方法)定义和驱动实体类来定义区域,而是可以从 ClientCache
应用程序将连接到的集群中已定义的区域来声明区域。
这允许你将配置集中化,使用服务器集群作为数据定义的主要来源,并确保集群的所有客户端应用程序具有一致的配置。这在云管理环境中快速扩展大量相同客户端应用程序实例以处理增加的负载时特别有用。
其思想是,不是由客户端应用程序驱动数据字典,而是用户使用 Pivotal GemFire 的 Gfsh CLI shell 工具定义区域。这有一个额外的好处,当向集群添加额外的对等节点时,它们也将拥有并共享相同的配置,因为它会被 Pivotal GemFire 的 Cluster Configuration Service 记住。
例如,用户可以在 Gfsh 中定义一个区域,如下所示
gfsh>create region --name=Books --type=PARTITION
Member | Status
--------- | --------------------------------------
ServerOne | Region "/Books" created on "ServerOne"
ServerTwo | Region "/Books" created on "ServerTwo"
gfsh>list regions
List of regions
---------------
Books
gfsh>describe region --name=/Books
..........................................................
Name : Books
Data Policy : partition
Hosting Members : ServerTwo
ServerOne
Non-Default Attributes Shared By Hosting Members
Type | Name | Value
------ | ----------- | ---------
Region | size | 0
| data-policy | PARTITION
借助 Pivotal GemFire 的 Cluster Configuration Service,添加到服务器集群中以处理增加负载(在后端)的任何附加对等成员也将具有相同的配置,例如
gfsh>list members
Name | Id
--------- | ----------------------------------------------
Locator | 10.0.0.121(Locator:68173:locator)<ec><v0>:1024
ServerOne | 10.0.0.121(ServerOne:68242)<v3>:1025
ServerTwo | 10.0.0.121(ServerTwo:68372)<v4>:1026
gfsh>start server --name=ServerThree --log-level=config --server-port=41414
Starting a Geode Server in /Users/you/geode/cluster/ServerThree...
...
Server in /Users/you/geode/cluster/ServerThree... on 10.0.0.121[41414] as ServerThree is currently online.
Process ID: 68467
Uptime: 3 seconds
Geode Version: 1.2.1
Java Version: 1.8.0_152
Log File: /Users/you/geode/cluster/ServerThree/ServerThree.log
JVM Arguments: -Dgemfire.default.locators=10.0.0.121[10334]
-Dgemfire.use-cluster-configuration=true
-Dgemfire.start-dev-rest-api=false
-Dgemfire.log-level=config
-XX:OnOutOfMemoryError=kill -KILL %p
-Dgemfire.launcher.registerSignalHandlers=true
-Djava.awt.headless=true
-Dsun.rmi.dgc.server.gcInterval=9223372036854775806
Class-Path: /Users/you/geode/cluster/apache-geode-1.2.1/lib/geode-core-1.2.1.jar
:/Users/you/geode/cluster/apache-geode-1.2.1/lib/geode-dependencies.jar
gfsh>list members
Name | Id
----------- | ----------------------------------------------
Locator | 10.0.0.121(Locator:68173:locator)<ec><v0>:1024
ServerOne | 10.0.0.121(ServerOne:68242)<v3>:1025
ServerTwo | 10.0.0.121(ServerTwo:68372)<v4>:1026
ServerThree | 10.0.0.121(ServerThree:68467)<v5>:1027
gfsh>describe member --name=ServerThree
Name : ServerThree
Id : 10.0.0.121(ServerThree:68467)<v5>:1027
Host : 10.0.0.121
Regions : Books
PID : 68467
Groups :
Used Heap : 37M
Max Heap : 3641M
Working Dir : /Users/you/geode/cluster/ServerThree
Log file : /Users/you/geode/cluster/ServerThree/ServerThree.log
Locators : 10.0.0.121[10334]
Cache Server Information
Server Bind :
Server Port : 41414
Running : true
Client Connections : 0
如你所见,“ServerThree”现在拥有“Books”区域。如果任何或所有服务器宕机,它们在重新启动时将拥有与“Books”区域相同的配置。
在客户端,可能会启动许多书店客户端应用程序实例来处理针对书店在线服务的书籍。 “Books”区域可能是实现书店应用程序服务所需的许多不同区域中的一个。SDG 不需要单独创建和配置每个区域,而是方便地允许从集群定义客户端应用程序区域,如下所示
@EnableClusterDefinedRegions
从集群定义客户端区域@ClientCacheApplication
@EnableClusterDefinedRegions
class BookStoreClientApplication {
public static void main(String[] args) {
....
}
...
}
@EnableClusterDefinedRegions 只能在客户端使用。 |
你可以使用 clientRegionShortcut 注解属性来控制在客户端创建的区域类型。默认情况下,会创建一个客户端 PROXY 区域。将 clientRegionShortcut 设置为 ClientRegionShortcut.CACHING_PROXY 以实现“近缓存 (near caching)”。此设置适用于所有从集群定义的区域创建的客户端区域。如果你想控制从集群定义的区域创建的客户端区域的单独设置(如数据策略),那么你可以实现一个 RegionConfigurer ,其中包含基于区域名称的自定义逻辑。 |
然后,在你的应用程序中使用“Books”区域就变得很简单了。你可以直接注入“Books”区域,如下所示
@org.springframework.stereotype.Repository
class BooksDataAccessObject {
@Resource(name = "Books")
private Region<ISBN, Book> books;
// implement CRUD and queries with the "Books" Region
}
或者,甚至可以基于映射到“Books”区域的应用程序域类型(实体)Book
来定义 Spring Data Repository,如下所示
interface BookRepository extends CrudRepository<Book, ISBN> {
...
}
然后,你可以将自定义的 BooksDataAccessObject
或 BookRepository
注入到应用程序服务组件中,以执行所需的任何业务功能。
6.12.3. 配置驱逐 (Eviction)
使用 Pivotal GemFire 管理数据是一项积极的任务。通常需要进行调优,并且你必须结合使用多种功能(例如,驱逐和过期)才能有效管理 Pivotal GemFire 中的内存数据。
鉴于 Pivotal GemFire 是一个内存数据网格 (IMDG),数据在内存中管理并分发到参与集群的其他节点,以最大限度地减少延迟、最大化吞吐量并确保数据高可用。由于并非所有应用程序的数据都能放入内存(即使跨越整个节点集群,更不用说单个节点),你可以通过向集群添加新节点来增加容量。这通常称为线性横向扩展(而不是纵向扩展,后者意味着增加更多内存、更多 CPU、更多磁盘或更多网络带宽——基本上是为了处理负载而增加所有系统资源)。
尽管如此,即使是节点集群,通常也必须只将最重要的数据保存在内存中。内存耗尽,甚至接近满负荷,几乎从未是好事。Stop-the-world GC 或更糟糕的 OutOfMemoryErrors
将导致你的应用程序完全停止。
因此,为了帮助管理内存并保留最重要的数据,Pivotal GemFire 支持最近最少使用 (LRU) 驱逐。也就是说,Pivotal GemFire 使用最近最少使用算法,根据区域条目最后一次访问的时间来驱逐它们。
要启用驱逐,请使用 @EnableEviction
注解应用程序类,如下所示
@SpringBootApplication
@PeerCacheApplication
@EnableEviction(policies = {
@EvictionPolicy(regionNames = "Books", action = EvictionActionType.INVALIDATE),
@EvictionPolicy(regionNames = { "Customers", "Orders" }, maximum = 90,
action = EvictionActionType.OVERFLOW_TO_DISK,
type = EvictonPolicyType.HEAP_PERCENTAGE)
})
class ServerApplication { .. }
驱逐策略通常设置在服务器中的区域上。
如前所示,policies
属性可以指定一个或多个嵌套的 @EvictionPolicy
注解,每个注解都针对需要应用驱逐策略的一个或多个区域进行单独配置。
此外,你可以引用 Pivotal GemFire 的 org.apache.geode.cache.util.ObjectSizer
接口的自定义实现,该实现可以在 Spring 容器中定义为一个 bean,并使用 objectSizerName
属性按名称引用。
ObjectSizer
允许你定义用于评估和确定存储在区域中的对象大小的标准。
有关完整的驱逐配置选项列表,请参阅 @EnableEviction
注解 Javadoc。
有关 Pivotal GemFire 驱逐的更多详细信息,请参见此处。
6.12.4. 配置过期 (Expiration)
除了驱逐之外,过期也可以通过允许存储在区域中的条目过期来管理内存。Pivotal GemFire 支持存活时间 (TTL) 和空闲超时 (TTI) 条目过期策略。
Spring Data for Pivotal GemFire 基于注解的过期配置基于 Spring Data for Pivotal GemFire 1.5 版本中添加的早期和现有条目过期注解支持。
本质上,Spring Data for Pivotal GemFire 的过期注解支持基于 Pivotal GemFire 的 org.apache.geode.cache.CustomExpiry
接口的自定义实现。此 o.a.g.cache.CustomExpiry
实现检查存储在区域中的用户应用程序域对象是否存在类型级别的过期注解。
Spring Data for Pivotal GemFire 提供以下过期注解
-
Expiration
-
IdleTimeoutExpiration
-
TimeToLiveExpiration
应用程序域对象类型可以使用一个或多个过期注解进行注解,如下所示
@Region("Books")
@TimeToLiveExpiration(timeout = 30000, action = "INVALIDATE")
class Book { .. }
要启用过期,请使用 @EnableExpiration
注解应用程序类,如下所示
@SpringBootApplication
@PeerCacheApplication
@EnableExpiration
class ServerApplication { .. }
除了应用程序域对象类型级别的过期策略之外,你还可以使用 @EnableExpiration
注解,逐个区域地直接和单独配置过期策略,如下所示
@SpringBootApplication
@PeerCacheApplication
@EnableExpiration(policies = {
@ExpirationPolicy(regionNames = "Books", types = ExpirationType.TIME_TO_LIVE),
@ExpirationPolicy(regionNames = { "Customers", "Orders" }, timeout = 30000,
action = ExpirationActionType.LOCAL_DESTROY)
})
class ServerApplication { .. }
前面的示例为 Books
、Customers
和 Orders
区域设置了过期策略。
过期策略通常设置在服务器中的区域上。
有关完整的过期配置选项列表,请参阅 @EnableExpiration
注解 Javadoc。
有关 Pivotal GemFire 过期的更多详细信息,请参见此处。
6.12.5. 配置压缩 (Compression)
Pivotal GemFire 允许你使用可插拔的 Compressors
或不同的压缩编解码器来压缩内存中的区域值。Pivotal GemFire 默认使用 Google 的 Snappy 压缩库。
要启用压缩,请使用 @EnableCompression
注解应用程序类,如下所示
@SpringBootApplication
@ClientCacheApplication
@EnableCompression(compressorBeanName = "MyCompressor", regionNames = { "Customers", "Orders" })
class ClientApplication { .. }
compressorBeanName 和 regionNames 属性都不是必需的。 |
compressorBeanName
默认为 SnappyCompressor
,启用 Pivotal GemFire 的 SnappyCompressor
。
regionNames
属性是一个区域名称数组,用于指定启用压缩的区域。默认情况下,如果未明确设置 regionNames
属性,所有区域都会压缩值。
或者,你可以在 application.properties 文件中使用 spring.data.gemfire.cache.compression.compressor-bean-name 和 spring.data.gemfire.cache.compression.region-names 属性来设置和配置这些 @EnableCompression 注解属性的值。 |
要使用 Pivotal GemFire 的区域压缩功能,你必须在应用程序的 pom.xml 文件(对于 Maven)或 build.gradle 文件(对于 Gradle)中包含 org.iq80.snappy:snappy 依赖项。这仅在你使用 Pivotal GemFire 对区域压缩的默认支持时才需要,该支持默认使用 SnappyCompressor 。当然,如果你使用其他压缩库,则需要在应用程序的 classpath 中包含该压缩库的依赖项。此外,你需要实现 Pivotal GemFire 的 Compressor 接口以适应你选择的压缩库,在 Spring 容器中将其定义为一个 bean,并将 compressorBeanName 设置为此自定义 bean 定义。 |
有关更多详细信息,请参阅 @EnableCompression
注解 Javadoc。
有关 Pivotal GemFire 压缩的更多详细信息,请参见此处。
6.12.6. 配置堆外内存 (Off-Heap Memory)
另一种有效减少 JVM 堆内存压力并最小化 GC 活动的方法是使用 Pivotal GemFire 的堆外内存支持。
区域条目不是存储在 JVM 堆上,而是存储在系统的主内存中。如 Pivotal GemFire 用户指南中所述,堆外内存通常在存储的对象大小均匀、大多小于 128K 且无需频繁反序列化时效果最佳。
要启用堆外内存,请使用 @EnableOffHeap
注解应用程序类,如下所示
@SpringBootApplication
@PeerCacheApplication
@EnableOffHeap(memorySize = 8192m regionNames = { "Customers", "Orders" })
class ServerApplication { .. }
memorySize
属性是必需的。memorySize
属性的值指定了区域可以使用的主内存量,单位可以是兆字节 (m
) 或千兆字节 (g
)。
regionNames
属性是一个区域名称数组,用于指定在主内存中存储条目的区域。默认情况下,如果未明确设置 regionNames
属性,所有区域都使用主内存。
或者,你可以在 application.properties 文件中使用 spring.data.gemfire.cache.off-heap.memory-size 和 spring.data.gemfire.cache.off-heap.region-names 属性来设置和配置这些 @EnableOffHeap 注解属性的值。 |
有关更多详细信息,请参阅 @EnableOffHeap
注解 Javadoc。
6.12.7. 配置磁盘存储 (Disk Stores)
或者,你可以配置区域将数据持久化到磁盘。你还可以配置区域在区域条目被驱逐时将数据溢出到磁盘。在这两种情况下,都需要一个 DiskStore
来持久化和/或溢出数据。如果未为具有持久化或溢出功能的区域配置显式 DiskStore
,Pivotal GemFire 将使用 DEFAULT
DiskStore
。
我们建议在将数据持久化和/或溢出到磁盘时,定义区域特定的 DiskStores
。
Spring Data for Pivotal GemFire 提供注解支持,通过使用 @EnableDiskStore
和 @EnableDiskStores
注解应用程序类来定义和创建应用程序区域 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
都具有许多属性以及相关的配置属性,用于在运行时定制创建的 DiskStore
。
此外,@EnableDiskStores
注解定义了一些适用于所有通过 @EnableDiskStore
注解(与 @EnableDiskStores
注解本身组合使用)创建的 DiskStore
的通用 DiskStore
属性。单个 DiskStore
配置会覆盖特定的全局设置,但 @EnableDiskStores
注解方便地定义了适用于该注解聚合的所有 DiskStore
的通用配置属性。
Spring Data for Pivotal GemFire 还提供了 DiskStoreConfigurer
回调接口,可以在 Java 配置中声明并用于在运行时定制 DiskStore
,而不是使用配置属性,如下例所示:
@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)
操作(特别是在预先知道 key 的情况下),数据通常通过对包含数据的 Region 执行查询来检索。使用 Pivotal GemFire,查询使用对象查询语言 (OQL) 编写,客户端希望访问的特定数据集在查询的谓词中表达(例如,SELECT * FROM /Books b WHERE b.author.name = 'Jon Doe'
)。
通常,没有索引的查询效率低下。在没有索引的情况下执行查询时,Pivotal GemFire 会执行相当于全表扫描的操作。
Spring Data for Pivotal GemFire 可以轻松地在存储和访问数据的 Region 上创建索引。您无需像以前那样使用 Spring 配置明确声明 Index
bean 定义,而是可以在 Java 中创建 Index
bean 定义,如下所示:
@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 定义,如下所示:
<gfe:index id="BooksIsbnIndex" expression="isbn" from="/Books" type="KEY"/>
然而,现在您可以直接在应用程序域对象类型的字段上定义索引,因为您知道这些字段将用于查询谓词中,以加快这些查询的速度。您甚至可以对应用程序存储库接口上用户定义的查询方法生成的 OQL 查询应用索引。
重新使用前面例子中的 Book
实体类,我们可以使用 @Indexed
注解标记 Book
中那些我们知道在 BookRepository
接口中用查询方法定义的查询中使用的字段,如下所示:
@Region("Books")
class Book {
@Id
private ISBN isbn;
@Indexed
private Author author;
private Category category;
private LocalDate releaseDate;
private Publisher publisher;
@LuceneIndexed
private String title;
}
在我们的新 Book
类定义中,我们使用 @Indexed
注解标记了 author
字段,并使用 @LuceneIndexed
注解标记了 title
字段。此外,isbn
字段之前已经使用 Spring Data 的 @Id
注解标记,该注解标识包含 Book
实例唯一标识符的字段,在 Spring Data for Pivotal GemFire 中,@Id
注解的字段或属性被用作 Region 中存储条目时的键。
-
@Id
注解的字段或属性会导致创建 Pivotal GemFireKEY
索引。 -
@Indexed
注解的字段或属性会导致创建 Pivotal GemFireHASH
索引(默认)。 -
@LuceneIndexed
注解的字段或属性会导致创建 Pivotal GemFire Lucene 索引,用于通过 Pivotal GemFire 的 Lucene 集成和支持进行基于文本的搜索。
当使用 @Indexed
注解但不设置任何属性时,索引的 name
、expression
和 fromClause
会从添加了 @Indexed
注解的类的字段或属性派生。expression
正是字段或属性的名称。fromClause
从域对象类上的 @Region
注解派生,如果未指定 @Region
注解,则使用域对象类的简单名称。
当然,您可以明确设置任何 @Indexed
注解属性,以覆盖 Spring Data for Pivotal GemFire 提供的默认值。
@Region("Books")
class Book {
@Id
private ISBN isbn;
@Indexed(name = "BookAuthorNameIndex", expression = "author.name", type = "FUNCTIONAL")
private Author author;
private Category category;
private LocalDate releaseDate;
private Publisher publisher;
@LuceneIndexed(name = "BookTitleIndex", destory = true)
private String title;
}
索引的 name
,在未明确设置时会自动生成,也用作在 Spring 容器中注册的索引 bean 的名称。如果需要,甚至可以通过名称将此索引 bean 注入到另一个应用程序组件中。
生成的索引名称遵循此模式:<Region 名称><字段/属性名称><索引类型>Idx
。例如,author
索引的名称将是 BooksAuthorHashIdx
。
要启用索引,请使用 @EnableIndexing
注解应用程序类,如下所示:
@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 配置建立在连续查询监听器容器中现有连续查询支持的基础上。
例如,假设某图书出版商希望注册兴趣并在 Book
订单(需求)超过当前库存(供应)时收到通知。然后,出版商的打印应用程序可能会注册以下 CQ:
ClientCache
应用程序。@SpringBootApplication
@ClientCacheApplication(subcriptionEnabled = true)
@EnableContinuousQueries
class PublisherPrintApplication {
@ContinuousQuery(name = "DemandExceedsSupply", query =
"SELECT book.* FROM /Books book, /Inventory inventory
WHERE book.title = 'How to crush it in the Book business like Amazon"
AND inventory.isbn = book.isbn
AND inventory.available < (
SELECT sum(order.lineItems.quantity)
FROM /Orders order
WHERE order.status = 'pending'
AND order.lineItems.isbn = book.isbn
)
")
void handleSupplyProblem(CqEvent event) {
// start printing more books, fast!
}
}
要启用连续查询,请使用 @EnableContinuousQueries
注解应用程序类。
定义连续查询包括使用 @ContinuousQuery
注解任何 Spring @Component
注解的 POJO 类方法(类似于 SDG 的 Function 注解的 POJO 方法)。使用 @ContinuousQuery
注解定义的带有 CQ 的 POJO 方法将在数据匹配查询谓词时被调用。
此外,POJO 方法签名应遵循关于ContinuousQueryListener
和 ContinuousQueryListenerAdapter
一节中概述的要求。
有关可用属性和配置设置的更多详细信息,请参阅 @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) 应用程序可能具有在销售交易期间确定或查找 Book
的 Price
的功能,如下例所示:
@Service
class PointOfSaleService
@Cacheable("BookPrices")
Price runPriceCheckFor(Book book) {
...
}
@Transactional
Receipt checkout(Order order) {
...
}
...
}
为了在使用 Spring Data for Pivotal GemFire 和 Spring 缓存抽象时更轻松地工作,基于注解的配置模型添加了两个新功能。
考虑以下 Spring 缓存配置:
@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 的新功能,您可以将相同的缓存配置简化为以下内容:
@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 的应用程序进程本地的。如果应用程序是对等 Cache
,则 Region 只存在于应用程序节点上。如果应用程序是 ClientCache
,则 SDG 会创建客户端 PROXY
Region,并期望集群中的服务器上已经存在同名的 Region。
SDG 无法通过使用 Spring CacheResolver 在运行时解析操作中使用的缓存来确定服务方法所需的缓存。 |
SDG 还支持在应用程序服务组件上使用 JCache (JSR-107) 缓存注解。有关在 JCache 缓存注解的替代方案中使用等效 Spring 缓存注解的信息,请参阅核心 Spring Framework 参考指南。 |
有关在 Spring 缓存抽象中使用 Pivotal GemFire 作为缓存提供程序的更多详细信息,请参阅“支持 Spring 缓存抽象”部分。
有关 Spring 缓存抽象的更多详细信息,请参阅此处。
6.15. 配置集群配置推送
这可能是 Spring Data for Pivotal GemFire 中最令人兴奋的新功能。
当客户端应用程序类使用 @EnableClusterConfiguration
注解时,客户端应用程序在 Spring 容器中定义和声明为 bean 的任何 Region 或 Index 都将被“推送到”客户端连接的服务器集群。不仅如此,这种“推送”以一种方式执行,使得 Pivotal GemFire 在使用 HTTP 时会记住客户端推送的配置。如果集群中的所有节点都宕机,它们会以与之前相同的配置重新启动。如果向集群添加新服务器,它将获得相同的配置。
从某种意义上说,此功能与您使用 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 Repositories、Spring 事务管理等)专注于您的应用程序业务逻辑,而 Spring Data for Pivotal GemFire 会代表您处理这些框架功能所需的所有 Pivotal GemFire 细节。
将配置从客户端推送到集群中的服务器并让集群记住它,部分是通过使用 Pivotal GemFire 的集群配置服务实现的。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 依赖。 |
考虑以下配置所表达的力量:
ClientCache
应用程序@SpringBootApplication
@ClientCacheApplication
@EnableCachingDefinedRegions
@EnableEntityDefinedRegions
@EnableIndexing
@EnableGemfireCaching
@EnableGemfireRepositories
@EnableClusterConfiguration
class ClientApplication { .. }
您将立即获得一个 Spring Boot 应用程序,其中包含 Pivotal GemFire ClientCache
实例、Spring Data Repositories、使用 Pivotal GemFire 作为缓存提供程序的 Spring 缓存抽象(其中 Region 和 Index 不仅在客户端创建,还推送到集群中的服务器)。
从那里,您只需要执行以下操作:
-
定义带有映射和索引注解的应用程序域模型对象。
-
为每个实体类型定义 Repository 接口,以支持基本数据访问操作和简单查询。
-
定义包含处理实体的业务逻辑的服务组件。
-
在需要缓存、事务行为等的服务方法上声明适当的注解。
在这种情况下,没有任何内容与应用程序后端服务(如 Pivotal GemFire)所需的基础设施和底层机制相关。数据库用户也有类似的功能。现在 Spring 和 Pivotal GemFire 开发人员也拥有了。
与以下 Spring Data for Pivotal GemFire 注解结合使用时,此应用程序只需极少的努力即可真正起飞:
-
@EnableContinuousQueries
-
@EnableGemfireFunctionExecutions
-
@EnableGemfireCacheTransactions
有关更多详细信息,请参阅 @EnableClusterConfiguration
注解的 Javadoc。
6.16. 配置 SSL
与在网络上传输数据同样重要的是保护传输中的数据。当然,在 Java 中实现此目的的常用方法是使用 Secure Sockets Extension (SSE) 和 Transport Layer Security (TLS)。
要启用 SSL,请使用 @EnableSsl
注解应用程序类,如下所示:
ClientCache
应用程序@SpringBootApplication
@ClientCacheApplication
@EnableSsl
public class ClientApplication { .. }
然后您需要设置必要的 SSL 配置属性:keystores、用户名/密码等。
您可以单独配置不同的 Pivotal GemFire 组件(GATEWAY
、HTTP
、JMX
、LOCATOR
和 SERVER
)的 SSL,也可以通过使用 CLUSTER
枚举值集体配置它们使用 SSL。
您可以使用嵌套的 @EnableSsl
注解、components
属性和 Component
枚举中的枚举值指定 SSL 配置设置应应用于哪些 Pivotal GemFire 组件,如下所示:
ClientCache
应用程序@SpringBootApplication
@ClientCacheApplication
@EnableSsl(components = { GATEWAY, LOCATOR, SERVER })
public class ClientApplication { .. }
此外,您还可以使用相应的注解属性或关联的配置属性指定组件级别的 SSL 配置(ciphers
、protocols
和 keystore
/truststore
信息)。
有关更多详细信息,请参阅 @EnableSsl
注解的 Javadoc。
有关 Pivotal GemFire SSL 支持的更多详细信息,请参阅此处。
6.17. 配置安全性
应用程序安全性无疑极其重要,Spring Data for Pivotal GemFire 为保护 Pivotal GemFire 客户端和服务器提供了全面的支持。
最近,Pivotal GemFire 引入了一个新的集成安全框架(取代了其旧的身份验证和授权安全模型)用于处理身份验证和授权。这个新安全框架的主要特性和优势之一是它与 Apache Shiro 集成,因此可以将身份验证和授权请求委托给 Apache Shiro 来强制执行安全。
本节的其余部分演示了 Spring Data for Pivotal GemFire 如何进一步简化 Pivotal GemFire 的安全故事。
6.17.1. 配置服务器安全
您可以通过几种不同的方式配置 Pivotal GemFire 集群中服务器的安全性。
-
实现 Pivotal GemFire
org.apache.geode.security.SecurityManager
接口,并使用完全限定类名设置 Pivotal GemFire 的security-manager
属性以引用您的应用程序SecurityManager
实现。或者,用户可以构建并初始化其SecurityManager
实现的实例,并在创建 Pivotal GemFire 对等Cache
时使用 CacheFactory.setSecurityManager(:SecurityManager) 方法设置它。 -
创建 Apache Shiro
shiro.ini
文件,其中定义了您应用程序的用户、角色和权限,然后设置 Pivotal GemFiresecurity-shiro-init
属性以引用此shiro.ini
文件,该文件必须在CLASSPATH
中可用。 -
仅使用 Apache Shiro,使用 Spring Data for Pivotal GemFire 的新
@EnableSecurity
注解您的 Spring Boot 应用程序类,并在 Spring 容器中将一个或多个 Apache ShiroRealms
定义为 bean,用于访问您的应用程序安全元数据(即授权用户、角色和权限)。
第一种方法的问题在于您必须实现自己的 SecurityManager
,这可能相当繁琐且容易出错。实现自定义 SecurityManager
在访问存储元数据的数据源(例如 LDAP 甚至专有的内部数据源)中的安全元数据方面提供了一些灵活性。然而,通过配置和使用 Apache Shiro Realms
已经解决了这个问题,Apache Shiro Realms
更普遍为人所知且与 Pivotal GemFire 无关。
第二种方法,使用 Apache Shiro INI 文件,稍微好一些,但您仍然需要熟悉 INI 文件格式。此外,INI 文件是静态的,在运行时不容易更新。
第三种方法是最理想的,因为它遵循广为人知和行业接受的概念(即 Apache Shiro 的安全框架),并且易于设置,如下例所示:
@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 : |
-
支持 INI 格式的
Realm
。
您甚至可以创建 Apache Shiro Realm
的自定义实现。
有关更多详细信息,请参阅 Apache Shiro 关于 Realms 的文档。
当 Apache Shiro 在集群中服务器的 CLASSPATH
上,并且在一个或多个 Apache Shiro Realms
已被定义为 Spring 容器中的 bean 时,Spring Data for Pivotal GemFire 会检测到此配置,并在使用 @EnableSecurity
注解时使用 Apache Shiro 作为安全提供程序来保护您的 Pivotal GemFire 服务器。
您可以在这篇spring.io 博客文章中找到更多关于 Spring Data for Pivotal GemFire 支持使用 Apache Shiro 的 Pivotal GemFire 新集成安全框架的信息。 |
有关可用属性和关联配置属性的更多详细信息,请参阅 @EnableSecurity
注解的 Javadoc。
有关 Pivotal GemFire 安全的更多详细信息,请参阅此处。
6.17.2. 配置客户端安全
没有讨论如何保护基于 Spring 的 Pivotal GemFire 缓存客户端应用程序,安全故事就不完整。
坦率地说,Pivotal GemFire 保护客户端应用程序的过程相当复杂。简而言之,您需要:
-
提供
org.apache.geode.security.AuthInitialize
接口的实现。 -
设置 Pivotal GemFire
security-client-auth-init
(系统)属性以引用自定义的应用程序提供的AuthInitialize
接口。 -
在专有的 Pivotal GemFire
gfsecurity.properties
文件中指定用户凭据。
Spring Data for Pivotal GemFire 使用服务器应用程序中使用的相同 @EnableSecurity
注解简化了所有这些步骤。换句话说,相同的 @EnableSecurity
注解处理客户端和服务器应用程序的安全性。此功能使用户更容易决定将应用程序从嵌入式对等 Cache
应用程序切换到 ClientCache
应用程序等情况。只需将 SDG 注解从 @PeerCacheApplication
或 @CacheServerApplication
更改为 @ClientCacheApplication
即可。
实际上,您只需在客户端执行以下操作:
@EnableSecurity
的 Spring 客户端应用程序@SpringBootApplication
@ClientCacheApplication
@EnableSecurity
class ClientApplication { .. }
然后您可以定义包含所需用户名和密码的熟悉的 Spring Boot application.properties
文件,如下例所示,一切就绪:
application.properties
文件spring.data.gemfire.security.username=jackBlack
spring.data.gemfire.security.password=b@cK!nB1@cK
默认情况下,当 application.properties 文件放在应用程序 CLASSPATH 的根目录时,Spring Boot 可以找到它。当然,Spring 支持使用其资源抽象定位资源的多种方式。 |
有关可用属性和关联配置属性的更多详细信息,请参阅 @EnableSecurity
注解的 Javadoc。
有关 Pivotal GemFire 安全的更多详细信息,请参阅此处。
6.18. 配置提示
以下提示可以帮助您最大限度地利用新的基于注解的配置模型:
6.18.1. 配置组织
正如我们在“配置集群配置推送”一节中所见,当使用注解启用许多 Pivotal GemFire 或 Spring Data for Pivotal GemFire 功能时,我们开始在 Spring 的 @Configuration
或 @SpringBootApplication
类上堆叠大量注解。在这种情况下,对配置进行一些分块组织是合理的。
例如,考虑以下声明:
ClientCache
应用程序@SpringBootApplication
@ClientCacheApplication
@EnableContinuousQueries
@EnableCachingDefinedRegions
@EnableEntityDefinedRegions
@EnableIndexing
@EnableGemfireCacheTransactions
@EnableGemfireCaching
@EnableGemfireFunctionExecutions
@EnableGemfireRepositories
@EnableClusterConfiguration
class ClientApplication { .. }
我们可以按关注点分解此配置,如下所示:
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
:启用 SDGGemfireBeanFactoryLocator
功能,仅在使用外部配置元数据(例如cache.xml
)时有用。例如,如果您在cache.xml
中定义的 Region 上定义CacheLoader
,您仍然可以使用 Spring 配置中定义的关联数据库DataSource
bean 来自动注入此CacheLoader
。此注解利用了此 SDG 功能,如果您有大量旧配置元数据(例如cache.xml
文件),可能会有用。 -
@EnableGemFireAsLastResource
:在 全局 - JTA 事务管理 中与 Pivotal GemFire 一起讨论。 -
@EnableMcast
:启用 Pivotal GemFire 旧的对等发现机制,该机制使用基于 UDP 的多播网络。(已弃用。改用 Pivotal GemFire Locator。参见 “配置嵌入式 Locator”。 -
@EnableRegionDataAccessTracing
:对调试很有用。这个注解通过注册一个 AOP 切面来代理 Spring 容器中声明为 bean 的所有 Region,拦截 Region 操作并记录事件,从而启用对 Region 上执行的所有数据访问操作的跟踪。
6.19. 结论
正如我们在前几节中学到的,Spring Data for Pivotal GemFire 新的基于注解的配置模型提供了强大的功能。希望它能实现其目标,让您在使用 Spring 配合 Pivotal GemFire 时能快速入门和轻松。
请记住,当您使用新注解时,您仍然可以使用 Java 配置或 XML 配置。您甚至可以使用 Spring 的 @Import
和 @ImportResource
注解,作用于 Spring 的 @Configuration
或 @SpringBootApplication
类上,结合所有这三种方法。一旦您显式提供一个 bean 定义,而这个 bean 定义原本可以通过 Spring Data for Pivotal GemFire 使用其中一个注解来提供,那么基于注解的配置就会退让。
在某些情况下,您甚至可能需要回退到 Java 配置,例如在 例如,另一个需要 Java 或 XML 配置的情况是配置 Pivotal GemFire WAN 组件,目前这些组件没有任何注解配置支持。但是,定义和注册 WAN 组件只需要使用 |
注解并非旨在处理所有情况。注解旨在帮助您尽可能快速和轻松地启动和运行,特别是在开发期间。
希望您会喜欢这些新功能!
6.20. 基于注解的配置快速入门
以下各节概述了 SDG 注解,以便快速入门。
所有注解都提供了额外的配置属性以及关联的 属性,以方便在运行时自定义 Pivotal GemFire 的配置和行为。然而,一般来说,使用特定的 Pivotal GemFire 功能并不需要任何属性或关联的属性。只需声明注解即可启用该功能,然后就完成了。有关更多详细信息,请参阅每个注解的独立 Javadoc。 |
6.20.1. 配置一个 ClientCache
应用
要配置和引导一个 Pivotal GemFire ClientCache
应用,请使用以下方法:
@SpringBootApplication
@ClientCacheApplication
public class ClientApplication {
public static void main(String[] args) {
SpringApplication.run(ClientApplication.class, args);
}
}
参见 使用 Spring 配置 Pivotal GemFire 应用 以获取更多详细信息。
6.20.2. 配置一个对等 Cache
应用
要配置和引导一个 Pivotal GemFire 对等 Cache
应用,请使用以下方法:
@SpringBootApplication
@PeerCacheApplication
public class ServerApplication {
public static void main(String[] args) {
SpringApplication.run(ServerApplication.class, args);
}
}
如果您想启用一个允许 ClientCache 应用连接到此服务器的 CacheServer ,那么只需将 @PeerCacheApplication 注解替换为 @CacheServerApplication 注解。这将在“localhost”上启动一个 CacheServer ,监听默认的 CacheServer 端口 40404 。 |
参见 使用 Spring 配置 Pivotal GemFire 应用 以获取更多详细信息。
6.20.3. 配置嵌入式 Locator
为您的 Spring @PeerCacheApplication
或 @CacheServerApplication
类添加 @EnableLocator
注解,以启动一个绑定到所有 NIC 并监听默认 Locator 端口 10334
的嵌入式 Locator,如下所示:
@SpringBootApplication
@CacheServerApplication
@EnableLocator
public class ServerApplication {
public static void main(String[] args) {
SpringApplication.run(ServerApplication.class, args);
}
}
@EnableLocator 只能用于 Pivotal GemFire 服务器应用。 |
参见 配置嵌入式 Locator 以获取更多详细信息。
6.20.4. 配置嵌入式 Manager
为您的 Spring @PeerCacheApplication
或 @CacheServerApplication
类添加 @EnableManager
注解,以启动一个绑定到所有 NIC 并监听默认 Manager 端口 1099
的嵌入式 Manager,如下所示:
@SpringBootApplication
@CacheServerApplication
@EnableManager
public class ServerApplication {
public static void main(String[] args) {
SpringApplication.run(ServerApplication.class, args);
}
}
@EnableManager 只能用于 Pivotal GemFire 服务器应用。 |
参见 配置嵌入式 Manager 以获取更多详细信息。
6.20.5. 配置嵌入式 HTTP 服务器
为您的 Spring @PeerCacheApplication
或 @CacheServerApplication
类添加 @EnableHttpService
注解,以启动监听端口 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 服务器
为您的 Spring @PeerCacheApplication
或 @CacheServerApplication
类添加 @EnableMemcachedServer
注解,以启动监听端口 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 服务器
为您的 Spring @PeerCacheApplication
或 @CacheServerApplication
类添加 @EnableRedisServer
注解,以启动监听端口 6379
的嵌入式 Redis 服务器,如下所示:
@SpringBootApplication
@CacheServerApplication
@EnableRedisServer
public class ServerApplication {
public static void main(String[] args) {
SpringApplication.run(ServerApplication.class, args);
}
}
@EnableRedisServer 只能用于 Pivotal GemFire 服务器应用。 |
您必须在Spring [Boot] 应用程序的类路径上明确声明org.apache.geode:geode-redis 模块。 |
参见 配置嵌入式 Redis 服务器 以获取更多详细信息。
6.20.8. 配置日志
要配置或调整 Pivotal GemFire 日志记录,请为您的 Spring、Pivotal GemFire 客户端或服务器应用类添加 @EnableLogging
注解,如下所示:
@SpringBootApplication
@ClientCacheApplication
@EnableLogging(logLevel="trace")
public class ClientApplication {
public static void main(String[] args) {
SpringApplication.run(ClientApplication.class, args);
}
}
默认的 log-level 是 “config”。此外,这个注解不会调整您应用中的日志级别,只调整 Pivotal GemFire 的。 |
参见 配置日志 以获取更多详细信息。
6.20.9. 配置统计信息
要在运行时收集 Pivotal GemFire 统计信息,请为您的 Spring、Pivotal GemFire 客户端或服务器应用类添加 @EnableStatistics
注解,如下所示:
@SpringBootApplication
@ClientCacheApplication
@EnableStatistics
public class ClientApplication {
public static void main(String[] args) {
SpringApplication.run(ClientApplication.class, args);
}
}
参见 配置统计信息 以获取更多详细信息。
6.20.10. 配置 PDX
要启用 Pivotal GemFire PDX 序列化,请为您的 Spring、Pivotal GemFire 客户端或服务器应用类添加 @EnablePdx
注解,如下所示:
@SpringBootApplication
@ClientCacheApplication
@EnablePdx
public class ClientApplication {
public static void main(String[] args) {
SpringApplication.run(ClientApplication.class, args);
}
}
Pivotal GemFire PDX 序列化是 Java 序列化的一种替代方案,具有许多额外的好处。例如,它可以轻松地使所有应用域模型类型可序列化,而无需实现 java.io.Serializable 。 |
默认情况下,SDG 配置 MappingPdxSerializer 来序列化您的应用域模型类型,这无需任何开箱即用的特殊配置即可正确识别需要序列化的应用域对象并执行序列化,因为 MappingPdxSerializer 中的逻辑基于 Spring Data 的映射基础设施。参见 MappingPdxSerializer 以获取更多详细信息。 |
参见 配置 PDX 以获取更多详细信息。
6.20.11. 配置 SSL
要启用 Pivotal GemFire SSL,请为您的 Spring、Pivotal GemFire 客户端或服务器应用类添加 @EnableSsl
注解,如下所示:
@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 的特定组件,例如客户端/服务器、Locator、Gateway 等。(可选)您可以指定 Pivotal GemFire 的所有组件都使用 SSL,通过设置 “ALL”。 |
参见 配置 SSL 以获取更多详细信息。
6.20.12. 配置安全
要启用 Pivotal GemFire 安全,请为您的 Spring、Pivotal GemFire 客户端或服务器应用类添加 @EnableSecurity
注解,如下所示:
@SpringBootApplication
@ClientCacheApplication
@EnableSecurity
public class ClientApplication {
public static void main(String[] args) {
SpringApplication.run(ClientApplication.class, args);
}
}
在服务器端,您必须配置对认证凭据的访问。您可以实现 Pivotal GemFire 的 SecurityManager 接口,或者声明一个或多个 Apache Shiro Realms 。参见 配置服务器安全 以获取更多详细信息。 |
在客户端,您必须配置用户名和密码。参见 配置客户端安全 以获取更多详细信息。 |
参见 配置安全 以获取更多详细信息。
6.20.13. 配置 Pivotal GemFire 属性
要配置未被面向功能的 SDG 配置注解覆盖的其他低级 Pivotal GemFire 属性,请为您的 Spring、Pivotal GemFire 客户端或服务器应用类添加 @GemFireProperties
注解,如下所示:
@SpringBootApplication
@PeerCacheApplication
@EnableGemFireProperties(
cacheXmlFile = "/path/to/cache.xml",
conserveSockets = true,
groups = "GroupOne",
remoteLocators = "lunchbox[11235],mailbox[10101],skullbox[12480]"
)
public class ServerApplication {
public static void main(String[] args) {
SpringApplication.run(ServerApplication.class, args);
}
}
一些 Pivotal GemFire 属性仅适用于客户端,而另一些仅适用于服务器端。请查阅 Pivotal GemFire 文档 以了解每个属性的正确用法。 |
参见 配置 Pivotal GemFire 属性 以获取更多详细信息。
6.20.14. 配置缓存
要将 Pivotal GemFire 用作 Spring 缓存抽象 中的一个缓存提供者,并让 SDG 自动为您的应用服务组件所需的缓存创建 Pivotal GemFire Region,请为您的 Spring、Pivotal GemFire 客户端或服务器应用类添加 @EnableGemfireCaching
和 @EnableCachingDefinedRegions
注解,如下所示:
@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、索引、Repository 和实体
为了轻松创建 Spring、Pivotal GemFire 持久化客户端或服务器应用,请为您的应用类添加 @EnableEntityDefinedRegions
、@EnableGemfireRepositories
和 @EnableIndexing
注解,如下所示:
@SpringBootApplication
@ClientCacheApplication
@EnableEntityDefinedRegions(basePackageClasses = Book.class)
@EnableGemfireRepositories(basePackageClasses = BookRepository.class)
@EnableIndexing
public class ClientApplication {
public static void main(String[] args) {
SpringApplication.run(ClientApplication.class, args);
}
}
当使用 @EnableIndexing 注解时,@EnableEntityDefinedRegions 注解是必需的。参见 配置索引 以获取更多详细信息。 |
接下来,定义您的实体类并使用 @Region
映射注解来指定存储实体数据的 Region。使用 @Indexed
注解来为应用查询中使用的实体字段定义索引,如下所示:
package example.app.model;
import ...;
@Region("Books")
public class Book {
@Id
private ISBN isbn;
@Indexed;
private Author author;
@Indexed
private LocalDate published;
@LuceneIndexed
private String title;
}
@Region("Books") 实体类注解被 @EnableEntityDefinedRegions 用来确定应用所需的 Region。参见 配置特定类型的 Region 和 POJO 映射 以获取更多详细信息。 |
最后,定义您的 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。
参见 配置 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. 配置 Function
Pivotal GemFire Function 在分布式计算场景中非常有用,在这种场景下,需要数据的潜在昂贵计算可以在集群中的节点间并行执行。在这种情况下,将逻辑带到数据所在(存储)的位置比请求和获取数据进行计算更有效率。
使用 @EnableGemfireFunctions
以及 @GemfireFunction
注解,以启用在 POJO 方法上实现的 Pivotal GemFire Function 定义,如下所示:
@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. 配置持续查询
实时事件流处理对于数据密集型应用来说正变得越来越重要,主要目的是及时响应用户请求。Pivotal GemFire 持续查询 (CQ) 将帮助您相当轻松地完成这项相对复杂的任务。
通过为您的应用类添加 @EnableContinuousQueries
注解来启用 CQ,并定义您的 CQ 以及相关的事件处理程序,如下所示:
@ClientCacheApplication
@EnableContinuousQueries
class ClientApplication {
public static void main(String[] args) {
SpringApplication.run(ClientApplication.class, args);
}
}
然后,通过为相关的处理方法添加 @ContinousQuery
注解来定义您的 CQ,如下所示:
@Service
class CustomerService {
@ContinuousQuery(name = "CustomerQuery", query = "SELECT * FROM /Customers c WHERE ...")
public void process(CqEvent event) {
...
}
}
每当发生改变 Customer
数据的事件,使其匹配您的持续 OQL 查询 (CQ) 中的谓词时,process
方法将被调用。
Pivotal GemFire CQ 仅是客户端功能。 |
6.20.19. 配置集群配置
当使用 Pivotal GemFire 作为 Pivotal GemFire ClientCache
应用开发 Spring Data 应用时,在开发期间配置服务器以匹配客户端(在客户端/服务器拓扑中)非常有用。实际上,Pivotal GemFire 要求当您在客户端有一个名为 "/Example" 的 PROXY Region
时,服务器中也存在一个同名的 Region
(即 "Example")。
您可以使用 Gfsh 来创建应用所需的所有 Region 和索引,或者,您可以在运行基于 Pivotal GemFire 的 Spring Data 应用时,简单地推送开发过程中已经表达的配置元数据。
这就像为您的主应用类添加 @EnableClusterConfiguration(..)
这样的注解一样简单。
@EnableClusterConfiguration
@ClientCacheApplication
@EnableClusterConfiguration(useHttp = true)
class ClientApplication {
...
}
大多数情况下,在使用客户端/服务器拓扑时,特别是在生产环境中,集群的服务器会使用 Gfsh 启动。在这种情况下,通常使用 HTTP(S) 将配置元数据(例如 Region 和索引定义)发送到集群。当使用 HTTP 时,配置元数据被发送到集群中的 Manager,并在集群中的服务器节点间一致地分发。 |
为了使用 @EnableClusterConfiguration ,您必须在您的 Spring 应用 classpath 中声明 org.springframework:spring-web 依赖。 |
参见 配置集群配置推送 以获取更多详细信息。
6.20.20. 配置 GatewayReceivers
不同 Pivotal GemFire 集群之间的数据复制是一种越来越重要的容错和高可用性 (HA) 机制。Pivotal GemFire WAN 复制是一种机制,它允许一个 Pivotal GemFire 集群以可靠且容错的方式将其数据复制到另一个 Pivotal GemFire 集群。
Pivotal GemFire WAN 复制需要配置两个组件:
-
GatewayReceiver
- 接收来自远程 Pivotal GemFire 集群GatewaySender
的数据的 WAN 复制组件。 -
GatewaySender
- 将数据发送到远程 Pivotal GemFire 集群GatewayReceiver
的数据的 WAN 复制组件。
要启用 GatewayReceiver
,应用类需要添加 @EnableGatewayReceiver
注解,如下所示:
@CacheServerApplication
@EnableGatewayReceiver(manualStart = false, startPort = 10000, endPort = 11000, maximumTimeBetweenPings = 1000,
socketBufferSize = 16384, bindAddress = "localhost",transportFilters = {"transportBean1", "transportBean2"},
hostnameForSenders = "hostnameLocalhost"){
...
...
}
}
class MySpringApplication { .. }
Pivotal GemFire GatewayReceiver 仅是服务器端功能,只能在 CacheServer 或对等 Cache 节点上配置。 |
6.20.21. 配置 GatewaySenders
要启用 GatewaySender
,应用类需要添加 @EnableGatewaySenders
和 @EnableGatewaySender
注解,如下所示:
@CacheServerApplication
@EnableGatewaySenders(gatewaySenders = {
@EnableGatewaySender(name = "GatewaySender", manualStart = true,
remoteDistributedSystemId = 2, diskSynchronous = true, batchConflationEnabled = true,
parallel = true, persistent = false,diskStoreReference = "someDiskStore",
orderPolicy = OrderPolicyType.PARTITION, alertThreshold = 1234, batchSize = 100,
eventFilters = "SomeEventFilter", batchTimeInterval = 2000, dispatcherThreads = 22,
maximumQueueMemory = 400,socketBufferSize = 16384,
socketReadTimeout = 4000, regions = { "Region1"}),
@EnableGatewaySender(name = "GatewaySender2", manualStart = true,
remoteDistributedSystemId = 2, diskSynchronous = true, batchConflationEnabled = true,
parallel = true, persistent = false, diskStoreReference = "someDiskStore",
orderPolicy = OrderPolicyType.PARTITION, alertThreshold = 1234, batchSize = 100,
eventFilters = "SomeEventFilter", batchTimeInterval = 2000, dispatcherThreads = 22,
maximumQueueMemory = 400, socketBufferSize = 16384,socketReadTimeout = 4000,
regions = { "Region2" })
}){
class MySpringApplication { .. }
}
Pivotal GemFire GatewaySender 仅是服务器端功能,只能在 CacheServer 或对等 Cache 节点上配置。 |
在上述示例中,应用配置了两个 Region,Region1
和 Region2
。此外,将配置两个 GatewaySenders
来为这两个 Region 提供服务。GatewaySender1
将配置为复制 Region1’s data 和 `GatewaySender2
将配置为复制 `Region2’s data。
如示例所示,每个 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 查询语言的强大功能,开发人员可以使用 find
和 findUnique
方法。与 query
方法相比,这些方法可以在多个 Region 上执行查询、执行投影等。
find
方法应在查询选择多个项(通过 SelectResults
)时使用,而后者,findUnique
,顾名思义,在只返回一个对象时使用。
7.2. 异常转换
使用新的数据访问技术不仅需要适应新的 API,还需要处理该技术特有的异常。
为了处理异常情况,Spring Framework 提供了一个技术无关且一致的 异常层次结构,将应用从专有的、通常是“受检”的异常抽象为一组有针对性的运行时异常。
如 Spring Framework 文档中所述,异常转换 可以通过使用 @Repository
注解和 AOP,并定义一个 PersistenceExceptionTranslationPostProcessor
bean,透明地应用于您的数据访问对象 (DAO)。只要声明了 CacheFactoryBean
(例如,使用 <gfe:cache/>
或 <gfe:client-cache>
声明),当使用 Pivotal GemFire 时也会启用相同的异常转换功能,它充当异常转换器,并被 Spring 基础设施自动检测并相应使用。
7.3. 本地缓存事务管理
最受欢迎的 Spring Framework 功能之一是 事务管理。
如果您不熟悉 Spring 的事务抽象,那么我们强烈建议 阅读 关于 Spring 的事务管理 基础设施,因为它提供了一个一致的编程模型,可以透明地跨多个 API 工作,并且可以以编程方式或声明方式进行配置(后者是最流行的选择)。
对于 Pivotal GemFire,Spring Data for Pivotal GemFire 提供了一个专用的、基于缓存的 PlatformTransactionManager
,一旦声明,就允许 Region 操作通过 Spring 以原子方式执行。
<gfe:transaction-manager id="txManager" cache-ref="myCache"/>
如果 Pivotal GemFire 缓存定义在默认名称 gemfireCache 下,上述示例可以通过移除 cache-ref 属性进一步简化。与 Spring Data for Pivotal GemFire 的其他命名空间元素一样,如果未配置缓存 bean 名称,将使用上述命名约定。此外,如果未明确指定,事务管理器名称为“gemfireTransactionManager”。 |
目前,Pivotal GemFire 支持带有 读已提交 (read committed) 隔离级别的乐观事务。此外,为了保证这种隔离,开发人员应避免进行 原地 修改缓存中现有值的更改。为了防止这种情况发生,事务管理器默认配置缓存使用 读时复制 (copy on read) 语义,这意味着每次执行读取时都会创建实际值的副本。如果需要,可以通过 copyOnRead
属性禁用此行为。
由于启用 读时复制 后,会为给定键的值创建一个副本,您随后必须调用 Region.put(key, value)
以便事务性地更新值。
有关底层 Geode 事务管理器的语义和行为的更多信息,请参阅 Geode 的 CacheTransactionManager
Javadoc 以及 文档。
7.4. 全局 JTA 事务管理
Pivotal GemFire 也可以参与全局的、基于 JTA 的事务,例如由 Java EE 应用服务器(例如 WebSphere Application Server (WAS))使用容器管理事务 (CMT) 管理的事务,以及其他 JTA 资源。
然而,与许多其他符合 JTA 标准的资源(例如 ActiveMQ 等 JMS 消息代理)不同,Pivotal GemFire 不是 一个符合 XA 标准的资源。因此,Pivotal GemFire 在 JTA 事务中(准备阶段)必须被定位为“最后一个资源 (Last Resource)”,因为它不实现两阶段提交协议,或者更确切地说,它不处理分布式事务。
许多支持 CMT 的受管环境维护着对 JTA 事务中“最后一个资源 (Last Resource)”、不符合 XA 标准的资源的支持,尽管 JTA 规范并未实际要求。关于不符合 XA 标准的“最后一个资源”意味着什么,可以在 Red Hat 的 文档 中找到。实际上,Red Hat 的 JBoss 项目 Narayana 是一个这样的 LGPL 开源实现。Narayana 将此称为“最后一个资源提交优化 (Last Resource Commit Optimization)” (LRCO)。更多详细信息可以在 这里 找到。
然而,无论您是在支持“最后一个资源”的开源 JTA 事务管理实现的独立环境中使用 Pivotal GemFire,还是在受管环境(例如 WAS 等 Java EE AS)中使用,Spring Data for Pivotal GemFire 都能满足您的需求。
要在涉及多个事务性资源的 JTA 事务中正确地将 Pivotal GemFire 用作“最后一个资源”,您必须完成一系列步骤。此外,在这种安排中,只能有一个不符合 XA 标准的资源(例如 Pivotal GemFire)。
1) 首先,您必须完成 Pivotal GemFire 文档中 这里 的步骤 1-4。
上述第 1 点与您的 Spring [Boot] 和/或 [Spring Data for Pivotal GemFire] 应用无关,并且必须成功完成。 |
2) 参考 Pivotal GemFire 文档中步骤 5(此处),Spring Data for Pivotal GemFire 的注解支持将尝试在使用 @EnableGemFireAsLastResource
注解时,为您设置 GemFireCache
的 copyOnRead
属性。
然而,如果 SDG 的自动配置在这方面不成功,那么您必须显式地在 <gfe:cache/>
或 <gfe:client-cache>
XML 元素中设置 copy-on-read
属性,或者在 JavaConfig 中将 CacheFactoryBean
类的 copyOnRead
属性设置为 true。例如:
ClientCache
XML
<gfe:client-cache ... copy-on-read="true"/>
ClientCache
JavaConfig
@Bean
ClientCacheFactoryBean gemfireCache() {
ClientCacheFactoryBean gemfireCache = new ClientCacheFactoryBean();
gemfireCache.setCopyOnRead(true);
return gemfireCache;
}
对等 Cache
XML
<gfe:cache ... copy-on-read="true"/>
对等 Cache
JavaConfig
@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 @Configuration
类添加 Spring Data for Pivotal GemFire 的 新 @EnableGemFireAsLastResource
注解即可,Spring 的 事务管理 基础设施与 Spring Data for Pivotal GemFire 的 @EnableGemFireAsLastResource
注解配置相结合,就能完成任务。
配置如下所示...
@Configuration
@EnableGemFireAsLastResource
@EnableTransactionManagement(order = 1)
class GeodeConfiguration {
...
}
唯一的要求是...
3.1) `@EnableGemFireAsLastResource` 注解必须在声明 Spring 的 `@Configuration` 注解的同一个 Spring 配置类上声明,该配置类也同时指定了 Spring 的 `@EnableTransactionManagement` 注解。
3.2) `@EnableTransactionManagement` 注解的 `order` 属性必须显式设置为一个整数值,该值不能是 `Integer.MAX_VALUE` 或 `Integer.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()` 操作。
具体来说,正确的事件顺序如下
-
jtaTransation.begin()
-
GFConnectionFactory.getConnection()
-
调用应用的 `@Transactional` 服务方法
-
`jtaTransaction.commit()` 或 `jtaTransaction.rollback()`
-
最后,`GFConnection.close()`
这与作为应用开发者,如果您必须自己使用 JTA API + Pivotal GemFire API 手动编码的方式一致,如 Pivotal GemFire 示例所示。
值得庆幸的是,Spring 为您完成了繁重的工作,应用适当的配置(如上所示)后,您只需做到:
@Service
class MyTransactionalService {
@Transactional
public <Return-Type> someTransactionalServiceMethod() {
// perform business logic interacting with and accessing multiple JTA resources atomically
}
...
}
上面的 #1 和 #4 由 Spring 基于 JTA 的 `PlatformTransactionManager` 在应用的 `@Transactional` 边界进入时(即调用 `MyTransactionService.someTransactionalServiceMethod()` 时)为您妥善处理。
#2 和 #3 由通过 `@EnableGemFireAsLastResource` 注解启用的 Spring Data for Pivotal GemFire 的新 Aspect 处理。
#3 当然是您应用的职责。
确实,配置了适当的日志记录后,您将看到正确的事件顺序...
2017-Jun-22 11:11:37 TRACE TransactionInterceptor - Getting transaction for [example.app.service.MessageService.send]
2017-Jun-22 11:11:37 TRACE GemFireAsLastResourceConnectionAcquiringAspect - Acquiring {data-store-name} Connection
from {data-store-name} JCA ResourceAdapter registered at [gfe/jca]
2017-Jun-22 11:11:37 TRACE MessageService - PRODUCER [ Message :
[{ @type = example.app.domain.Message, id= MSG0000000000, message = SENT }],
JSON : [{"id":"MSG0000000000","message":"SENT"}] ]
2017-Jun-22 11:11:37 TRACE TransactionInterceptor - Completing transaction for [example.app.service.MessageService.send]
2017-Jun-22 11:11:37 TRACE GemFireAsLastResourceConnectionClosingAspect - Closed {data-store-name} Connection @ [Reference [...]]
有关使用 Pivotal GemFire 缓存级别事务的更多详细信息,请参阅 此处。
有关在 JTA 事务中使用 Pivotal GemFire 的更多详细信息,请参阅 此处。
有关将 Pivotal GemFire 配置为“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_COMMIT`、`AFTER_COMPLETION`、`AFTER_ROLLBACK` 和 `BEFORE_COMMIT`。如果未指定,`phase` 默认为 `AFTER_COMMIT`。如果您希望即使没有事务也调用监听器,可以将 `fallbackExecution` 设置为 `true`。
7.6. 连续查询 (CQ)
Pivotal GemFire 提供的一项强大功能是连续查询(或 CQ)。
简而言之,CQ 允许开发者创建并注册一个 OQL 查询,然后在添加到 Pivotal GemFire 的新数据匹配查询谓词时自动收到通知。Spring Data for Pivotal GemFire 通过 `org.springframework.data.gemfire.listener` 包及其**监听器容器**为 CQ 提供了专门的支持;其功能和命名与 *Spring Framework* 中的 JMS 集成非常相似;事实上,熟悉 Spring 中 JMS 支持的用户会感到得心应手。
基本上,Spring Data for Pivotal GemFire 允许 POJO 上的方法成为 CQ 的端点。只需定义查询并指定应调用的方法,以便在匹配时收到通知。Spring Data for Pivotal GemFire 会处理其余部分。这与 Java EE 的消息驱动 Bean 风格非常相似,但没有基于 Pivotal GemFire 的基类或接口实现的任何要求。
目前,连续查询仅在 Pivotal GemFire 的客户端/服务器拓扑中受支持。此外,使用的客户端池需要启用订阅。有关更多信息,请参阅 Pivotal GemFire 文档。 |
7.6.1. 连续查询监听器容器
Spring Data for Pivotal GemFire 通过使用 SDG 的 `ContinuousQueryListenerContainer` 来简化 CQ 事件的创建、注册、生命周期管理和分派,该容器为用户完成了所有繁重的工作。熟悉 EJB 和 JMS 的用户会发现这些概念很熟悉,因为它的设计尽可能接近 *Spring Framework* 为其消息驱动 POJO (MDP) 提供的支持。
SDG 的 `ContinuousQueryListenerContainer` 充当事件(或消息)监听器容器;它用于接收来自已注册 CQ 的事件并调用注入其中的 POJO。监听器容器负责消息接收和分派给监听器进行处理的所有线程管理。它充当 EDP(事件驱动 POJO)与事件提供者之间的中介,并负责 CQ 的创建和注册(用于接收事件)、资源获取和释放、异常转换等。这使得作为应用开发者的您,可以编写与接收事件(并对其作出反应)相关的(可能复杂的)业务逻辑,并将样板式的 Pivotal GemFire 基础设施关注点委托给框架。
监听器容器是完全可定制的。开发者可以选择使用 CQ 线程执行分派(同步交付)或使用(来自现有线程池的)新线程进行异步方法,通过定义合适的 `java.util.concurrent.Executor`(或 Spring 的 `TaskExecutor`)来实现。根据负载、监听器数量或运行时环境的不同,开发者应更改或调整执行器以更好地满足其需求。特别是在受管环境(如应用服务器)中,强烈建议选择合适的 `TaskExecutor` 以利用其运行时特性。
7.6.2. `ContinuousQueryListener` 和 `ContinuousQueryListenerAdapter`
`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` 实例将任意参数传递给声明的类。
在本节中,我们将描述如何在 `cache.xml` 中定义这些可插拔组件时使用 Spring 进行配置,同时将您的 Cache/Region 配置保留在 `cache.xml` 中。这使得您的可插拔组件可以专注于应用逻辑,而不是 `DataSources` 或其他协作对象的查找或创建。
然而,如果您正在启动一个新的“绿地”项目(从头开始的项目),建议您直接在 Spring 中配置 Cache、Region 以及其他可插拔的 Pivotal GemFire 组件。这避免了继承 `Declarable` 接口或本节介绍的基类。
有关此方法的更多信息,请参阅以下侧边栏。
作为使用 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` 并在初始化过程中执行注入。
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>
`CacheManager` bean 定义上的 `cache-ref` 属性不是必需的,如果使用的是默认的缓存 bean 名称(即 "gemfireCache"),即 ` |
当声明了 `GemfireCacheManager`(Singleton)bean 实例并启用了声明式缓存(无论是通过 XML 中的 `
这些缓存(即 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 如何简化和改进 Pivotal GemFire 在 Java 中的自定义序列化的各种方式。
8.1. 注入反序列化的实例
序列化的对象带有瞬时数据是很常见的。瞬时数据通常依赖于特定时间点其所在的系统或环境。例如,`DataSource` 是环境特定的。序列化此类信息是无用的,甚至可能很危险,因为它仅限于特定的虚拟机或机器。对于这种情况,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. 自动生成自定义 `Instantiator`
对于数据密集型应用,随着数据流入,每台机器上可能会创建大量实例。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>
上述定义会自动为两个类(`CustomTypeA` 和 `CustomTypeB`)生成两个 `Instantiator`,并将它们以用户 ID `1025` 和 `1026` 注册到 Pivotal GemFire。这两个 `Instantiator` 避免使用反射,并通过 Java 代码直接创建实例。
9. POJO 映射
本节涵盖
9.1. 对象映射基础
本节涵盖 Spring Data 对象映射、对象创建、字段和属性访问、可变性和不可变性的基础知识。请注意,本节仅适用于不使用底层数据存储(如 JPA)对象映射的 Spring Data 模块。同时务必查阅特定于存储的部分,以获取特定于存储的对象映射信息,例如索引、自定义列或字段名等。
Spring Data 对象映射的核心职责是创建领域对象实例并将存储的原生数据结构映射到这些对象上。这意味着我们需要两个基本步骤
-
通过使用暴露的构造函数之一创建实例。
-
填充实例以实例化所有暴露的属性。
9.1.1. 对象创建
Spring Data 会自动尝试检测用于实例化该类型对象的持久化实体构造函数。解析算法如下:
-
如果存在无参数构造函数,则会使用它。其他构造函数将被忽略。
-
如果存在单个带参数的构造函数,则会使用它。
-
如果存在多个带参数的构造函数,Spring Data 将使用标注有 `@PersistenceConstructor` 注解的那个。
值解析假定构造函数参数名称与实体的属性名称匹配,即解析将按照属性将被填充的方式进行,包括映射中的所有自定义(不同的数据存储列或字段名等)。这也要求类文件中包含参数名称信息或构造函数上存在 `@ConstructorProperties` 注解。
可以使用 Spring Framework 的 `@Value` 注解和特定于存储的 SpEL 表达式来自定义值解析。有关更多详细信息,请参阅关于特定于存储的映射的部分。
9.1.2. 属性填充
创建实体实例后,Spring Data 会填充该类的所有剩余持久化属性。除非已由实体的构造函数填充(即通过其构造函数参数列表消耗),否则将首先填充标识符属性,以允许解析循环对象引用。之后,所有尚未由构造函数填充的非瞬时属性都会设置到实体实例上。为此,我们使用以下算法:
-
如果属性是不可变的但暴露了 wither 方法(见下文),我们将使用 wither 方法创建一个带有新属性值的新实体实例。
-
如果定义了属性访问(即通过 getter 和 setter 方法访问),我们将调用 setter 方法。
-
默认情况下,我们直接设置字段值。
让我们看看以下实体
class Person {
private final @Id Long id; (1)
private final String firstname, lastname; (2)
private final LocalDate birthday;
private final int age; (3)
private String comment; (4)
private @AccessType(Type.PROPERTY) String remarks; (5)
static Person of(String firstname, String lastname, LocalDate birthday) { (6)
return new Person(null, firstname, lastname, birthday,
Period.between(birthday, LocalDate.now()).getYears());
}
Person(Long id, String firstname, String lastname, LocalDate birthday, int age) { (6)
this.id = id;
this.firstname = firstname;
this.lastname = lastname;
this.birthday = birthday;
this.age = age;
}
Person withId(Long id) { (1)
return new Person(id, this.firstname, this.lastname, this.birthday, this.age);
}
void setRemarks(String remarks) { (5)
this.remarks = remarks;
}
}
1 | 标识符属性是 final 的,但在构造函数中设置为 `null`。该类暴露了一个 `withId(…)` 方法,用于设置标识符,例如当实例插入到数据存储并生成了标识符时。原始的 `Person` 实例保持不变,因为创建了一个新实例。通常对其他由存储管理但为了持久化操作可能需要更改的属性应用相同的模式。 |
2 | `firstname` 和 `lastname` 属性是普通的不可变属性,可能通过 getter 方法暴露。 |
3 | `age` 属性是一个不可变但从 `birthday` 属性派生的属性。按照所示的设计,数据库值将优先于默认值,因为 Spring Data 使用的是唯一声明的构造函数。即使预期应该优先使用计算结果,重要的是这个构造函数也接受 `age` 作为参数(可能忽略它),否则属性填充步骤将尝试设置 age 字段,并因为它是不可变的且没有 wither 方法而失败。 |
4 | `comment` 属性是可变的,通过直接设置其字段来填充。 |
5 | `remarks` 属性是可变的,通过直接设置 `comment` 字段或调用其 setter 方法来填充 |
6 | 该类暴露了一个工厂方法和一个构造函数用于对象创建。这里的核心思想是使用工厂方法而不是额外的构造函数,以避免通过 `@PersistenceConstructor` 进行构造函数消歧。相反,属性的默认值处理在工厂方法内部进行。 |
9.1.3. 一般建议
-
尽量坚持使用不可变对象 — 不可变对象创建起来很简单,因为实例化对象只需调用其构造函数。此外,这避免了您的领域对象充斥着允许客户端代码操作对象状态的 setter 方法。如果您需要这些方法,最好将它们设为 package protected,以便只有有限数量的同包类型可以调用它们。仅通过构造函数实例化比属性填充快约 30%。
-
提供一个全参数构造函数 — 即使您不能或不想将实体建模为不可变值,提供一个接受实体所有属性作为参数(包括可变属性)的构造函数仍然很有价值,因为这允许对象映射跳过属性填充,从而获得最佳性能。
-
使用工厂方法代替重载构造函数以避免 `@PersistenceConstructor` — 由于最佳性能需要全参数构造函数,我们通常希望暴露更多针对特定应用用例的构造函数,省略诸如自动生成的标识符等内容。使用静态工厂方法来暴露全参数构造函数的这些变体是一种已建立的模式。
-
确保您遵守允许使用生成的 instantiator 和属性访问器类的限制 —
-
对于要生成的标识符,仍请结合 wither 方法使用 final 字段 —
-
使用 Lombok 避免样板代码 — 由于持久化操作通常需要一个接受所有参数的构造函数,其声明成为参数到字段赋值的乏味重复样板代码,最好通过使用 Lombok 的 `@AllArgsConstructor` 来避免。
9.1.4. Kotlin 支持
Spring Data 适配了 Kotlin 的特性,以允许对象创建和修改。支持实例化 Kotlin 类,所有类默认都是不可变的,并且需要显式的属性声明来定义可变属性。考虑以下 `data` 类 `Person`
Kotlin 对象创建
上面的类编译为一个带有显式构造函数的典型类。我们可以通过添加另一个构造函数并使用 `@PersistenceConstructor` 注解进行标注来定制此类型,以指示构造函数偏好
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 中,所有类默认都是不可变的,并且需要显式属性声明来定义可变属性。考虑以下data
类Person
data class Person(val id: String, val name: String)
此类实际上是不可变的。它允许创建新实例,因为 Kotlin 会生成一个copy(…)
方法,该方法通过复制现有对象的所有属性值并应用作为方法参数提供的属性值来创建新的对象实例。
9.2. 实体映射
Spring Data for Pivotal GemFire 提供对存储在 Region 中的实体进行映射的支持。映射元数据是通过在应用程序域类上使用注解来定义的,如下例所示
@Region("People")
public class Person {
@Id Long id;
String firstname;
String lastname;
@PersistenceConstructor
public Person(String firstname, String lastname) {
// …
}
…
}
@Region
注解可用于定制Person
类实例存储在哪个 Region 中。@Id
注解可用于注解应作为缓存 Region 键的属性,用于标识 Region 条目。@PersistenceConstructor
注解有助于区分多个潜在可用的构造函数,它接受参数并明确将带有注解的构造函数标记为用于构造实体的构造函数。在没有或只有一个构造函数的应用程序域类中,可以省略该注解。
除了在顶层 Region 中存储实体外,实体还可以存储在 Sub-Region 中,如下例所示
@Region("/Users/Admin")
public class Admin extends User {
…
}
@Region("/Users/Guest")
public class Guest extends User {
…
}
请务必使用 Pivotal GemFire Region 的完整路径,该路径使用 Spring Data for Pivotal GemFire XML 命名空间的<*-region>
元素的id
或name
属性定义。
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 的数据管理策略(例如,分区——也称为分片——与复制数据)。
将这些特定于类型的 Region 映射注解与 SDG 注解配置模型一起使用,可以避免您必须在配置中显式定义这些 Regions。
9.3. Repository 映射
除了在实体类上使用@Region
注解指定实体存储在哪个 Region 中之外,您还可以在实体的Repository
接口上指定@Region
注解。有关更多详细信息,请参阅Spring Data for Pivotal GemFire Repository。
然而,假设您想将Person
记录存储在多个 Pivotal GemFire Regions 中(例如,People
和Customers
)。那么您可以如下定义相应的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
注解明确标识,如下例所示
@Value
public class Person {
public Person(@Value("#root.thing") String firstName, @Value("bean") String lastName) {
…
}
}
以这种方式注解的实体类会将“thing”字段从PdxReader
中读取并作为构造函数参数firstname
的参数值传递。lastName
的值是一个名为“bean”的 Spring bean。
除了由EntityInstantiators
提供的自定义实例化逻辑和策略外,MappingPdxSerializer
还提供了远超 Pivotal GemFire 自身ReflectionBasedAutoSerializer
的功能。
虽然 Pivotal GemFire 的ReflectionBasedAutoSerializer
方便地使用 Java 反射来填充实体,并使用正则表达式来标识应由序列化器处理(序列化和反序列化)的类型,但与MappingPdxSerializer
不同,它无法执行以下操作
-
按实体字段或属性名称及类型注册自定义 PdxSerializer 对象。
-
方便地标识 ID 属性。
-
自动处理只读属性。
-
自动处理瞬态属性。
-
以 null 和类型安全的方式实现更健壮的类型过滤(例如,不局限于仅使用 regex 表达类型)。
现在我们将更详细地探讨 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
来处理用户的密码,如下所示
PdxSerializers
Map<?, PdxSerializer> customPdxSerializers = new HashMap<>();
customPdxSerializers.put(Password.class, new SaltedHashPasswordPdxSerializer());
mappingPdxSerializer.setCustomPdxSerializers(customPdxSerializers);
将应用程序定义的SaltedHashPasswordPdxSerializer
实例注册到Password
应用程序域模型类型后,MappingPdxSerializer
将咨询自定义PdxSerializer
来序列化和反序列化所有Password
对象,无论包含对象是什么(例如,User
)。
然而,假设您只想定制Passwords
在User
对象上的序列化。为此,您可以为User
类型注册自定义PdxSerializer
,指定Class
的字段或属性的完全限定名,如下例所示
PdxSerializers
Map<?, PdxSerializer> customPdxSerializers = new HashMap<>();
customPdxSerializers.put("example.app.security.auth.model.User.password", new SaltedHashPasswordPdxSerializer());
mappingPdxSerializer.setCustomPdxSerializers(customPdxSerializers);
请注意,自定义PdxSerializer
注册键使用了完全限定的字段或属性名(即example.app.security.auth.model.User.password
)。
您可以使用更符合逻辑的代码片段来构造注册键,例如: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(..)
方法时,Customer
的id
字段被标记为 PDX 类型元数据中的标识符字段,通过使用PdxWriter.markIdentifierField(:String)
。
9.4.3. 映射只读属性
当您的实体定义了只读属性时会发生什么?
首先,了解什么是“只读”属性非常重要。如果您按照JavaBeans规范定义 POJO(如 Spring 所做),您可能会定义一个带有只读属性的 POJO,如下所示
package example;
class ApplicationDomainType {
private AnotherType readOnly;
public AnotherType getReadOnly() [
this.readOnly;
}
...
}
readOnly
属性是只读的,因为它没有提供设置方法(setter method)。它只有一个获取方法(getter method)。在这种情况下,readOnly
属性(不要与readOnly
DomainType
字段混淆)被认为是只读的。
因此,在反序列化期间的PdxSerializer.fromData(:Class<ApplicationDomainType>, :PdxReader)
方法中填充ApplicationDomainType
实例时,特别是当 PDX 序列化字节中存在值时,MappingPdxSerializer
不会尝试为该属性设置值。
这在您可能返回某个实体类型的视图或投影,并且只想设置可写入的状态时非常有用。也许实体的视图或投影是基于授权或其他标准的。重点是,您可以根据应用程序的使用场景和需求适当利用此特性。如果您希望字段或属性始终被写入,只需定义一个设置方法即可。
9.4.4. 映射瞬态属性
同样,当您的实体定义了transient
属性时会发生什么?
您会期望实体的transient
字段或属性在序列化实体时不会序列化到 PDX。事实确实如此,与 Pivotal GemFire 自身的ReflectionBasedAutoSerializer
不同,后者会通过 Java 反射序列化对象中可访问的一切。
MappingPdxSerializer
不会序列化任何被限定为瞬态的字段或属性,无论是使用 Java 自身的transient
关键字(对于类实例字段)还是在字段或属性上使用@Transient
Spring Data 注解。
例如,您可能会如下定义一个带有瞬态字段和属性的实体
package example;
class Process {
private transient int id;
private File workingDirectory;
private String name;
private Type type;
@Transient
public String getHostname() {
...
}
...
}
Process
的id
字段和可读的hostname
属性都不会写入 PDX。
9.4.5. 按类类型过滤
与 Pivotal GemFire 的ReflectionBasedAutoSerializer
类似,SDG 的MappingPdxSerializer
允许您过滤要序列化和反序列化的对象类型。
然而,与使用复杂正则表达式来表达序列化器处理哪些类型的 Pivotal GemFire ReflectionBasedAutoSerializer
不同,SDG 的MappingPdxSerializer
使用更健壮的java.util.function.Predicate
接口和 API 来表达类型匹配标准。
如果您喜欢使用正则表达式,可以使用 Java 的正则表达式支持来实现一个Predicate 。 |
Java 的Predicate
接口的优点在于,您可以使用方便且适当的 API 方法来组合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
,这些 Predicates 从以下包中过滤或排除类型
-
java.*
-
com.gemstone.gemfire.*
-
org.apache.geode.*
-
org.springframework.*
此外,MappingPdxSerializer
在调用PdxSerializer.toData(:Object, :PdxWriter)
方法时过滤null
对象,并在调用PdxSerializer.fromData(:Class<?>, :PdxReader)
方法时过滤null
类类型。
通过简单地定义一个Predicate
并将其添加到MappingPdxSerializer
中,如前所示,添加对其他类类型或整个类型包的排除非常容易。
MappingPdxSerializer.setExcludeTypeFilters(:Predicate<Class<?>>)
方法是累加性的,这意味着它使用Predicate.and(:Predicate<Class<?>>)
方法将您的应用程序定义的类型过滤器与上面指示的现有预定义类型过滤器Predicates
组合起来。
然而,如果您想包含被排除类型过滤器隐式排除的类类型(例如,java.security Principal
)怎么办?请参阅包含类型过滤。
包含类型过滤
如果您想显式包含类类型,或者覆盖隐式排除应用程序所需类类型(例如,java.security.Principal
,它默认被MappingPdxSerializer
上的java.*
包排除类型过滤器排除)的类类型过滤器,那么只需定义适当的Predicate
并使用MappingPdxSerializer.setIncludeTypeFilters(:Predicate<Class<?>>)
方法将其添加到序列化器中,如下所示
Predicate<Class<?>> principalTypeFilter =
type -> java.security.Principal.class.isAssignableFrom(type);
mappingPdxSerializer.setIncludeTypeFilters(principalTypeFilters);
同样,与setExcludeTypeFilters(:Predicate<Class<?>>)
一样,MappingPdxSerializer.setIncludeTypeFilters(:Predicate<Class<?>>)
方法是累加性的,因此使用Predicate.or(:Predicate<Class<?>>)
组合任何传递的类型过滤器。这意味着您可以根据需要多次调用setIncludeTypeFilters(:Predicate<Class<?>>)
。
当存在包含类型过滤器时,MappingPdxSerializer
会根据类类型是否未被隐式排除或是否被显式包含来决定是否反序列化/序列化该类类型的实例,两者中返回 true 的即为准。然后,该类类型的实例将被相应地序列化或反序列化。
例如,当如前所示显式注册类型过滤器Predicate<Class<Principal>>
时,它会取消掉对java.*
包类型的隐式排除类型过滤。
10. Spring Data for Pivotal GemFire Repository
Spring Data for Pivotal GemFire 提供使用 Spring Data Repository 抽象轻松将实体持久化到 Pivotal GemFire 中并执行查询的支持。有关 Repository 编程模型的通用介绍,请参阅此处。
10.1. Spring XML 配置
要引导 Spring Data Repository,请使用 Spring Data for Pivotal GemFire Data 命名空间中的<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 Repository,如下例所示
@EnableGemfireRepositories
引导 Spring Data for Pivotal GemFire Repository@SpringBootApplication
@EnableGemfireRepositories(basePackages = "com.example.acme.repository")
class SpringDataApplication {
...
}
您可以优先使用类型安全的basePackageClasses
属性,而不是使用basePackages
属性。basePackageClasses
允许您通过仅指定一个应用程序 Repository 接口类型来指定包含所有应用程序 Repository 类的包。考虑在每个包中创建一个特殊的无操作标记类或接口,其唯一目的是标识此属性引用的应用程序 Repository 的位置。
除了basePackages
和basePackageClasses
属性外,与 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 实现的一个示例是在执行连接时。SDG Repository 不支持连接。对于 Pivotal GemFire 的PARTITION
Region,连接必须在协同定位的PARTITION
Region 上执行,因为 Pivotal GemFire 不支持“分布式”连接。此外,等值连接 OQL 查询必须在 Pivotal GemFire Function 内部执行。有关 Pivotal GemFire 等值连接查询的更多详细信息,请参阅此处。
SDG 的 Repository 基础设施扩展的许多其他方面也可以定制。有关所有配置设置的更多详细信息,请参阅@EnableGemfireRepositories
Javadoc。
10.3. 执行 OQL 查询
Spring Data for Pivotal GemFire Repository 允许定义查询方法,以便轻松对托管实体映射到的 Region 执行 Pivotal GemFire OQL 查询,如下例所示
@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
注解查询方法,如第三个和第四个方法所示。
下表简要列出了您可以在查询方法中使用的支持关键字
关键字 | 示例 | 逻辑结果 |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(无关键字) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 查询语言扩展集包括
关键字 | 注解 | 描述 | 参数 |
---|---|---|---|
|
OQL 查询索引提示。 |
|
|
|
限定应用程序特定类型。 |
|
|
|
限制返回的查询结果集。 |
|
|
|
启用 OQL 查询特定的调试。 |
不适用 |
举例来说,假设您有一个Customers
应用程序域类和相应的 Pivotal GemFire Region,以及一个CustomerRepository
和一个按姓氏查找Customers
的查询方法,如下所示
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
注解查询方法,如下所示
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
注解是多余的,没有附加效果。
考虑到许多客户可能具有相同的信誉值, |
10.5. 查询后处理
由于使用了 Spring Data Repository 抽象,用于定义特定于数据存储的查询(例如 OQL)的查询方法约定简单方便。然而,有时仍然希望检查甚至可能修改从 Repository 查询方法生成的查询。
自 2.0.x 版本以来,Spring Data for Pivotal GemFire 包含了o.s.d.gemfire.repository.query.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
接受对应于类型参数T
和QUERY
的类型参数。类型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
实现来做到这一点
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
,如下例所示
interface CustomerRepository extends CrudRepository<Customer, Long> {
Customer findByAccountNumber(String accountNumber);
List<Customer> findByLastNameLike(String lastName);
}
然后,您可以将LoggingQueryPostProcessor
专门类型化为CustomerRepository
,如下所示
class LoggingQueryPostProcessor implements QueryPostProcessor<CustomerRepository, String> { .. }
因此,只有在CustomerRepository
接口中定义的查询(例如findByAccountNumber
)会被记录。
您可能希望为 Repository 查询方法定义的特定查询创建一个QueryPostProcessor
。例如,假设您想将从CustomerRepository.findByLastNameLike(:String)
查询方法生成的 OQL 查询限制为仅返回五个结果,并按firstName
升序排序Customers
。为此,您可以定义一个自定义的QueryPostProcessor
,如下例所示
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 约定来实现相同的效果。例如,相同的查询可以定义如下
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
属性,如下例所示
order
属性class LoggingQueryPostProcessor implements QueryPostProcessor<Repository, String> {
@Override
int getOrder() {
return 1;
}
}
class CustomerQueryPostProcessor implements QueryPostProcessor<CustomerRepository, String> {
@Override
int getOrder() {
return 0;
}
}
这确保您总是在LoggingQueryPostProcessor
记录查询之前看到由其他QueryPostProcessors
应用的后处理效果。
您可以在 Spring ApplicationContext
中定义任意数量的 QueryPostProcessors
,并以任意顺序应用它们,可以应用于所有或特定的应用程序 Repository 接口,通过使用 postProcess(..)
方法回调提供的参数来做到所需的精细程度。
11. 函数执行的注解支持
Spring Data for Pivotal GemFire 包含注解支持,以简化使用 Pivotal GemFire 函数执行的过程。
在底层,Pivotal GemFire API 提供了用于实现和注册部署在 Pivotal GemFire 服务器上的 Pivotal GemFire 函数的类,这些函数随后可以由其他对等成员应用程序或从缓存客户端远程调用。
函数可以并行执行,分布在集群中的多个 Pivotal GemFire 服务器上,使用 map-reduce 模式聚合结果并将其发送回调用者。函数也可以指定在单个服务器或区域上运行。Pivotal GemFire API 支持通过使用各种预定义范围来远程执行函数:在区域上、在成员上(分组)、在服务器上等。远程函数的实现和执行,与任何 RPC 协议一样,需要一些样板代码。
Spring Data for Pivotal GemFire,秉承 Spring 的核心价值主张,旨在隐藏远程函数执行的机制,让您专注于核心 POJO 编程和业务逻辑。为此,Spring Data for Pivotal GemFire 引入了注解,用于声明式地将 POJO 类的公共方法注册为 Pivotal GemFire 函数,并能够通过使用注解接口来调用已注册的函数(包括远程调用)。
11.1. 实现对比执行
需要解决两个独立的关注点:实现和执行。
第一个是函数实现(服务器端),它必须与 FunctionContext
交互以访问调用参数,与 ResultsSender
交互以发送结果,以及获取其他执行上下文信息。函数实现通常访问缓存和区域,并以唯一的 ID 注册到 FunctionService
中。
调用函数的缓存客户端应用程序不依赖于其实现。要调用函数,应用程序实例化一个 Execution
,提供函数 ID、调用参数和函数目标,函数目标定义其范围:区域、服务器、服务器组、成员或成员组。如果函数产生结果,调用者使用 ResultCollector
来聚合并获取执行结果。在某些情况下,需要自定义的 ResultCollector
实现,并可以注册到 Execution
中。
'客户端' 和 '服务器' 在此处用于函数执行的上下文,这可能与 Pivotal GemFire 的客户端-服务器拓扑中的客户端和服务器具有不同的含义。虽然使用 ClientCache 实例的应用程序通常会在集群中的一个或多个 Pivotal GemFire 服务器上调用函数,但在对等 (P2P) 配置中执行函数也是可能的,其中应用程序是托管对等 Cache 实例的集群成员。请记住,对等成员缓存应用程序受到作为集群对等成员的所有限制。 |
11.2. 实现函数
使用 Pivotal GemFire API,FunctionContext
提供了运行时调用上下文,包括客户端的调用参数和用于将结果发送回客户端的 ResultSender
实现。此外,如果函数在区域上执行,FunctionContext
实际上是 RegionFunctionContext
的一个实例,它提供额外的信息,例如函数被调用的目标区域、与 Execution
相关的任何过滤器(一组特定键)等等。如果区域是 PARTITION
区域,函数应使用 PartitionRegionHelper
提取本地数据集。
通过使用 Spring,您可以编写一个简单的 POJO,并使用 Spring 容器将其一个或多个公共方法绑定到函数。预期用作函数的 POJO 方法的签名通常必须与客户端的执行参数一致。然而,在区域执行的情况下,也可以提供区域数据(如果区域是 PARTITION
区域,则数据可能保存在本地分区中)。
此外,如果应用了过滤器,函数可能需要该过滤器。这表明客户端和服务器共享调用参数的契约,但方法签名可能包含额外的参数来传递由 FunctionContext
提供的值。一种可能性是客户端和服务器共享一个公共接口,但这并不是严格要求的。唯一的限制是,在额外参数解析后,方法签名必须与调用函数时使用的调用参数序列在顺序和类型上完全对应。
例如,假设客户端提供一个 String
和一个 int
作为调用参数。这些参数在 FunctionContext
中作为数组提供,如下例所示
Object[] args = new Object[] { "test", 123 };
Spring 容器应该能够绑定到类似于以下签名的方法(暂时忽略返回类型)
public Object method1(String s1, int i2) { ... }
public Object method2(Map<?, ?> data, String s1, int i2) { ... }
public Object method3(String s1, Map<?, ?> data, int i2) { ... }
public Object method4(String s1, Map<?, ?> data, Set<?> filter, int i2) { ... }
public void method4(String s1, Set<?> filter, int i2, Region<?,?> data) { ... }
public void method5(String s1, ResultSender rs, int i2) { ... }
public void method6(FunctionContest context) { ... }
一般规则是,一旦解析了任何额外参数(即区域数据和过滤器),其余参数必须在顺序和类型上与预期的函数方法参数完全对应。方法的返回类型必须是 void 或可序列化的类型(例如 java.io.Serializable
、DataSerializable
或 PdxSerializable
)。后者也是调用参数的要求。
区域数据通常应定义为 Map
,以便于单元测试,但如有必要,也可以是 Region 类型。如前例所示,如果您需要控制结果如何返回给客户端,直接传递 FunctionContext
本身或 ResultSender
也是有效的。
11.2.1. Function 实现的注解
以下示例展示了 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 支持的任何方法注册 bean(例如 XML 配置或使用 Spring Boot 时的 Java 配置类)。这使得 Spring 容器能够创建该类的一个实例,并将其包装在 PojoFunctionWrapper
中。Spring 为每个使用 `@GemfireFunction` 注解的方法创建一个包装器实例。每个包装器实例共享相同的目标对象实例来调用相应的方法。
POJO 函数类是 Spring bean 的事实可能带来其他好处。由于它与 Pivotal GemFire 组件(如缓存和区域)共享 ApplicationContext ,因此如有必要,可以将这些组件注入到类中。 |
Spring 创建包装器类并将函数注册到 Pivotal GemFire 的 FunctionService
中。用于注册每个函数的函数 ID 必须是唯一的。按照约定,它默认为简单(非限定)方法名。可以使用 `@GemfireFunction` 注解的 id
属性显式定义名称。
@GemfireFunction
注解还提供了其他配置属性:HA
和 optimizedForWrite
,这些属性与 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
可以是 ResourcePermission.Resource
枚举值中的一个。OPERATION
可以是 ResourcePermission.Operation
枚举值中的一个。Target
可以是区域的名称或 ResourcePermission.Target
枚举值中的一个(可选)。最后,如果指定了 Target
区域,Key
是该区域中的有效键(可选)。
PojoFunctionWrapper
实现了 Pivotal GemFire 的 Function
接口,绑定方法参数,并在其 execute()
方法中调用目标方法。它还使用 ResultSender
将方法的返回值发送回调用者。
11.2.2. 批量处理结果
如果返回类型是数组或 Collection
,则必须考虑如何返回结果。默认情况下,PojoFunctionWrapper
一次性返回整个数组或 Collection
。如果数组或 Collection
中的元素数量非常大,可能会导致性能下降。为了将负载分成更小、更易于管理的块,您可以设置 batchSize
属性,如前面所示的 function2
中所示。
如果您需要对 ResultSender 进行更多控制,特别是如果方法本身创建 Collection 会占用过多内存,您可以直接传递 ResultSender 或通过 FunctionContext 访问它,并在方法内部直接使用它将结果发送回调用者。 |
11.2.3. 启用注解处理
根据 Spring 标准,您必须显式激活 `@GemfireFunction` 注解的注解处理。以下示例通过 XML 激活注解处理
<gfe:annotation-driven/>
以下示例通过注解 Java 配置类来激活注解处理
@Configuration
@EnableGemfireFunctions
class ApplicationConfiguration { ... }
11.3. 执行函数
调用远程函数的进程需要提供函数 ID、调用参数、执行目标(onRegion
、onServers
、onServer
、onMember
或 onMembers
)以及(可选)过滤器集。使用 Spring Data for Pivotal GemFire,您只需定义一个由注解支持的接口。Spring 为该接口创建一个动态代理,该代理使用 FunctionService
创建一个 Execution
,调用 Execution
,并在必要时将结果强制转换为定义的返回类型。这种技术类似于 Spring Data for Pivotal GemFire 的 Repository 扩展的工作方式。因此,某些配置和概念应该很熟悉。
通常,单个接口定义映射到多个函数执行,每个函数执行对应于接口中定义的一个方法。
11.3.1. Function 执行的注解
为了支持客户端函数执行,提供了以下 SDG 函数注解:`@OnRegion`、`@OnServer`、`@OnServers`、`@OnMember` 和 `@OnMembers`。这些注解对应于 Pivotal GemFire 的 FunctionService
类提供的 Execution
实现。
每个注解都暴露了适当的属性。这些注解还提供了一个可选的 resultCollector
属性,其值是实现 ResultCollector
接口的 Spring bean 的名称,用于本次执行。
代理接口将所有声明的方法绑定到相同的执行配置。虽然预计单方法接口很常见,但接口中的所有方法都由同一个代理实例支持,因此共享相同的配置。 |
以下列表显示了一些示例
@OnRegion(region="SomeRegion", resultCollector="myCollector")
public interface FunctionExecution {
@FunctionId("function1")
String doIt(String s1, int i2);
String getString(Object arg1, @Filter Set<Object> keys);
}
默认情况下,函数 ID 是简单(非限定)方法名。可以使用 `@FunctionId` 注解将此调用绑定到不同的函数 ID。
11.3.2. 启用注解处理
客户端使用 Spring 的 classpath 组件扫描功能来发现注解接口。要在 XML 中启用函数执行注解处理,请在您的 XML 配置中插入以下元素
<gfe-data:function-executions base-package="org.example.myapp.gemfire.functions"/>
function-executions
元素在 gfe-data
XML 命名空间中提供。`base-package` 属性是必需的,以避免扫描整个 classpath。可以按照 Spring 参考文档中的描述提供额外的过滤器。
(可选)您可以如下注解您的 Java 配置类
@EnableGemfireFunctionExecutions(basePackages = "org.example.myapp.gemfire.functions")
11.4. 编程式函数执行
使用上一节中定义的函数执行注解接口,只需将您的接口自动注入到将调用函数的应用程序 bean 中即可
@Component
public class MyApplication {
@Autowired
FunctionExecution functionExecution;
public void doSomething() {
functionExecution.doIt("hello", 123);
}
}
或者,您可以直接使用函数执行模板。在以下示例中,GemfireOnRegionFunctionTemplate
创建一个 onRegion
函数 Execution
GemfireOnRegionFunctionTemplate
Set<?, ?> myFilter = getFilter();
Region<?, ?> myRegion = getRegion();
GemfireOnRegionOperations template = new GemfireOnRegionFunctionTemplate(myRegion);
String result = template.executeAndExtract("someFunction", myFilter, "hello", "world", 1234);
在内部,函数 Executions
总是返回一个 List
。executeAndExtract
假设 List
包含单个结果并尝试将该值强制转换为请求的类型。还有一个 execute
方法,它按原样返回 List
。第一个参数是函数 ID。过滤器参数是可选的。其余参数是一个可变参数 List
。
11.5. 使用 PDX 执行函数
将 Spring Data for Pivotal GemFire 的函数注解支持与 Pivotal GemFire 的 PDX 序列化结合使用时,需要记住一些注意事项。
正如本节前面通过示例解释的那样,您通常应该使用使用 Spring Data for Pivotal GemFire 函数注解注解的 POJO 类来定义 Pivotal GemFire 函数,如下所示
public class OrderFunctions {
@GemfireFunction(...)
Order process(@RegionData data, Order order, OrderSource orderSourceEnum, Integer count) { ... }
}
Integer 类型的 count 参数是任意的,Order 类和 OrderSource 枚举的分离也是任意的,它们可能逻辑上应该组合在一起。然而,这样设置参数是为了演示在 PDX 上下文中函数执行的问题。 |
您的 Order
类和 OrderSource
枚举可以定义如下
public class Order ... {
private Long orderNumber;
private LocalDateTime orderDateTime;
private Customer customer;
private List<Item> items
...
}
public enum OrderSource {
ONLINE,
PHONE,
POINT_OF_SALE
...
}
当然,您可以定义一个函数 Execution
接口来调用 'process' Pivotal GemFire 服务器函数,如下所示
@OnServer
public interface OrderProcessingFunctions {
Order process(Order order, OrderSource orderSourceEnum, Integer count);
}
显然,这个 process(..)
Order
函数正在从带有 ClientCache
实例的客户端调用(即 `distribution
都要求在客户端和服务器(或对等节点)之间传输的数据进行序列化。
现在,如果您已配置 Pivotal GemFire 使用 PDX 进行序列化(例如,而不是 Java 序列化),您还可以在配置 Pivotal GemFire 服务器时将 pdx-read-serialized
属性设置为 true
,如下所示
<gfe:cache ... pdx-read-serialized="true"/>
或者,您可以为 Pivotal GemFire 缓存客户端应用程序将 pdx-read-serialized
属性设置为 true
,如下所示
<gfe:client-cache ... pdx-read-serialized="true"/>
这样做会导致从缓存(即区域)读取的所有值以及在客户端和服务器(或对等节点)之间传递的信息保持序列化形式,包括但不限于函数参数。
Pivotal GemFire 仅序列化您已专门配置(注册)的应用程序域对象类型,无论是使用 Pivotal GemFire 的 ReflectionBasedAutoSerializer
,还是更特定地(推荐)使用“自定义” Pivotal GemFire PdxSerializer
。如果您使用 Spring Data for Pivotal GemFire 的 Repository 扩展,您甚至可能考虑使用 Spring Data for Pivotal GemFire 的 MappingPdxSerializer
,它使用实体映射元数据来确定从应用程序域对象到 PDX 实例序列化的数据。
然而,不太明显的是,Pivotal GemFire 自动处理 Java Enum
类型,无论它们是否被显式配置(即使用 ReflectionBasedAutoSerializer
、使用正则表达式模式和 classes
参数注册,或由“自定义” Pivotal GemFire PdxSerializer
处理),尽管 Java 枚举实现了 java.io.Serializable
。
因此,当您在注册并使用 Pivotal GemFire 函数(包括 Spring Data for Pivotal GemFire 函数注解 POJO 类)的 Pivotal GemFire 服务器上将 pdx-read-serialized
设置为 true
时,在调用函数 Execution
时可能会遇到令人惊讶的行为。
您在调用函数时可能会传递以下参数
orderProcessingFunctions.process(new Order(123, customer, LocalDateTime.now(), items), OrderSource.ONLINE, 400);
然而,服务器上的 Pivotal GemFire 函数会收到以下内容
process(regionData, order:PdxInstance, :PdxInstanceEnum, 400);
Order
和 OrderSource
已作为 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) {
...
}
仅当相应的应用程序域类型位于 classpath 上且函数注解 POJO 方法期望它时,Spring Data for Pivotal GemFire 才会将 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 (Spring Data for Pivotal GemFire) XML 配置中创建 LuceneIndex
,如下所示
<gfe:lucene-index id="IndexOne" fields="fieldOne, fieldTwo" region-path="/Example"/>
此外,Apache Lucene 允许为每个字段指定 分析器(analyzers),并可以按以下示例进行配置
<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 定义,并使用嵌套的 `ref
属性进行引用,如下所示:`
Spring Data for Pivotal GemFire 的 LuceneIndexFactoryBean
API 和 SDG 的 XML 命名空间还允许在创建 LuceneIndex
时指定一个 org.apache.geode.cache.lucene.LuceneSerializer
。LuceneSerializer
允许您配置在对象被索引时,如何将对象转换为 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
区域上创建。
其次,所有 LuceneIndexes
必须在它们所应用的区域之前创建。
为了确保 Spring 容器中定义的所有声明的 LuceneIndexes 在其应用的区域之前创建,SDG 包含 org.springframework.data.gemfire.config.support.LuceneIndexRegionBeanFactoryPostProcessor 。您可以使用 `BeanFactoryPostProcessor 。o.s.d.g.config.support.LuceneIndexRegionBeanFactoryPostProcessor 只能在使用 SDG XML 配置时使用。有关 Spring BeanFactoryPostProcessors 的更多详细信息可以在这里找到。 |
Pivotal GemFire 的这些限制可能在未来版本中不会适用,这就是为什么 SDG LuceneIndexFactoryBean
API 也直接引用区域,而不仅仅是区域路径的原因。
当您希望在应用程序生命周期的后期,根据需求,在具有数据的现有区域上定义 LuceneIndex
时,这更为理想。在可能的情况下,SDG 努力遵循强类型对象。然而,目前您必须使用 regionPath
属性来指定应用 LuceneIndex
的区域。
此外,在前例中,请注意 Books 区域 bean 定义上存在 Spring 的 `@DependsOn` 注解。这创建了从 Books 区域 bean 到 bookTitleIndex LuceneIndex bean 定义的依赖关系,确保 LuceneIndex 在其应用的区域之前创建。 |
现在,一旦我们有了 LuceneIndex
,我们就可以执行基于 Lucene 的数据访问操作,例如查询。
12.1. Lucene 模板数据访问器
Spring Data for Pivotal GemFire 提供了两种主要的 Lucene 数据访问操作模板,具体取决于您的应用程序愿意处理的最低级别。
LuceneOperations
接口使用 Pivotal GemFire Lucene 类型定义查询操作,这些类型在以下接口定义中定义
public interface LuceneOperations {
<K, V> List<LuceneResultStruct<K, V>> query(String query, String defaultField [, int resultLimit]
, String... projectionFields);
<K, V> PageableLuceneQueryResults<K, V> query(String query, String defaultField,
int resultLimit, int pageSize, String... projectionFields);
<K, V> List<LuceneResultStruct<K, V>> query(LuceneQueryProvider queryProvider [, int resultLimit]
, String... projectionFields);
<K, V> PageableLuceneQueryResults<K, V> query(LuceneQueryProvider queryProvider,
int resultLimit, int pageSize, String... projectionFields);
<K> Collection<K> queryForKeys(String query, String defaultField [, int resultLimit]);
<K> Collection<K> queryForKeys(LuceneQueryProvider queryProvider [, int resultLimit]);
<V> Collection<V> queryForValues(String query, String defaultField [, int resultLimit]);
<V> Collection<V> queryForValues(LuceneQueryProvider queryProvider [, int resultLimit]);
}
[, int resultLimit] 表示 resultLimit 参数是可选的。 |
LuceneOperations
接口中的操作与 Pivotal GemFire 的 LuceneQuery
接口提供的操作匹配。然而,SDG 的附加价值在于将专有的 Pivotal GemFire 或 Apache Lucene Exceptions
转换为 Spring 高度一致和表达性的 DAO 异常层次结构,尤其因为许多现代数据访问操作涉及多个存储或仓库。
此外,SDG 的 LuceneOperations
接口可以在底层 Pivotal GemFire 或 Apache Lucene API 发生接口破坏性更改时保护您的应用程序。
然而,如果仅使用 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);
或者,您可以查询类型为 Customer
的 Page
,如下所示
Page<Customer> customers = luceneTemplate.query("lastName: D*", "lastName", 100, 20, Customer.class);
然后可以使用 Page
来获取结果的单独页面,如下所示
List<Customer> firstPage = customers.getContent();
方便的是,Spring Data Commons Page
接口也实现了 java.lang.Iterable<T>
,从而易于迭代其内容。
对 Spring Data Commons 投影基础设施的唯一限制是投影类型必须是接口。然而,可以扩展提供的 SDC 投影基础设施,并提供一个自定义的 ProjectionFactory
,该工厂使用 CGLIB 生成代理类作为投影实体。
您可以使用 setProjectionFactory(:ProjectionFactory)
在 Lucene 模板上设置自定义的 ProjectionFactory
。
12.2. 注解配置支持
最后,Spring Data for Pivotal GemFire 为 LuceneIndexes
提供了注解配置支持。
最终,SDG 的 Lucene 支持将进入 Pivotal GemFire 的 Repository 基础设施扩展,以便 Lucene 查询可以表示为应用程序 Repository
接口上的方法,就像当前的 OQL 支持一样。
然而,在此期间,如果您希望方便地表达 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 区域。 |
根据我们前面关于 Person
类的定义,SDG 的注解配置支持会找到 Person
实体类定义,并确定人员存储在一个名为“People”的 PARTITION
区域中,并且 Person
在 birthDate
上有一个 OQL Index
以及在 lastName
上有一个 LuceneIndex
。
13. 在 Pivotal GemFire 中启动 Spring 应用上下文
通常,基于 Spring 的应用程序使用 Spring Data for Pivotal GemFire 的功能启动 Pivotal GemFire。通过指定使用 Spring Data for Pivotal GemFire XML 命名空间的 `Cache
实例,并使用默认设置进行初始化。
然而,有时需要(或许是您的 IT 组织强制要求)Pivotal GemFire 通过提供的 Pivotal GemFire 工具套件(例如使用 Gfsh)进行完全管理和操作。通过使用 Gfsh,Pivotal GemFire 启动您的 Spring ApplicationContext
,而不是反过来。Pivotal GemFire 执行启动并托管您的应用程序,而不是由应用程序服务器或使用 Spring Boot 的 Java main 类来启动。
Pivotal GemFire 不是应用程序服务器。此外,这种方法在使用 Pivotal GemFire 缓存配置方面存在限制。 |
13.1. 使用 Pivotal GemFire 通过 Gfsh 启动 Spring 上下文
为了在使用 Gfsh 启动 Pivotal GemFire 服务器时在 Pivotal GemFire 中启动 Spring ApplicationContext
,您必须使用 Pivotal GemFire 的初始化器(initializer)功能。初始化器块可以声明一个在 Pivotal GemFire 初始化缓存后启动的应用程序回调。
初始化器在 initializer
元素内声明,使用最少量的 Pivotal GemFire 原生 cache.xml
片段。要启动 Spring ApplicationContext
,需要一个 cache.xml
文件,这与启动使用组件扫描配置的 Spring ApplicationContext
(例如 `
幸运的是,框架已经方便地提供了这样的初始化器: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-location
、properties-ref
、critical-heap-percentage
、pdx-serializer-ref
、lock-lease
等)都不能指定。如果使用了,这些属性将被忽略。
这样做的原因是,在初始化程序被调用之前,Pivotal GemFire 本身就已经创建并初始化了缓存。因此,缓存已经存在,并且由于它是“单例”,它不能被重新初始化或增强其任何配置。
13.2. 惰性连接 (Lazy-wiring) Pivotal GemFire 组件
Spring Data for Pivotal GemFire 已经提供了对 Pivotal GemFire 在 cache.xml
中声明和创建的组件(例如 CacheListeners
、CacheLoaders
、CacheWriters
等)的自动连接 (auto-wiring) 支持,通过使用 SDG 的 WiringDeclarableSupport
类,如 使用自动连接和注解进行配置 中所述。然而,这仅在 Spring 进行引导 (bootstrapping) 时(即 Spring 引导 Pivotal GemFire 时)才有效。
当您的 Spring ApplicationContext
由 Pivotal GemFire 引导时,这些 Pivotal GemFire 应用组件会被忽略,因为 Spring ApplicationContext
尚不存在。只有在 Pivotal GemFire 调用初始化程序块之后,Spring ApplicationContext
才会创建,这发生在所有其他 Pivotal GemFire 组件(缓存、Region 等)都已创建并初始化之后。
为了解决这个问题,引入了一个新的 LazyWiringDeclarableSupport
类。这个新类感知 Spring ApplicationContext
。这个抽象基类背后的意图是,任何实现类都会注册自己,以便在初始化程序被调用后最终由 Pivotal GemFire 创建的 Spring 容器进行配置。实质上,这使您的 Pivotal GemFire 应用组件有机会由 Spring 容器中定义的 Spring bean 进行配置和自动连接 (auto-wired)。
为了让您的 Pivotal GemFire 应用组件能够被 Spring 容器自动连接 (auto-wired),您应该创建一个继承 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 示例 仓库中。 |
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 组件进行类路径扫描以查找 Spring 组件。
缓存配置定义了 Pivotal GemFire 缓存、一个 region,并为了说明目的,定义了一个充当日志记录器的 CacheListener
。
主要的 bean 是 HelloWorld
和 CommandProcessor
,它们依赖于 GemfireTemplate
与分布式 fabric 进行交互。这两个类都使用注解来定义它们的依赖关系和生命周期回调。
资源
附录
附录 A:命名空间参考
`<repositories />` 元素
`<repositories />` 元素触发 Spring Data 仓库基础设施的设置。最重要的属性是 base-package
,它定义了扫描 Spring Data 仓库接口的包。详见“[repositories.create-instances.spring]”。下表描述了 <repositories />
元素的属性:
名称 | 描述 |
---|---|
|
在自动检测模式下,定义用于扫描继承自 |
|
定义自动检测自定义仓库实现的后缀。名称以此后缀结尾的类被视为候选类。默认为 |
|
确定用于创建查询查找策略的策略。详见“[repositories.query-methods.query-lookup-strategies]”。默认为 |
|
定义用于搜索包含外部定义查询的 Properties 文件的位置。 |
|
是否应考虑嵌套的仓库接口定义。默认为 |
附录 B:Populators 命名空间参考
`<populator />` 元素
`<populator />` 元素允许通过 Spring Data 仓库基础设施填充数据存储。[1]
名称 | 描述 |
---|---|
|
指定从何处查找用于填充仓库的对象文件。 |
附录 C:仓库查询关键字
支持的查询关键字
下表列出了 Spring Data 仓库查询派生机制通常支持的关键字。但是,请查阅特定存储的文档以获取支持关键字的精确列表,因为此处列出的某些关键字可能在特定存储中不受支持。
逻辑关键字 | 关键字表达式 |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
附录 D:仓库查询返回类型
支持的查询返回类型
下表列出了 Spring Data 仓库通常支持的返回类型。但是,请查阅特定存储的文档以获取支持返回类型的精确列表,因为此处列出的某些类型可能在特定存储中不受支持。
地理空间类型(例如 GeoResult 、GeoResults 和 GeoPage )仅适用于支持地理空间查询的数据存储。 |
返回类型 | 描述 |
---|---|
|
表示没有返回值。 |
原语类型 (Primitives) |
Java 原语类型。 |
包装类型 (Wrapper types) |
Java 包装器类型。 |
|
一个唯一的实体。期望查询方法最多返回一个结果。如果未找到结果,则返回 |
|
一个 `Iterator`。 |
|
一个 `Collection`。 |
|
一个 `List`。 |
|
一个 Java 8 或 Guava `Optional`。期望查询方法最多返回一个结果。如果未找到结果,则返回 |
|
Scala 或 Vavr `Option` 类型。语义上与前面描述的 Java 8 的 `Optional` 行为相同。 |
|
一个 Java 8 `Stream`。 |
|
`Iterable` 的便捷扩展,直接暴露用于流处理 (stream)、映射 (map) 和过滤 (filter) 结果、连接 (concatenate) 它们等的方法。 |
实现 `Streamable` 并接受 `Streamable` 构造函数或工厂方法参数的类型 |
暴露接受 `Streamable` 作为参数的构造函数或 `….of(…)` / `….valueOf(…)` 工厂方法的类型。详见 [repositories.collections-and-iterables.streamable-wrapper]。 |
Vavr `Seq`, `List`, `Map`, `Set` |
Vavr 集合类型。详见 [repositories.collections-and-iterables.vavr]。 |
|
一个 `Future`。期望方法使用 `@Async` 进行注解,并且需要启用 Spring 的异步方法执行能力。 |
|
一个 Java 8 `CompletableFuture`。期望方法使用 `@Async` 进行注解,并且需要启用 Spring 的异步方法执行能力。 |
|
一个 `org.springframework.util.concurrent.ListenableFuture`。期望方法使用 `@Async` 进行注解,并且需要启用 Spring 的异步方法执行能力。 |
|
一个固定大小的数据块,并指示是否有更多数据可用。需要一个 `Pageable` 方法参数。 |
|
一个带有附加信息(例如总结果数)的 `Slice`。需要一个 `Pageable` 方法参数。 |
|
一个结果条目,带有附加信息,例如到参考位置的距离。 |
|
一个 `GeoResult<T>` 列表,带有附加信息,例如到参考位置的平均距离。 |
|
一个带有 `GeoResult<T>` 的 `Page`,例如到参考位置的平均距离。 |
|
一个 Project Reactor `Mono`,使用响应式仓库发出零个或一个元素。期望查询方法最多返回一个结果。如果未找到结果,则返回 `Mono.empty()`。如果多于一个结果,则触发 `IncorrectResultSizeDataAccessException`。 |
|
一个 Project Reactor `Flux`,使用响应式仓库发出零个、一个或多个元素。返回 `Flux` 的查询也可以发出无限数量的元素。 |
|
一个 RxJava `Single`,使用响应式仓库发出单个元素。期望查询方法最多返回一个结果。如果未找到结果,则返回 `Mono.empty()`。如果多于一个结果,则触发 `IncorrectResultSizeDataAccessException`。 |
|
一个 RxJava `Maybe`,使用响应式仓库发出零个或一个元素。期望查询方法最多返回一个结果。如果未找到结果,则返回 `Mono.empty()`。如果多于一个结果,则触发 `IncorrectResultSizeDataAccessException`。 |
|
一个 RxJava `Flowable`,使用响应式仓库发出零个、一个或多个元素。返回 `Flowable` 的查询也可以发出无限数量的元素。 |