© 2010-2019 原始作者。
本文件的副本可供您自己使用,也可分发给其他人,前提是您不对此类副本收取任何费用,并且无论以印刷版还是电子版分发,每个副本都包含此版权声明。 |
前言
Spring Data for Pivotal GemFire 专注于将 Spring Framework 强大的非侵入式编程模型和概念与 Pivotal GemFire 集成,以便在使用 Pivotal GemFire 作为数据管理解决方案时简化 Java 应用程序的配置和开发。
本文档假设您已经对核心 Spring Framework 和 Pivotal GemFire 概念有了基本的了解和一些熟悉。
虽然已尽一切努力确保本文件全面、完整且无错误,但某些主题超出了本文件的范围,可能需要更多解释(例如,在保持一致性的同时使用分区进行数据分布管理和高可用性)。此外,还可能出现一些印刷错误。如果您发现错误甚至更严重的错误,请通过在 JIRA 中提出适当的 问题,将这些问题告知 Spring Data 团队。
1. 简介
Pivotal GemFire 的 Spring Data 参考指南解释了如何使用 Spring Framework 配置和开发 Pivotal GemFire 应用程序。它介绍了基本概念并提供了大量示例,帮助您快速入门。
2. 要求
Pivotal GemFire 的 Spring Data 需要 Java 8.0、Spring Framework 5 和 Pivotal GemFire 9.8.2。
3. 新功能
从 1.2.0.RELEASE 开始,此项目(以前称为 Spring GemFire)已更名为 Spring Data for Pivotal GemFire,以反映它现在是 Spring Data 项目的模块,并基于 Pivotal GemFire 构建。 |
3.1. 1.2 版本中的新增功能
-
通过 SDG
gfe
XML 命名空间全面支持 Pivotal GemFire 配置。现在,可以完全配置 Pivotal GemFire 组件,而无需本机cache.xml
文件。 -
Pivotal GemFire 6.6.x 的 WAN 网关支持。请参见 配置 WAN 网关
-
使用专用 SDG XML 命名空间 gfe-data 的 Spring Data 存储库支持。请参见 Pivotal GemFire 存储库的 Spring Data
-
gfe-data
XML 命名空间支持注册 Pivotal GemFire 函数。请参见 配置函数服务 -
已向 SDG
gfe
XML 命名空间添加了一个顶级<disk-store>
元素,以允许在区域以及支持持久备份或溢出的其他 Pivotal GemFire 组件之间共享持久存储。请参见 [bootstrap-diskstore]<*-region>
元素不再允许嵌套的<disk-store>
元素。 -
Pivotal GemFire 子区域受嵌套
<*-region>
元素支持。 -
已添加一个
<local-region>
元素来配置本地区域。 -
支持 Pivotal GemFire 7.0 中重新设计的 WAN 网关。
3.2. 1.3 版本中的新增功能
-
已升级到 Spring Framework 3.2.8。
-
已升级到 Spring Data Commons 1.7.1。
-
Pivotal GemFire 函数的注释支持。现在,可以通过使用注释来声明和注册以 POJO 形式编写的函数。此外,函数执行被定义为带注释的接口,类似于 Spring Data 存储库的工作方式。请参见 函数执行的注释支持。
-
在 SDG XML 命名空间中添加了
<datasource>
元素,以简化与 Pivotal GemFire 数据网格建立基本 客户端连接。 -
在 SDG
gfe-data
XML 命名空间中添加了<json-region-autoproxy>
元素,以 支持 JSON,该功能在 Pivotal GemFire 7.0 中引入,使 Spring AOP 能够在区域数据访问操作中自动执行必要的转换。 -
升级到 Pivotal GemFire 7.0.1,并为新的
AsyncEventQueue
属性添加了 XML 命名空间支持。 -
添加了对在区域上设置订阅兴趣策略的支持。
-
支持函数执行的 void 返回值。有关完整详细信息,请参阅 函数执行的注释支持。
-
支持持久化本地区域。请参阅 本地区域。
-
支持 Pivotal GemFire 客户端缓存上的条目生存时间 (TTL) 和条目空闲时间 (TTI)。请参阅 配置 Pivotal GemFire ClientCache
-
通过使用单个 Pivotal GemFire 集群,在 tc Server 内同时运行,支持多个基于 Web 的 Pivotal GemFire Spring Data 应用程序。
-
通过使用 SDG
gfe
XML 命名空间,支持所有缓存区域定义上的concurrency-checks-enabled
。请参阅 [bootstrap:region:common:attributes] -
支持客户端本地区域上的
CacheLoaders
和CacheWriters
。 -
支持在 Pivotal GemFire Cache 子区域上注册
CacheListeners
、AsyncEventQueues
和GatewaySenders
。 -
支持区域中的 PDX 持久键。
-
当使用
colocated-with
属性指定并置时,支持在 Spring 上下文中正确创建分区区域 bean。 -
使用 SDG
gfe
XML 命名空间中正确的嵌套<*-region>
元素语法,全面支持缓存子区域。
3.3. 1.4 版本中的新增功能
-
升级到 Pivotal GemFire 7.0.2。
-
升级到 Spring Framework 3.2.13.RELEASE。
-
升级到 Spring Data Commons 1.8.6.RELEASE。
-
将 Spring Data for Pivotal GemFire 与 Spring Boot 集成,其中包括
spring-boot-starter-data-gemfire
POM 和一个 Spring Boot 示例应用程序,该应用程序演示了使用 SDG 配置并使用 Spring Boot 引导的 Pivotal GemFire 缓存事务。 -
添加了在从
Gfsh
启动时在 Pivotal GemFire 服务器中引导 SpringApplicationContext
的支持。请参阅 在 Pivotal GemFire 中引导 Spring ApplicationContext -
添加了将应用程序域对象和实体持久化到多个 Pivotal GemFire 缓存区域的支持。请参阅 实体映射
-
添加了对将应用程序域对象和实体持久化到 Pivotal GemFire 缓存子区域的支持,避免在子区域可唯一标识但名称相同的情况下发生冲突。请参阅 实体映射
-
向所有 Pivotal GemFire 缓存区域类型的数据策略和区域快捷方式添加了严格的 XSD 类型规则。
-
更改了 SDG
<*-region>
元素的默认行为,从查找变为始终创建新区域,并提供了使用ignore-if-exists
属性恢复旧行为的选项。请参阅 通用区域属性 和 [bootstrap:region:common:regions-subregions-lookups-caution] -
Spring Data for Pivotal GemFire 现在可以在 JDK 7 和 JDK 8 上完全构建和运行。
3.4. 1.5 版本中的新增功能
-
保持与 Pivotal GemFire 7.0.2 的兼容性。
-
升级到 Spring Framework 4.0.9.RELEASE。
-
升级到 Spring Data Commons 1.9.4.RELEASE。
-
将参考指南转换为 Asciidoc。
-
重新支持在 OSGi 容器中部署 Spring Data for Pivotal GemFire。
-
删除了 Spring Data for Pivotal GemFire XML 命名空间区域类型元素中指定的所有默认值,以改为依赖 Pivotal GemFire 默认值。
-
增加了自动创建
DiskStore
目录位置的便利性。 -
现在可以从
Gfsh
执行 SDG 注释的函数实现。 -
启用了手动启动 Pivotal GemFire
GatewayReceivers
。 -
添加了对自动区域查找的支持。请参阅 [bootstrap:region:auto-lookup]
-
添加了对区域模板的支持。请参阅 [bootstrap:region:common:region-templates]
3.5. 1.6 版本中的新增功能
-
升级到 Pivotal GemFire 8.0.0。
-
保持与 Spring Framework 4.0.9.RELEASE 的兼容性。
-
升级到 Spring Data Commons 1.10.2.RELEASE。
-
添加了对 Pivotal GemFire 8 的新基于群集的配置服务的支持。
-
启用了在 Spring 配置的 Pivotal GemFire 服务器中使用“自动重新连接”功能。
-
允许创建并发和并行的
AsyncEventQueues
和GatewaySenders
。 -
添加了对 Pivotal GemFire 8 的区域数据压缩的支持。
-
添加了属性,用于设置
DiskStore
使用情况的临界百分比和警告百分比。 -
支持向
GatewaySenders
添加EventSubstitutionFilters
的功能。
3.6. 1.7 版本中的新增功能
-
升级到 Pivotal GemFire 8.1.0。
-
升级到 Spring Framework 4.1.9.RELEASE。
-
升级到 Spring Data Commons 1.11.6.RELEASE。
-
添加了对 Apache Geode 的早期访问支持。
-
添加了对在 Spring XML、
cache.xml
中配置的现有区域或甚至 Pivotal GemFire 的群集配置服务中添加 Spring 定义的CacheListeners
、CacheLoaders
和CacheWriters
的支持。 -
向
SpringContextBootstrappingInitializer
添加了 Spring JavaConfig 支持。 -
在
SpringContextBootstrappingInitializer
中添加了对自定义ClassLoaders
的支持,以加载 Spring 定义的 Bean 类。 -
添加了对
LazyWiringDeclarableSupport
重新初始化和完全替换WiringDeclarableSupport
的支持。 -
向
<gfe:pool>
元素添加了locators
和servers
属性,允许使用 Spring 的属性占位符配置变量定位器和服务器端点列表。 -
支持将
<gfe-data:datasource>
元素与非 Spring 配置的 Pivotal GemFire 服务器一起使用。 -
添加了多索引定义和创建支持。
-
添加了对缓存和区域数据快照的支持。请参阅 配置快照服务
3.7. 1.8 版本中的新增功能
-
已升级到 Pivotal GemFire 8.2.0。
-
已升级到 Spring Framework 4.2.9.RELEASE。
-
已升级到 Spring Data Commons 1.12.11.RELEASE。
-
添加了 Maven POM 以使用 Maven 构建 SDG。
-
添加了对 CDI 的支持。
-
启用了在没有
Pool
的情况下配置ClientCache
。 -
将
<gfe:cache>
和<gfe:client-cache>
元素的use-bean-factory-locator
属性的默认值设为 false。 -
向
<gfe:client-cache>
添加了durable-client-id
和durable-client-timeout
属性。 -
使
GemfirePersistentProperty
现在可以正确处理其他非实体标量类型(例如BigDecimal
和BigInteger
)。 -
防止在使用这些
Pools
的Regions
之前销毁 SDG 定义的Pools
。 -
处理了作为存储库查询方法定义的不区分大小写的 Pivotal GemFire OQL 查询。
-
将
GemFireCache.evict(key)
更改为在 SDG 的 Spring Cache Abstraction 支持中调用Region.remove(key)
。 -
修复了使用为 Pivotal GemFire 服务器组配置的特定
Pool
关联的客户端Region
上的存储库查询的RegionNotFoundException
。 -
将
GatewaySenders/Receivers
更改为不再与 Spring 容器绑定。
3.8. 1.9 版本中的新增内容
-
已升级至 Pivotal GemFire 8.2.11。
-
已升级至 Spring Framework 4.3.18.RELEASE。
-
已升级至 Spring Data Commons 1.13.13.RELEASE。
-
引入了完全基于注释的新配置模型,其灵感来自 Spring Boot。
-
在
GemfireTransactionManager
中添加了对挂起和恢复的支持。 -
在存储库中添加了支持,以便在没有
@Id
注释时使用 beanid
属性作为区域键。 -
在使用
@EnablePdx
时,使用MappingPdxSerializer
作为默认的 Pivotal GemFire 序列化策略。 -
启用
GemfireCacheManager
以明确列出要在 Spring 的缓存抽象中使用的区域名称。 -
配置了 Pivotal GemFire 缓存、缓存服务器、定位器、池、区域、索引、磁盘存储、到期、驱逐、统计信息、多播、HttpService、Auth、SSL、日志记录、系统属性。
-
添加了对类路径上具有多个 Spring Data 模块的存储库支持。
3.9. 2.0 版本中的新增内容
-
已升级至 Pivotal GemFire 9.1.1。
-
已升级至 Spring Data Commons 2.0.8.RELEASE。
-
已升级至 Spring Framework 5.0.7.RELEASE。
-
通过按问题打包不同的类和组件来重组 SDG 代码库。
-
添加了对 Java 8 类型的广泛支持,尤其是在 SD 存储库抽象中。
-
更改为存储库接口和抽象,例如,不再要求 ID 为
java.io.Serializable
。 -
将
@EnableEntityDefinedRegions
注释ignoreIfExists
属性默认设置为true
。 -
将
@Indexed
注释override
属性默认设置为false
。 -
将
@EnableIndexes
重命名为@EnableIndexing
。 -
引入了
InterestsBuilder
类,以便在使用 JavaConfig 时轻松方便地表达客户端和服务器之间键和值中的兴趣。 -
在注释配置模型中添加了对非堆、Redis 适配器和 Pivotal GemFire 的新安全框架的支持。
参考指南
4. 文档结构
以下章节介绍 Spring Data for Pivotal GemFire 提供的核心功能
-
使用 Spring 容器引导 Pivotal GemFire 介绍了为配置、初始化和访问 Pivotal GemFire 缓存、区域和相关分布式系统组件提供的配置支持。
-
使用 Pivotal GemFire API 介绍了 Pivotal GemFire API 与 Spring 中提供的各种数据访问功能之间的集成,例如基于模板的数据访问、异常转换、事务管理和缓存。
-
使用 Pivotal GemFire 序列化 介绍了对 Pivotal GemFire 对受管理对象的序列化和反序列化的增强。
-
POJO 映射 介绍了使用 Spring Data 将存储在 Pivotal GemFire 中的 POJO 进行持久性映射。
-
Spring Data for Pivotal GemFire 存储库 介绍了如何创建和使用 Spring Data 存储库,通过使用基本的 CRUD 和简单查询操作来访问存储在 Pivotal GemFire 中的数据。
-
函数执行的注释支持 介绍了如何创建和使用 Pivotal GemFire 函数,通过使用注释在数据所在的位置执行分布式计算。
-
连续查询 (CQ) 介绍了如何使用 Pivotal GemFire 的连续查询 (CQ) 功能来处理事件流,该事件流基于已定义并已在 Pivotal GemFire 的 OQL(对象查询语言)中注册的兴趣。
-
在 Pivotal GemFire 中引导 Spring ApplicationContext 介绍了如何使用
Gfsh
配置和引导在 Pivotal GemFire 服务器中运行的 SpringApplicationContext
。 -
示例应用程序 介绍了随发行版提供的示例,以说明 Spring Data for Pivotal GemFire 中提供的各种功能。
5. 使用 Spring 容器引导 Pivotal GemFire
Spring Data for Pivotal GemFire 使用 Spring IoC 容器提供 Pivotal GemFire 内存数据网格 (IMDG) 的完整配置和初始化。该框架包含多个类,以帮助简化 Pivotal GemFire 组件的配置,包括:缓存、区域、索引、磁盘存储、函数、WAN 网关、持久性备份以及其他几个分布式系统组件,以支持各种应用程序用例,同时最大程度地减少工作量。
本部分假定您基本熟悉 Pivotal GemFire。有关更多信息,请参阅 Pivotal GemFire 产品文档。 |
5.1. 在 Pivotal GemFire cache.xml
中使用 Spring 的优势
Spring Data for Pivotal GemFire 的 XML 命名空间支持对 Pivotal GemFire 内存数据网格 (IMDG) 进行完全配置。XML 命名空间是两种在 Spring 上下文中配置 Pivotal GemFire 的方式之一,以便在 Spring 容器内正确管理 Pivotal GemFire 的生命周期。在 Spring 上下文中配置 Pivotal GemFire 的另一种方法是使用 基于注解的配置。
虽然出于传统原因,对 Pivotal GemFire 的本机 cache.xml
的支持仍然存在,但鼓励使用 XML 配置的 Pivotal GemFire 应用程序开发人员在 Spring XML 中执行所有操作,以利用 Spring 提供的许多优点,例如模块化 XML 配置、属性占位符和覆盖、SpEL(Spring 表达式语言)和环境配置文件。在 XML 命名空间的背后,Spring Data for Pivotal GemFire 广泛使用 Spring 的 FactoryBean
模式来简化 Pivotal GemFire 组件的创建、配置和初始化。
Pivotal GemFire 提供了多个回调接口,例如 CacheListener
、CacheLoader
和 CacheWriter
,这些接口允许开发人员添加自定义事件处理程序。使用 Spring 的 IoC 容器,您可以将这些回调配置为普通 Spring Bean,并将它们注入到 Pivotal GemFire 组件中。这比本机 cache.xml
有了显著的改进,后者提供的配置选项相对有限,并且要求回调实现 Pivotal GemFire 的 Declarable
接口(请参阅 连接 Declarable
组件,了解如何在 Spring 容器中仍然使用 Declarables
)。
此外,诸如 Spring Tool Suite (STS) 之类的 IDE 为 Spring XML 命名空间提供了出色的支持,包括代码完成、弹出注释和实时验证。
5.2. 使用核心命名空间
为了简化配置,Spring Data for Pivotal GemFire 为配置核心 Pivotal GemFire 组件提供了一个专用的 XML 命名空间。可以使用 Spring 的标准 <bean>
定义直接配置 Bean。但是,所有 Bean 属性都通过 XML 命名空间公开,因此使用原始 Bean 定义几乎没有好处。
有关 Spring 中基于 XML 架构的配置的详细信息,请参阅 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 也会在本地解析架构,因为它包含在 Spring Data for Pivotal GemFire 库中。 |
4 | 使用 gfe 前缀的 XML 命名空间的示例声明。 |
你可以将默认命名空间从
|
5.3. 使用数据访问命名空间
除了核心 XML 命名空间 (gfe
) 之外,Pivotal GemFire 的 Spring Data 还提供了一个数据访问 XML 命名空间 (gfe-data
),其主要目的是简化 Pivotal GemFire 客户端应用程序的开发。此命名空间目前包含对 Pivotal GemFire 存储库 和函数 执行 的支持,以及一个 <datasource>
标记,它提供了一种连接到 Pivotal GemFire 集群的便捷方式。
5.3.1. 连接到 Pivotal GemFire 的简单方法
对于许多应用程序,使用默认值连接到 Pivotal GemFire 数据网格就足够了。Pivotal GemFire 的 Spring Data <datasource>
标记提供了一种访问数据的方法。数据源创建一个 ClientCache
和连接 Pool
。此外,它查询集群服务器以获取所有现有的根区域,并为每个区域创建一个(空)客户端区域代理。
<gfe-data:datasource>
<locator host="remotehost" port="1234"/>
</gfe-data:datasource>
<datasource>
标记在语法上类似于 <gfe:pool>
。它可以配置一个或多个嵌套的 locator
或 server
元素以连接到现有的数据网格。此外,还支持配置池的所有属性。此配置会自动为连接到定位器的群集成员上定义的每个区域创建客户端区域 bean,以便 Spring Data 映射注释 (GemfireTemplate
) 可以无缝地引用它们,并将它们自动装配到应用程序类中。
当然,你可以显式配置客户端区域。例如,如果你想将数据缓存到本地内存中,如下例所示
<gfe-data:datasource>
<locator host="remotehost" port="1234"/>
</gfe-data:datasource>
<gfe:client-region id="Example" shortcut="CACHING_PROXY"/>
5.4. 配置缓存
要使用 Pivotal GemFire,你需要创建一个新缓存或连接到一个现有缓存。使用当前版本的 Pivotal GemFire,你只能在每个 VM(更严格地说,每个 ClassLoader
)中打开一个缓存。在大多数情况下,缓存只应创建一次。
本部分描述了对等 (P2P) 拓扑和缓存服务器中对等 Cache 成员的创建和配置。Cache 成员还可用于独立应用程序和集成测试。然而,在典型的生产系统中,大多数应用程序进程充当缓存客户端,而是创建 ClientCache 实例。这在 配置 Pivotal GemFire ClientCache 和 客户端区域 部分中进行了描述。
|
具有默认配置的对等 Cache
可使用以下简单声明创建
<gfe:cache/>
在 Spring 容器初始化期间,包含此缓存定义的任何 ApplicationContext
都会注册一个 CacheFactoryBean
,该工厂创建一个名为 gemfireCache
的 Spring bean,该 bean 引用 Pivotal GemFire Cache
实例。此 bean 引用现有的 Cache
,或者如果尚未存在,则引用新创建的 Cache
。由于未指定其他属性,因此新创建的 Cache
会应用默认缓存配置。
所有依赖于 Cache
的 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"/>
在此示例中,如果需要创建缓存,它将使用位于类路径根目录中名为 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 成员能够从定位器检索常见的、基于集群的共享配置。有关更多详细信息,请参阅 Pivotal GemFire 产品文档。 |
5 | 使用 bean 引用的事务侦听器回调声明示例。引用的 bean 必须实现 TransactionListener。可以实现 TransactionListener 来处理与事务相关的事件(例如 afterCommit 和 afterRollback)。 |
6 | 使用内部 bean 声明的事务写入器回调声明示例。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,它提供分布式区域创建服务。 |
9 | 声明 JNDI 绑定,以便在 Pivotal GemFire 事务中登记外部 DataSource。 |
启用 PDX 序列化
前面的示例包括许多与 Pivotal GemFire 的增强序列化框架 PDX 相关的属性。虽然 PDX 的完整讨论超出了本参考指南的范围,但请务必注意,PDX 是通过注册 PdxSerializer
启用的,该注册通过设置 pdx-serializer
属性来指定。
Pivotal GemFire 提供了一个使用 Java 反射的实现类 (org.apache.geode.pdx.ReflectionBasedAutoSerializer
)。但是,开发人员通常会提供自己的实现。该属性的值仅仅是实现 PdxSerializer
接口的 Spring Bean 的引用。
有关序列化支持的更多信息,请参见 使用 Pivotal GemFire 序列化。
启用自动重新连接
设置 <gfe:cache enable-auto-reconnect="[true|false*]>
属性为 true
时,应小心。
通常,只有在使用 Spring Data for Pivotal GemFire 的 XML 命名空间来配置和引导添加到集群的新非应用程序 Pivotal GemFire 服务器时,才应启用“自动重新连接”。换句话说,当使用 Spring Data for Pivotal GemFire 开发和构建碰巧也是 Pivotal GemFire 集群的 peer Cache
成员的 Pivotal GemFire 应用程序时,不应启用“自动重新连接”。
此限制的主要原因是,大多数 Pivotal GemFire 应用程序使用对 Pivotal GemFire Cache
或区域的引用来执行数据访问操作。这些引用由 Spring 容器“注入”到应用程序组件(例如存储库)中,以便应用程序使用。当对等成员被迫与集群中的其他成员断开连接时(可能是因为对等成员变得无响应,或者网络分区将一个或多个对等成员分隔成一个太小而无法作为一个独立分布式系统运行的组),对等成员将关闭,并且所有 Pivotal GemFire 组件引用(缓存、区域和其他)将变为无效。
从根本上说,每个对等成员中的当前强制断开连接处理逻辑会从头开始解构系统。JGroups 堆栈会关闭,分布式系统会进入关闭状态,最后,缓存会关闭。实际上,所有内存引用都会变得陈旧并丢失。
从分布式系统断开连接后,对等成员会进入“重新连接”状态,并定期尝试重新加入分布式系统。如果对等成员重新连接成功,则成员会根据现有成员重建其对分布式系统的“视图”,并接收新的分布式系统 ID。此外,所有缓存、区域和其他 Pivotal GemFire 组件都会重建。因此,所有旧引用(可能已由 Spring 容器注入到应用程序中)现在都已陈旧且不再有效。
Pivotal GemFire 不保证(即使使用 Pivotal GemFire 公共 Java API)应用程序缓存、区域或其他组件引用会通过重新连接操作自动刷新。因此,Pivotal GemFire 应用程序必须注意刷新其自己的引用。
遗憾的是,没有办法收到断开连接事件的通知,也没有办法收到重新连接事件的通知。如果是这种情况,你将有一个清晰的方法来了解何时调用 ConfigurableApplicationContext.refresh()
(如果应用程序甚至适用),这就是为什么不建议对等 Cache
应用程序使用 Pivotal GemFire 的此“功能”。
有关“自动重新连接”的更多信息,请参阅 Pivotal GemFire 的 产品文档。
使用基于群集的配置
Pivotal GemFire 的群集配置服务是一种便捷的方式,任何加入群集的对等成员都可以通过使用定位器维护的共享持久配置来获取群集的“一致视图”。使用基于群集的配置可确保对等成员的配置在成员加入时与 Pivotal GemFire 分布式系统兼容。
Spring Data for Pivotal GemFire 的此功能(将 use-cluster-configuration
属性设置为 true
)的工作方式与 cache-xml-location
属性相同,只是 Pivotal GemFire 配置元数据的来源通过定位器来自网络,而不是驻留在本地文件系统中的本机 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 。一个 property placeholder 会读取一个或多个属性文件,然后在运行时用值替换属性占位符。这样做可以让管理员更改值,而无需触及主应用程序配置。Spring 还提供 SpEL 和一个 environment abstraction 来支持从主代码库中将特定于环境的属性外化,从而简化跨多台机器的部署。
|
为了避免初始化问题,Spring Data for Pivotal GemFire 启动的 CacheServer 在 Spring 容器完全初始化之后启动。这样做可以让声明性定义的潜在区域、侦听器、写入器或实例化器在服务器开始接受连接之前完全初始化并注册。在以编程方式配置这些元素时请记住这一点,因为服务器可能在组件之前启动,因此客户端无法立即看到它们。
|
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
成员相反,缓存客户端通过一个池连接到远程缓存服务器。默认情况下,将创建一个池来连接到运行在 localhost
上并侦听端口 40404
的服务器。默认池由所有客户端区域使用,除非该区域配置为使用特定池。
可以使用 pool
元素定义池。此客户端池可用于直接为单个实体或整个缓存配置与服务器的连接,方法是通过一个或多个定位器。
例如,要自定义 client-cache
使用的默认池,开发人员需要定义一个池并将其连接到缓存定义,如下例所示
<beans>
<gfe:client-cache id="myCache" pool-name="myPool"/>
<gfe:pool id="myPool" subscription-enabled="true">
<gfe:locator host="${gemfire.locator.host}" port="${gemfire.locator.port}"/>
</gfe:pool>
</beans>
<client-cache>
元素还具有 ready-for-events
属性。如果将该属性设置为 true
,则客户端缓存初始化将包括对 ClientCache.readyForEvents()
的调用。
客户端区域 更详细地介绍了客户端配置。
Pivotal GemFire 的 DEFAULT 池和 Spring Data for Pivotal GemFire 池定义
如果 Pivotal GemFire ClientCache
仅为本地,则不需要池定义。例如,可以定义以下内容
<gfe:client-cache/>
<gfe:client-region id="Example" shortcut="LOCAL"/>
在这种情况下,“Example”区域为 LOCAL
,并且不会在客户端和服务器之间分发数据。因此,不需要池。对于任何客户端本地区域,都是如此,如 Pivotal GemFire 的 ClientRegionShortcut
(所有 LOCAL_*
快捷方式)定义的那样。
但是,如果客户端区域是服务器端区域的(缓存)代理,则需要一个池。在这种情况下,有几种方法可以定义和使用池。
当 ClientCache
、池和基于代理的区域全部定义但未明确标识时,Spring Data for Pivotal GemFire 会自动解析引用,如下例所示
<gfe:client-cache/>
<gfe:pool>
<gfe:locator host="${geode.locator.host}" port="${geode.locator.port}"/>
</gfe:pool>
<gfe:client-region id="Example" shortcut="PROXY"/>
在前面的示例中,ClientCache
被标识为 gemfireCache
,池被标识为 gemfirePool
,客户端区域被标识为“Example”。但是,ClientCache
从 gemfirePool
初始化 Pivotal GemFire 的 DEFAULT
池,并且客户端区域在客户端和服务器之间分发数据时使用 gemfirePool
。
基本上,Spring Data for Pivotal GemFire 将前面的配置解析为以下内容
<gfe:client-cache id="gemfireCache" pool-name="gemfirePool"/>
<gfe:pool id="gemfirePool">
<gfe:locator host="${geode.locator.host}" port="${geode.locator.port}"/>
</gfe:pool>
<gfe:client-region id="Example" cache-ref="gemfireCache" pool-name="gemfirePool" shortcut="PROXY"/>
Pivotal GemFire 仍然创建一个名为 DEFAULT
的池。Spring Data for Pivotal GemFire 导致 DEFAULT
池从 gemfirePool
初始化。在定义了多个池并且客户端区域使用单独的池或根本不声明池的情况下,这样做很有用。
考虑以下内容
<gfe:client-cache pool-name="locatorPool"/>
<gfe:pool id="locatorPool">
<gfe:locator host="${geode.locator.host}" port="${geode.locator.port}"/>
</gfe:pool>
<gfe:pool id="serverPool">
<gfe:server host="${geode.server.host}" port="${geode.server.port}"/>
</gfe:pool>
<gfe:client-region id="Example" pool-name="serverPool" shortcut="PROXY"/>
<gfe:client-region id="AnotherExample" shortcut="CACHING_PROXY"/>
<gfe:client-region id="YetAnotherExample" shortcut="LOCAL"/>
在此设置中,Pivotal GemFire client-cache
DEFAULT
池从 locatorPool
初始化,如 pool-name
属性指定的。没有 Spring Data for Pivotal GemFire 定义的 gemfirePool
,因为两个池都已明确标识(命名)——分别是 locatorPool
和 serverPool
。
“Example”区域明确引用并专门使用 serverPool
。AnotherExample
区域使用 Pivotal GemFire 的 DEFAULT
池,该池再次根据客户端缓存 bean 定义的 pool-name
属性从 locatorPool
配置。
最后,YetAnotherExample
区域不使用池,因为它为 LOCAL
。
AnotherExample 区域将首先查找名为 gemfirePool 的池 bean,但这需要定义一个匿名池 bean(即 <gfe:pool/> )或一个明确命名为 gemfirePool 的池 bean(例如,<gfe:pool id="gemfirePool"/> )。
|
如果我们将 locatorPool 的名称更改为 gemfirePool 或使池 bean 定义为匿名,则它将与前面的配置产生相同的效果。
|
5.5. 配置区域
需要一个区域来存储和检索缓存中的数据。org.apache.geode.cache.Region
是一个扩展 java.util.Map
的接口,并使用熟悉的键值语义启用基本数据访问。Region
接口连接到需要它的应用程序类中,因此实际的区域类型与编程模型分离。通常,每个区域都与一个域对象相关联,类似于关系数据库中的表。
Pivotal GemFire 实现以下类型的区域
-
REPLICATE - 数据在定义区域的群集中的所有缓存成员之间复制。这提供了非常高的读取性能,但写入需要更长的时间来执行复制。
-
PARTITION - 数据被分区到定义区域的群集中的许多缓存成员之间的存储桶(分片)中。这提供了较高的读取和写入性能,并且适用于对于单个节点而言过大的大型数据集。
-
本地 - 数据仅存在于本地节点上。
-
客户端 - 从技术上讲,客户端区域是一个本地区域,充当一个代理,用于访问群集中缓存服务器上托管的复制或分区区域。它可以保存本地创建或获取的数据。或者,它可以为空。本地更新会同步到缓存服务器。此外,客户端区域可以订阅事件,以便与访问同一服务器区域的远程进程发起的更改保持最新(同步)。
有关各种区域类型及其功能以及配置选项的更多信息,请参阅 Pivotal GemFire 关于区域类型的文档。
5.5.1. 使用外部配置的区域
要引用已在 Pivotal GemFire 本机 cache.xml
文件中配置的区域,请使用 lookup-region
元素。只需使用 name
属性声明目标区域名称。例如,要为名为 Orders
的现有区域声明一个标识为 ordersRegion
的 bean 定义,可以使用以下 bean 定义
<gfe:lookup-region id="ordersRegion" name="Orders"/>
如果未指定 name
,则 bean 的 id
将用作区域的名称。上面的示例变为
<!-- lookup for a Region called 'Orders' -->
<gfe:lookup-region id="Orders"/>
如果区域不存在,将抛出初始化异常。要配置新区域,请继续执行以下相应的步骤。 |
在前面的示例中,由于没有明确定义缓存名称,因此使用了默认命名约定(gemfireCache
)。或者,可以使用 cache-ref
属性引用缓存 bean
<gfe:cache id="myCache"/>
<gfe:lookup-region id="ordersRegion" name="Orders" cache-ref="myCache"/>
lookup-region
允许您检索现有的预配置区域,而无需公开区域语义或设置基础设施。
5.5.2. 自动区域查找
当您在 <gfe:cache>
元素上使用 cache-xml-location
属性时,auto-region-lookup
允许您将 Pivotal GemFire 本机 cache.xml
文件中定义的所有区域导入到 Spring ApplicationContext
中。
例如,考虑以下 cache.xml
文件
<?xml version="1.0" encoding="UTF-8"?>
<cache xmlns="https://geode.apache.org/schema/cache"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://geode.apache.org/schema/cache https://geode.apache.org/schema/cache/cache-1.0.xsd"
version="1.0">
<region name="Parent" refid="REPLICATE">
<region name="Child" refid="REPLICATE"/>
</region>
</cache>
您可以按如下方式导入前面的 cache.xml
文件
<gfe:cache cache-xml-location="cache.xml"/>
然后,您可以使用 <gfe:lookup-region>
元素(例如,<gfe:lookup-region id="Parent"/>
)将特定区域引用为 Spring 容器中的 bean,或者您可以选择通过使用以下方式导入 cache.xml
中定义的所有区域
<gfe:auto-region-lookup/>
Pivotal GemFire 的 Spring Data 会自动为 cache.xml
中定义的所有 Pivotal GemFire 区域创建 bean,这些区域尚未使用显式 <gfe:lookup-region>
bean 声明显式添加到 Spring 容器中。
必须意识到 Spring Data for Pivotal GemFire 使用 Spring BeanPostProcessor 在创建并初始化缓存之后对其进行后处理,以确定在 Pivotal GemFire 中定义的区域,并将其作为 bean 添加到 Spring ApplicationContext
中。
你可以注入这些“自动查找”的区域,就像注入在 Spring ApplicationContext
中定义的任何其他 bean 一样,但有一个例外:你可能需要定义一个与“gemfireCache”bean 的 depends-on
关联,如下所示
package example;
import ...
@Repository("appDao")
@DependsOn("gemfireCache")
public class ApplicationDao extends DaoSupport {
@Resource(name = "Parent")
private Region<?, ?> parent;
@Resource(name = "/Parent/Child")
private Region<?, ?> child;
...
}
前面的示例仅在你使用 Spring 的 component-scan
功能时适用。
如果你使用 Spring XML 配置声明组件,则需要执行以下操作
<bean class="example.ApplicationDao" depends-on="gemfireCache"/>
这样做可确保在使用 <gfe:auto-region-lookup>
元素时,在具有自动装配引用的任何组件之前创建 Pivotal GemFire 缓存和 cache.xml
中定义的所有区域。
5.5.3. 配置区域
Spring Data for Pivotal GemFire 通过以下元素提供对配置任何类型区域的全面支持
-
LOCAL 区域:
<local-region>
-
PARTITION 区域:
<partitioned-region>
-
REPLICATE 区域:
<replicated-region>
-
Client 区域:
<client-region>
有关 区域类型 的全面描述,请参见 Pivotal GemFire 文档。
通用区域属性
下表列出了所有区域类型可用的属性
名称 | 值 | 说明 |
---|---|---|
cache-ref |
Pivotal GemFire Cache bean 引用 |
定义 Pivotal GemFire Cache 的 bean 的名称(默认情况下为“gemfireCache”)。 |
cloning-enabled |
布尔值(默认值: |
当为 |
close |
布尔值(默认值: |
确定是否应在关闭时关闭区域。 |
concurrency-checks-enabled |
布尔值(默认值: |
确定成员是否执行检查以对分布式区域的并发或无序更新提供一致的处理。 |
data-policy |
请参见 Pivotal GemFire 的 数据策略。 |
区域的数据策略。请注意,并非所有数据策略都受每个区域类型支持。 |
销毁 |
布尔值(默认值: |
确定是否在关闭时销毁区域。 |
磁盘存储引用 |
已配置磁盘存储的名称。 |
对通过 |
磁盘同步 |
布尔值(默认值: |
确定磁盘存储写入是否同步。 |
ID |
任何有效的 bean 名称。 |
如果未指定 |
如果存在则忽略 |
布尔值(默认值: |
如果区域已在缓存中存在,则忽略此 bean 定义,从而导致查找。 |
忽略 JTA |
布尔值(默认值: |
确定此区域是否参与 JTA(Java 事务 API)事务。 |
索引更新类型 |
|
确定在创建条目时是否同步或异步更新索引。 |
初始容量 |
整数(默认:16) |
区域条目的数量的初始内存分配。 |
键约束 |
任何有效的完全限定 Java 类名称。 |
预期的键类型。 |
负载因子 |
浮点数(默认:.75) |
设置用于存储区域条目的底层 |
名称 |
任何有效的区域名称。 |
区域的名称。如果未指定,则假定为 |
持久 |
*布尔值(默认: |
确定区域是否将条目持久保存到本地磁盘(磁盘存储)。 |
快捷方式 |
请参阅 https://gemfire-98-javadocs.docs.pivotal.io//org/apache/geode/cache/RegionShortcut.html |
此区域的 |
统计信息 |
布尔值(默认值: |
确定区域是否报告统计信息。 |
模板 |
区域模板的名称。 |
对通过其中一个 |
值约束 |
任何有效的完全限定 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 引用约定
|
CacheLoaders 和 CacheWriters
与 cache-listener
类似,XML 命名空间提供 cache-loader
和 cache-writer
元素,以注册这些 Pivotal GemFire 组件,用于一个 Region。
当缓存未命中时,会调用 CacheLoader
,以便从外部数据源(如数据库)加载条目。在创建或更新条目之前,会调用 CacheWriter
,以便将条目同步到外部数据源。主要区别在于 Pivotal GemFire 最多支持每个区域使用一个 CacheLoader
和 CacheWriter
实例。但是,可以使用任一声明样式。
以下示例声明了一个同时具有 CacheLoader
和 CacheWriter
的区域
<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 区域也可以进行压缩,以减少 JVM 内存消耗和压力,从而可能避免全局 GC。为区域启用压缩后,存储在区域内存中的所有值都将被压缩,而键和索引将保持未压缩状态。将新值放入区域时会对其进行压缩,并且从区域读回所有值时会自动对其进行解压缩。将值持久化到磁盘或通过网络发送到其他对等成员或客户端时,不会对其进行压缩。
以下示例显示了一个启用了压缩的区域
<beans>
<gfe:replicated-region id="exampleReplicateRegionWithCompression">
<gfe:compressor>
<bean class="org.apache.geode.compression.SnappyCompressor"/>
</gfe:compressor>
</gfe:replicated-region>
</beans>
有关 区域压缩 的更多信息,请参阅 Pivotal GemFire 的文档。
5.5.5. 堆外
Pivotal GemFire 区域还可以配置为将区域值存储在堆外内存中,堆外内存是 JVM 内存的一部分,不受垃圾回收 (GC) 的影响。通过避免昂贵的 GC 周期,您的应用程序可以将更多时间花在重要的事情上,例如处理请求。
使用堆外内存非常简单,只需声明要使用的内存量,然后启用您的区域使用堆外内存,如下面的配置所示
<util:properties id="gemfireProperties">
<prop key="off-heap-memory-size">200G</prop>
</util:properties>
<gfe:cache properties-ref="gemfireProperties"/>
<gfe:partitioned-region id="ExampleOffHeapRegion" off-heap="true"/>
您可以通过使用 <gfe:cache>
元素设置以下 Pivotal GemFire 配置属性来控制堆外内存管理的其他方面:
<gfe:cache critical-off-heap-percentage="90" eviction-off-heap-percentage"80"/>
Pivotal GemFire 的 ResourceManager
将使用这两个阈值(critical-off-heap-percentage
和 eviction-off-heap-percentage
)来更有效地管理堆外内存,就像 JVM 在管理堆内存时所做的那样。Pivotal GemFire ResourceManager
将通过驱逐旧数据来防止缓存消耗过多的堆外内存。如果堆外管理器无法跟上,则 ResourceManager
会拒绝向缓存添加数据,直到堆外内存管理器释放出足够数量的内存。
有关 管理堆和非堆内存 的更多信息,请参阅 Pivotal GemFire 的文档。
具体来说,请阅读章节 管理非堆内存。
5.5.6. 子区域
Spring Data for Pivotal GemFire 还支持子区域,允许按层次关系排列区域。
例如,Pivotal GemFire 允许使用 /Customer/Address
区域和不同的 /Employee/Address
区域。此外,子区域可能拥有自己的子区域和配置。子区域不会从其父区域继承属性。区域类型可以混合匹配,但须遵守 Pivotal GemFire 约束。子区域自然被声明为区域的子元素。子区域的 name
属性是简单名称。前面的示例可能配置如下
<beans>
<gfe:replicated-region name="Customer">
<gfe:replicated-region name="Address"/>
</gfe:replicated-region>
<gfe:replicated-region name="Employee">
<gfe:replicated-region name="Address"/>
</gfe:replicated-region>
</beans>
请注意,子区域不允许使用 Monospaced ([id])
属性。子区域使用 bean 名称创建(在本例中分别为 /Customer/Address 和 /Employee/Address)。因此,它们可以通过使用区域的完整路径名称注入到其他应用程序 bean(例如 GemfireTemplate
)中。区域的完整路径名也应在 OQL 查询字符串中使用。
5.5.7. 区域模板
Spring Data for Pivotal GemFire 还支持区域模板。
此功能允许开发人员定义一次通用的区域配置和属性,并在 Spring ApplicationContext
中声明的许多区域 bean 定义中重用配置。
Spring Data for Pivotal GemFire 在其命名空间中包含五个区域模板标记
标记名称 | 说明 |
---|---|
|
定义通用的通用区域属性。在 XML 命名空间中扩展 |
|
定义通用的“本地”区域属性。在 XML 命名空间中扩展 |
|
定义通用的“分区”区域属性。在 XML 命名空间中扩展 |
|
定义通用“REPLICATE”区域属性。在 XML 命名空间中扩展 |
|
定义通用“Client”区域属性。在 XML 命名空间中扩展 |
除了标签之外,具体的 <gfe:*-region>
元素(以及抽象 <gfe:*-region-template>
元素)具有一个 template
属性,用于定义区域模板,区域从中继承其配置。区域模板甚至可以从其他区域模板继承。
以下示例显示了一个可能的配置
<beans>
<gfe:async-event-queue id="AEQ" persistent="false" parallel="false" dispatcher-threads="4">
<gfe:async-event-listener>
<bean class="example.AeqListener"/>
</gfe:async-event-listener>
</gfe:async-event-queue>
<gfe:region-template id="BaseRegionTemplate" initial-capacity="51" load-factor="0.85" persistent="false" statistics="true"
key-constraint="java.lang.Long" value-constraint="java.lang.String">
<gfe:cache-listener>
<bean class="example.CacheListenerOne"/>
<bean class="example.CacheListenerTwo"/>
</gfe:cache-listener>
<gfe:entry-ttl timeout="600" action="DESTROY"/>
<gfe:entry-tti timeout="300 action="INVLIDATE"/>
</gfe:region-template>
<gfe:region-template id="ExtendedRegionTemplate" template="BaseRegionTemplate" load-factor="0.55">
<gfe:cache-loader>
<bean class="example.CacheLoader"/>
</gfe:cache-loader>
<gfe:cache-writer>
<bean class="example.CacheWriter"/>
</gfe:cache-writer>
<gfe:async-event-queue-ref bean="AEQ"/>
</gfe:region-template>
<gfe:partitioned-region-template id="PartitionRegionTemplate" template="ExtendedRegionTemplate"
copies="1" load-factor="0.70" local-max-memory="1024" total-max-memory="16384" value-constraint="java.lang.Object">
<gfe:partition-resolver>
<bean class="example.PartitionResolver"/>
</gfe:partition-resolver>
<gfe:eviction type="ENTRY_COUNT" threshold="8192000" action="OVERFLOW_TO_DISK"/>
</gfe:partitioned-region-template>
<gfe:partitioned-region id="TemplateBasedPartitionRegion" template="PartitionRegionTemplate"
copies="2" local-max-memory="8192" persistent="true" total-buckets="91"/>
</beans>
区域模板也适用于子区域。请注意,“TemplateBasedPartitionRegion”扩展了“PartitionRegionTemplate”,后者扩展了“ExtendedRegionTemplate”,后者扩展了“BaseRegionTemplate”。在后续继承的区域 bean 定义中定义的属性和子元素会覆盖父元素中的内容。
模板的工作原理
Pivotal GemFire 的 Spring Data 在解析 Spring ApplicationContext
配置元数据时应用区域模板,因此,必须按继承顺序声明区域模板。换句话说,必须在子模板之前定义父模板。这样做可确保应用正确的配置,尤其是在元素属性或子元素被覆盖时。
同样重要的是要记住,区域类型只能从其他类似类型的区域继承。例如,<gfe:replicated-region> 无法从 <gfe:partitioned-region-template> 继承。
|
区域模板是单继承的。 |
关于区域、子区域和查找的注意事项
以前,Pivotal GemFire XML 命名空间中的 replicated-region
、partitioned-region
、local-region
和 client-region
元素的基本属性之一是在尝试创建区域之前先执行查找。这样做是为了防止区域已存在,如果区域在导入的 Pivotal GemFire 本机 cache.xml
配置文件中定义,则会出现这种情况。因此,先执行查找以避免任何错误。这是设计使然,可能会更改。
此行为已更改,现在默认行为是先创建区域。如果区域已存在,则创建逻辑将快速失败,并抛出适当的异常。但是,与 CREATE TABLE IF NOT EXISTS …
DDL 语法非常相似,Pivotal GemFire 的 Spring Data <gfe:*-region>
XML 命名空间元素现在包含一个 ignore-if-exists
属性,该属性通过在尝试创建区域之前先查找按名称标识的现有区域来恢复旧行为。如果按名称找到现有区域,并且 ignore-if-exists
设置为 true
,则在 Spring 配置中定义的区域 bean 定义将被忽略。
Spring 团队强烈建议仅严格使用 replicated-region 、partitioned-region 、local-region 和 client-region XML 命名空间元素来定义新区域。当这些元素定义的区域已存在,并且区域元素首先执行查找时,可能会出现一个问题,如果你在应用程序配置中为驱逐、过期、订阅等定义了不同的区域语义和行为,则区域定义可能不匹配,并且可能表现出与应用程序所需的行为相反的行为。更糟糕的是,你可能希望将区域定义为分布式区域(例如,PARTITION ),而实际上,现有区域定义仅为本地区域。
|
推荐做法 - 仅使用 replicated-region 、partitioned-region 、local-region 和 client-region XML 命名空间元素来定义新区域。
|
考虑以下本机 Pivotal GemFire cache.xml
配置文件
<?xml version="1.0" encoding="UTF-8"?>
<cache xmlns="https://geode.apache.org/schema/cache"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://geode.apache.org/schema/cache https://geode.apache.org/schema/cache/cache-1.0.xsd"
version="1.0">
<region name="Customers" refid="REPLICATE">
<region name="Accounts" refid="REPLICATE">
<region name="Orders" refid="REPLICATE">
<region name="Items" refid="REPLICATE"/>
</region>
</region>
</region>
</cache>
此外,考虑您可能已将应用程序 DAO 定义如下
public class CustomerAccountDao extends GemDaoSupport {
@Resource(name = "Customers/Accounts")
private Region customersAccounts;
...
}
在此,我们在应用程序 DAO 中注入对 Customers/Accounts
区域的引用。因此,开发人员在 Spring XML 配置元数据中为其中一些或所有这些区域定义 bean 并不罕见,如下所示
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:gfe="https://www.springframework.org/schema/gemfire"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
https://www.springframework.org/schema/gemfire https://www.springframework.org/schema/gemfire/spring-gemfire.xsd
">
<gfe:cache cache-xml-location="classpath:cache.xml"/>
<gfe:lookup-region name="Customers/Accounts"/>
<gfe:lookup-region name="Customers/Accounts/Orders"/>
</beans>
Customers/Accounts
和 Customers/Accounts/Orders
区域在 Spring 容器中分别作为 bean 引用为 Customers/Accounts
和 Customers/Accounts/Orders
。使用 lookup-region
元素和相应语法(前面已描述)的好处在于,它允许您直接引用子区域,而无需为父区域(在本例中为 Customers
)不必要地定义 bean。
考虑以下不良示例,它将配置元数据语法更改为使用嵌套格式
<gfe:lookup-region name="Customers">
<gfe:lookup-region name="Accounts">
<gfe:lookup-region name="Orders"/>
</gfe:lookup-region>
</gfe:lookup-region>
现在考虑另一个不良示例,它使用顶级 replicated-region
元素以及 ignore-if-exists
属性集来首先执行查找
<gfe:replicated-region name="Customers" persistent="true" ignore-if-exists="true">
<gfe:replicated-region name="Accounts" persistent="true" ignore-if-exists="true">
<gfe:replicated-region name="Orders" persistent="true" ignore-if-exists="true"/>
</gfe:replicated-region>
</gfe:replicated-region>
在 Spring ApplicationContext
中定义的区域 bean 包括以下内容:{ "Customers", "/Customers/Accounts", "/Customers/Accounts/Orders" }.
这意味着前面示例中显示的依赖注入引用(即 @Resource(name = "Customers/Accounts")
)现在已损坏,因为实际上没有定义名为 Customers/Accounts
的 bean。因此,您不应如前两个示例中所示配置区域。
Pivotal GemFire 在引用父区域和子区域时很灵活,无论是否带有前导正斜杠。例如,父区域可以引用为 /Customers
或 Customers
,子区域可以引用为 /Customers/Accounts
或 Customers/Accounts
。但是,在根据区域命名 bean 时,Pivotal GemFire 的 Spring 数据非常具体。它始终使用正斜杠 (/) 来表示子区域(例如,/Customers/Accounts
)。
因此,您应使用前面显示的非嵌套 lookup-region
语法,或使用前导正斜杠 (/) 定义直接引用,如下所示
<gfe:lookup-region name="/Customers/Accounts"/>
<gfe:lookup-region name="/Customers/Accounts/Orders"/>
前面的示例中,嵌套的 replicated-region
元素用于引用子区域,显示了前面提到的问题。客户、帐户和订单区域和子区域是持久性的还是非持久性的?它们不是持久性的,因为这些区域在原生 Pivotal GemFire cache.xml
配置文件中定义为 REPLICATE
,并且在缓存 bean 初始化之前存在(一旦处理了 <gfe:cache>
元素)。
5.5.8. 数据驱逐(带溢出)
基于各种约束,每个区域都可以制定驱逐策略,以从内存中驱逐数据。目前,在 Pivotal GemFire 中,驱逐适用于最近最少使用条目(也称为 LRU)。驱逐的条目要么被销毁,要么被分页到磁盘(称为“溢出到磁盘”)。
Spring Data for Pivotal GemFire 支持所有驱逐策略(条目计数、内存和堆使用),用于分区区域、复制区域以及客户端、本地区域,方法是使用嵌套的 eviction
元素。
例如,要配置分区区域,使其在内存大小超过 512 MB 时溢出到磁盘,可以指定以下配置
<gfe:partitioned-region id="examplePartitionRegionWithEviction">
<gfe:eviction type="MEMORY_SIZE" threshold="512" action="OVERFLOW_TO_DISK"/>
</gfe:partitioned-region>
副本不能使用 local destroy 驱逐,因为这会使它们失效。有关更多信息,请参阅 Pivotal GemFire 文档。
|
为溢出配置区域时,应通过 disk-store
元素配置存储,以获得最大的效率。
有关驱逐策略的详细说明,请参阅 Pivotal GemFire 文档中的 驱逐。
5.5.9. 数据过期
Pivotal GemFire 允许您控制条目在缓存中存在多长时间。过期是由经过的时间驱动的,而不是由条目计数或堆或内存使用驱动的。一旦条目过期,就无法再从缓存中访问它。
Pivotal GemFire 支持以下过期类型
-
生存时间 (TTL):对象在最后创建或更新后可在缓存中保留的时间(以秒为单位)。对于条目,计数器在创建和放置操作中设置为零。区域计数器在创建区域时和条目重置其计数器时重置。
-
空闲超时 (TTI):对象在最后一次访问后可在缓存中保留的时间(以秒为单位)。对象的空闲超时计数器在每次重置其 TTL 计数器时重置。此外,每次通过 get 操作或
netSearch
访问条目时,条目的空闲超时计数器都会重置。每当某个条目的空闲超时重置时,区域的空闲超时计数器也会重置。
这些设置可以应用于区域本身或区域中的条目。Spring Data for Pivotal GemFire 提供了 <region-ttl>
、<region-tti>
、<entry-ttl>
和 <entry-tti>
区域子元素来指定超时值和过期操作。
以下示例显示了一个具有过期值的 PARTITION
区域
<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,您可以在各个区域条目值(或者换句话说,直接在应用程序域对象上)定义过期策略和设置。例如,您可以按照以下方式在基于会话的应用程序域对象上定义过期策略
@Expiration(timeout = "1800", action = "INVALIDATE")
public class SessionBasedApplicationDomainObject {
...
}
您还可以通过使用 @IdleTimeoutExpiration
和 @TimeToLiveExpiration
注释分别针对空闲超时 (TTI) 和生存时间 (TTL) 过期在区域条目上指定过期类型特定设置,如下例所示
@TimeToLiveExpiration(timeout = "3600", action = "LOCAL_DESTROY")
@IdleTimeoutExpiration(timeout = "1800", action = "LOCAL_INVALIDATE")
@Expiration(timeout = "1800", action = "INVALIDATE")
public class AnotherSessionBasedApplicationDomainObject {
...
}
如前例所示,当指定了多个过期注释类型时,@IdleTimeoutExpiration
和 @TimeToLiveExpiration
都优先于通用 @Expiration
注释。@IdleTimeoutExpiration
和 @TimeToLiveExpiration
互不覆盖。相反,当配置了不同的区域条目过期策略(例如 TTL 和 TTI)时,它们会相互补充。
所有基于
|
Spring Data for Pivotal GemFire 的 @Expiration
注释支持通过 Pivotal GemFire 的 CustomExpiry
接口实现。有关更多详细信息,请参阅 Pivotal GemFire 关于 配置数据到期 的文档
Spring Data for Pivotal GemFire AnnotationBasedExpiration
类(和 CustomExpiry
实现)负责处理 SDG @Expiration
注释,并在请求时为区域条目到期适当地应用到期策略配置。
要使用 Spring Data for Pivotal GemFire 为特定的 Pivotal GemFire 区域配置,以便适当地将到期策略应用到使用基于 @Expiration
注释的应用程序域对象,您必须
-
使用适当的构造函数或其中一种便捷的工厂方法,在 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
)为存储在区域中的应用程序域对象添加注释,并使用到期策略和自定义设置 -
(可选)在特定应用程序域对象根本未使用 Spring Data for Pivotal GemFire 的
@Expiration
注释添加注释的情况下,但 Pivotal GemFire 区域配置为使用 SDG 的自定义AnnotationBasedExpiration
类来确定存储在区域中的对象的到期策略和设置,您可以通过执行以下操作在AnnotationBasedExpiration
bean 上设置“默认”到期属性
<bean id="defaultExpirationAttributes" class="org.apache.geode.cache.ExpirationAttributes">
<constructor-arg value="600"/>
<constructor-arg value="#{T(org.apache.geode.cache.ExpirationAction).DESTROY}"/>
</bean>
<bean id="ttiExpiration" class="org.springframework.data.gemfire.expiration.AnnotationBasedExpiration"
factory-method="forIdleTimeout">
<constructor-arg ref="defaultExpirationAttributes"/>
</bean>
<gfe:partitioned-region id="Example" persistent="false">
<gfe:custom-entry-tti ref="ttiExpiration"/>
</gfe:partitioned-region>
您可能已经注意到,Spring Data for Pivotal GemFire 的 @Expiration
注释使用 String
作为属性类型,而不是(可能更合适)强类型——例如,'timeout' 的 int
和 SDG 的 ExpirationActionType
的 'action'。这是为什么呢?
嗯,输入 Spring Data for Pivotal GemFire 的其他功能之一,利用 Spring 的核心基础设施来方便配置:属性占位符和 Spring 表达式语言 (SpEL) 表达式。
例如,开发人员可以通过在 @Expiration
注释属性中使用属性占位符来指定到期的 'timeout' 和 'action',如下例所示
@TimeToLiveExpiration(timeout = "${geode.region.entry.expiration.ttl.timeout}"
action = "${geode.region.entry.expiration.ttl.action}")
public class ExampleApplicationDomainObject {
...
}
然后,在您的 Spring XML 配置或 JavaConfig 中,您可以声明以下 bean
<util:properties id="expirationSettings">
<prop key="geode.region.entry.expiration.ttl.timeout">600</prop>
<prop key="geode.region.entry.expiration.ttl.action">INVALIDATE</prop>
...
</util:properties>
<context:property-placeholder properties-ref="expirationProperties"/>
当多个应用程序域对象可能共享类似的到期策略以及您希望将配置外部化时,这很方便。
但是,您可能希望根据正在运行的系统的状态来进行更动态的到期配置。实际上,SpEL 的强大功能在这里发挥作用,并且是推荐的方法。您不仅可以引用 Spring 容器中的 bean 并访问 bean 属性、调用方法等,而且到期 'timeout' 和 'action' 的值可以是强类型的。考虑以下示例(它基于前面的示例)
<util:properties id="expirationSettings">
<prop key="geode.region.entry.expiration.ttl.timeout">600</prop>
<prop key="geode.region.entry.expiration.ttl.action">#{T(org.springframework.data.gemfire.expiration.ExpirationActionType).DESTROY}</prop>
<prop key="geode.region.entry.expiration.tti.action">#{T(org.apache.geode.cache.ExpirationAction).INVALIDATE}</prop>
...
</util:properties>
<context:property-placeholder properties-ref="expirationProperties"/>
然后,在应用程序域对象上,您可以定义超时和操作,如下所示
@TimeToLiveExpiration(timeout = "@expirationSettings['geode.region.entry.expiration.ttl.timeout']"
action = "@expirationSetting['geode.region.entry.expiration.ttl.action']")
public class ExampleApplicationDomainObject {
...
}
您可以想象,“expirationSettings”bean 可能比 `java.util.Properties` 的简单实例更有趣、更有用的对象。在前面的示例中,`properties` 元素(`expirationSettings`)使用 SpEL 将操作值基于实际的 `ExpirationAction` 枚举类型,如果枚举类型发生更改,则会快速导致已识别的故障。
例如,所有这些都在 Spring Data for Pivotal GemFire 测试套件中得到了演示和测试。有关更多详细信息,请参阅 源代码。
5.5.10. 数据持久性
区域可以是持久的。Pivotal GemFire 确保将放入已配置为持久性的区域中的所有数据写入磁盘,以便在下次重新创建区域时可以恢复。这样做可以让数据在机器或进程故障后甚至在有序关机和 Pivotal GemFire 数据节点随后重新启动后恢复。
要使用 Spring Data for Pivotal GemFire 启用持久性,请在任何 `<*-region>` 元素上将 `persistent` 属性设置为 `true`,如下例所示
<gfe:partitioned-region id="examplePersitentPartitionRegion" persistent="true"/>
还可以通过设置 `data-policy` 属性来配置持久性。要这样做,请将该属性的值设置为 Pivotal GemFire 的 DataPolicy 设置 之一,如下例所示
<gfe:partitioned-region id="anotherExamplePersistentPartitionRegion" data-policy="PERSISTENT_PARTITION"/>
`DataPolicy` 必须与区域类型匹配,并且还必须与 `persistent` 属性(如果也显式设置)一致。如果将 `persistent` 属性设置为 `false` 但指定了持久性 `DataPolicy`(例如 `PERSISTENT_REPLICATE` 或 `PERSISTENT_PARTITION`),则会引发初始化异常。
为了在持久化区域时获得最大的效率,您应该通过 `disk-store` 元素配置存储。`DiskStore` 通过使用 `disk-store-ref` 属性进行引用。此外,区域可以同步或异步执行磁盘写入。以下示例显示了一个同步 `DiskStore`
<gfe:partitioned-region id="yetAnotherExamplePersistentPartitionRegion" persistent="true"
disk-store-ref="myDiskStore" disk-synchronous="true"/>
这将在 配置 DiskStore 中进一步讨论。
5.5.11. 订阅策略
Pivotal GemFire 允许配置对等 (P2P) 事件消息传递,以控制区域接收的条目事件。Spring Data for Pivotal GemFire 提供了 <gfe:subscription/>
子元素,用于将 REPLICATE
和 PARTITION
区域的订阅策略设置为 ALL
或 CACHE_CONTENT
。以下示例显示了一个将订阅策略设置为 CACHE_CONTENT
的区域
<gfe:partitioned-region id="examplePartitionRegionWithCustomSubscription">
<gfe:subscription type="CACHE_CONTENT"/>
</gfe:partitioned-region>
5.5.12. 本地区域
Spring Data for Pivotal GemFire 提供了一个专用的 local-region
元素,用于创建本地区域。顾名思义,本地区域是独立的,这意味着它们不与任何其他分布式系统成员共享数据。除此之外,所有常见的区域配置选项都适用。
以下示例显示了一个最小的声明(同样,该示例依赖于 Spring Data for Pivotal GemFire XML 命名空间命名约定来连接缓存)
<gfe:local-region id="exampleLocalRegion"/>
在前面的示例中,创建了一个本地区域(如果不存在同名区域)。区域的名称与 Bean ID(exampleLocalRegion
)相同,并且 Bean 假设存在名为 gemfireCache
的 Pivotal GemFire 缓存。
5.5.13. 复制区域
常见的区域类型之一是 REPLICATE
区域或“副本”。简而言之,当区域配置为 REPLICATE
时,托管该区域的每个成员都会在本地存储该区域条目的副本。对 REPLICATE
区域的任何更新都会分发到该区域的所有副本。创建副本时,它会经历一个初始化阶段,在此阶段它会发现其他副本并自动复制所有条目。当一个副本正在初始化时,您仍然可以使用其他副本。
所有常见的配置选项都可用于 REPLICATE 区域。Spring Data for Pivotal GemFire 提供了一个 replicated-region
元素。以下示例显示了一个最小的声明
<gfe:replicated-region id="exampleReplica"/>
有关更多详细信息,请参阅 Pivotal GemFire 关于分布式和复制区域的文档。
5.5.14. 分区区域
Spring Data for Pivotal GemFire XML 命名空间还支持 PARTITION
区域。
引用 Pivotal GemFire 文档
“分区区域是一个区域,其中数据在托管该区域的对等服务器之间进行划分,以便每个对等服务器存储数据的一个子集。在使用分区区域时,应用程序会显示一个逻辑视图,该视图看起来像一个包含区域中所有数据的单一映射。对该映射的读取或写入将透明地路由到托管操作目标条目的对等服务器。Pivotal GemFire 将哈希码的域划分为存储桶。每个存储桶都分配给一个特定的对等服务器,但可以随时将其重新分配到集群中的另一个对等服务器,以提高整个集群中资源的利用率。”
使用 partitioned-region
元素创建 PARTITION
区域。它的配置选项与 replicated-region
类似,但添加了分区特定功能,如冗余副本数、总最大内存、存储桶数、分区解析器等。
以下示例展示了如何设置具有两个冗余副本的 PARTITION
区域
<gfe:partitioned-region id="examplePartitionRegion" copies="2" total-buckets="17">
<gfe:partition-resolver>
<bean class="example.PartitionResolver"/>
</gfe:partition-resolver>
</gfe:partitioned-region>
有关更多详细信息,请参阅 Pivotal GemFire 关于 分区区域 的文档。
分区区域属性
下表提供了 PARTITION
区域特定配置选项的快速概览。这些选项是除了 前面 描述的通用区域配置选项之外的选项。
名称 | 值 | 说明 |
---|---|---|
copies |
0..4 |
每个分区的高可用性副本数。默认情况下,不创建副本,这意味着没有冗余。每个副本以额外的存储为代价提供额外的备份。 |
colocated-with |
有效区域名称 |
此新创建的 |
local-max-memory |
正整数 |
此 进程中区域使用的最大内存量(以兆字节为单位)。 |
total-max-memory |
任何整数值 |
所有 进程中区域使用的最大内存量(以兆字节为单位)。 |
partition-listener |
bean 名称 |
此区域用于处理分区事件的 |
partition-resolver |
bean 名称 |
此区域用于自定义分区的 |
recovery-delay |
任何长整数值 |
在另一个成员崩溃后,现有成员在满足冗余之前等待的延迟(以毫秒为单位)。-1(默认值)表示在故障后不恢复冗余。 |
startup-recovery-delay |
任何长整数值 |
新成员在满足冗余之前等待的延迟(以毫秒为单位)。-1 表示添加新成员不会触发冗余恢复。默认情况下,在添加新成员时立即恢复冗余。 |
5.5.15. 客户端区域
Pivotal GemFire 支持各种部署拓扑来管理和分发数据。Pivotal GemFire 拓扑主题超出了本文档的范围。但是,简单回顾一下,Pivotal GemFire 的支持拓扑可以分类为:对等 (p2p)、客户端-服务器和广域网 (WAN)。在后两种配置中,通常声明连接到缓存服务器的客户端区域。
Spring Data for Pivotal GemFire 通过其 client-cache 元素为每种配置提供专门支持:client-region
和 pool
。顾名思义,client-region
定义客户端区域,而 pool
定义各种客户端区域使用和共享的连接池。
以下示例显示典型的客户端区域配置
<bean id="myListener" class="example.CacheListener"/>
<!-- client Region using the default SDG gemfirePool Pool -->
<gfe:client-region id="Example">
<gfe:cache-listener ref="myListener"/>
</gfe:client-region>
<!-- client Region using its own dedicated Pool -->
<gfe:client-region id="AnotherExample" pool-name="myPool">
<gfe:cache-listener ref="myListener"/>
</gfe:client-region>
<!-- Pool definition -->
<gfe:pool id="myPool" subscription-enabled="true">
<gfe:locator host="remoteHost" port="12345"/>
</gfe:pool>
与其他区域类型一样,client-region
支持 CacheListener
实例以及 CacheLoader
和 CacheWriter
。它还需要一个连接 Pool
来连接到一组定位器或服务器。每个客户端区域都可以有自己的 Pool
,或者它们可以共享同一个 Pool
。如果未指定 Pool
,则将使用“DEFAULT”Pool
。
在前面的示例中,Pool 使用定位器进行配置。定位器是一个单独的进程,用于发现分布式系统中的缓存服务器和对等数据成员,建议用于生产系统。还可以使用 server 元素将 Pool 配置为直接连接到一个或多个缓存服务器。
|
有关在客户端和 Pool
上设置的选项的完整列表,请参阅 Spring Data for Pivotal GemFire 架构(“Spring Data for Pivotal GemFire 架构”)和 Pivotal GemFire 关于 客户端-服务器配置 的文档。
客户端兴趣
为了最大程度地减少网络流量,每个客户端可以分别定义自己的“兴趣”策略,向 Pivotal GemFire 指示它实际需要的数据。在 Spring Data for Pivotal GemFire 中,可以为每个客户端区域分别定义“兴趣”。支持基于键和基于正则表达式的兴趣类型。
以下示例显示基于键和基于正则表达式的 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
订阅队列会在客户端断开连接时保持。当客户端重新连接时,客户端会收到在客户端与集群中的服务器断开连接时发生的任何事件。
在客户端中定义的每个连接 Pool
都会在集群中的服务器上维护一个订阅队列,并且该 Pool
也已“启用”订阅。订阅队列用于存储(并可能合并)发送到客户端的事件。如果订阅队列是 durable
,它会在客户端会话(即连接)之间持续存在,可能长达指定超时时间。如果客户端在给定时间范围内没有返回,则客户端 Pool 订阅队列将被销毁,以减少集群中服务器上的资源消耗。如果订阅队列不是 durable
,它会在客户端断开连接时立即被销毁。您需要决定您的客户端是否应该接收断开连接期间发生的事件,或者是否仅需要在重新连接后接收最新事件。
receive-values
属性指示是否接收创建和更新事件的条目值。如果为 true
,则会接收值。如果为 false
,则仅接收失效事件。
最后,“result-policy` 是以下枚举值:KEYS
、KEYS_VALUE
和 NONE
。默认值为 KEYS_VALUES
。result-policy
控制客户端首次连接以初始化本地缓存时的初始转储,实质上是使用与兴趣策略匹配的所有条目的事件为客户端设置种子。
如前所述,如果没有在 Pool
上启用订阅,客户端端的兴趣注册作用不大。事实上,在未启用订阅的情况下尝试进行兴趣注册是一种错误。以下示例显示了如何执行此操作
<gfe:pool ... subscription-enabled="true">
...
</gfe:pool>
除了 subscription-enabled
,您还可以设置 subscription-ack-interval
、subscription-message-tracking-timeout
和 subscription-redundancy
。subscription-redundancy
用于控制集群中的服务器应维护多少个订阅队列副本。如果冗余大于 1,并且“主”订阅队列(即服务器)宕机,则“辅助”订阅队列将接管,防止客户端在 HA 场景中错过事件。
除了 Pool
设置之外,服务器端的区域使用其他属性 enable-subscription-conflation
来控制发送到客户端的事件的合并。这还可以进一步最大程度地减少网络流量,并且在应用程序仅关心条目的最新值的情况下很有用。但是,当应用程序保留发生的事件的时间序列时,合并会阻碍该用例。默认值为 false
。以下示例显示了服务器上的区域配置,客户端包含一个相应的客户端 [CACHING_]PROXY
区域,对该服务器区域中的键感兴趣
<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 支持在区域中缓存 JSON 文档,以及使用 Pivotal GemFire OQL(对象查询语言)查询存储的 JSON 文档。JSON 文档在内部存储为 PdxInstance 类型,使用 JSONFormatter 类执行到 JSON 文档(作为 String
)的转换。
Spring Data for Pivotal GemFire 提供 <gfe-data:json-region-autoproxy/>
元素,以启用 AOP 组件来建议适当的代理区域操作,该操作有效地封装了 JSONFormatter
,从而让您的应用程序直接使用 JSON 字符串。
此外,写入 JSON 配置区域的 Java 对象会自动使用 Jackson 的 ObjectMapper
转换为 JSON。当读回这些值时,它们将作为 JSON 字符串返回。
默认情况下,<gfe-data:json-region-autoproxy/>
对所有区域执行转换。要将此功能应用于选定的区域,请在 region-refs
属性中提供区域 Bean ID 的逗号分隔列表。其他属性包括 pretty-print
标志(默认为 false
)和 convert-returned-collections
。
此外,默认情况下,getAll()
和 values()
区域操作的结果会针对已配置区域进行转换。这是通过在本地内存中创建并行数据结构来完成的。对于大型集合,这可能会产生很大的开销,因此如果您想禁用这些区域操作的自动转换,请将 convert-returned-collections
设置为 false
。
某些区域操作(特别是那些使用 Pivotal GemFire 专有 Region.Entry 的操作,例如:entries(boolean) 、entrySet(boolean) 和 getEntry() 类型)不适用于 AOP 建议。此外,entrySet() 方法(返回 Set<java.util.Map.Entry<?, ?>> )也不受影响。
|
以下示例配置显示如何设置 pretty-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 允许在区域数据上创建索引(有时也称为指标),以提高 OQL(对象查询语言)查询的性能。
在 Pivotal GemFire 的 Spring Data 中,索引使用 index
元素声明,如下例所示
<gfe:index id="myIndex" expression="someField" from="/SomeRegion" type="HASH"/>
在 Pivotal GemFire 的 Spring Data XML 架构(也称为 SDG XML 命名空间)中,index
bean 声明不绑定到区域,这与 Pivotal GemFire 的本机 cache.xml
不同。相反,它们是顶级元素,类似于 <gfe:cache>
元素。这使您可以对任何区域声明任意数量的索引,无论它们是刚创建的还是已经存在的——这比 Pivotal GemFire 的本机 cache.xml
格式有了显著的改进。
Index
必须有名称。您可以使用 name
属性为 Index
指定显式名称。否则,index
bean 定义的 bean 名称(即 id
属性的值)将用作 Index
名称。
expression
和 from
子句构成了 Index
的主要组件,它们标识要索引的数据(即 from
子句中标识的区域)以及用于索引数据的条件(即 expression
)。expression
应基于应用程序域对象字段在用于查询和查找存储在区域中的对象的应用程序定义的 OQL 查询的谓词中使用。
考虑以下示例,其中有一个 lastName
属性
@Region("Customers")
class Customer {
@Id
Long id;
String lastName;
String firstName;
...
}
现在考虑以下示例,其中有一个应用程序定义的 SDG 存储库用于查询 Customer
对象
interface CustomerRepository extends GemfireRepository<Customer, Long> {
Customer findByLastName(String lastName);
...
}
SDG 存储库查找器/查询方法导致生成并运行以下 OQL 语句
SELECT * FROM /Customers c WHERE c.lastName = '$1'
因此,您可能希望使用类似于以下语句创建 Index
<gfe:index id="myIndex" name="CustomersLastNameIndex" expression="lastName" from="/Customers" type="HASH"/>
from
子句必须引用有效存在的区域,并且这就是 Index
应用于区域的方式。这并非 Pivotal GemFire 的 Spring Data 所特有。这是 Pivotal GemFire 的一项功能。
Index
type
可以是 Pivotal GemFire 的 Spring Data 定义的 IndexType
枚举中定义的三个枚举值之一: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 架构。
有关 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
。所有“已定义”索引都将在 Spring ApplicationContext
“刷新”时一次性创建,或者换句话说,当 Spring 容器发布 ContextRefreshedEvent
时。Spring Data for Pivotal GemFire 将自己注册为侦听 ContextRefreshedEvent
的 ApplicationListener
。触发时,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
。因此,在编写使用查询提示的 OQL 查询语句时应小心,尤其是引用被忽略的应用程序 Index
的查询提示。需要更改这些查询提示。
当抛出 IndexNameConflictException
并且 ignoreIfExists
设置为 true
(或 <gfe:index ignore-if-exists="true">
)时,此 index
bean 定义或声明本来会创建的 Index
也将被忽略,并且再次返回“现有”Index
,就像抛出 IndexExistsException
时一样。
但是,当抛出 IndexNameConflictException
时,返回现有的 Index
并忽略应用程序对 Index
的定义存在更大的风险。对于 IndexNameConflictException
,虽然冲突索引的名称相同,但定义可能不同。这种情况可能会对特定于应用程序的 OQL 查询产生影响,在这些查询中,您会假定索引是专门针对应用程序数据访问模式和查询定义的。但是,如果同名索引的定义不同,则可能并非如此。因此,您应该验证您的 Index
名称。
SDG 尽最大努力在被忽略的 Index 与现有 Index 的定义显著不同时通知用户。但是,为了让 SDG 完成此操作,它必须能够找到现有的 Index ,而查找方法是使用 Pivotal GemFire API(唯一可用的方法)。
|
Override
行为
当抛出 IndexExistsException
且 override
设置为 true
(或 <gfe:index override="true">
)时,Index
将有效重命名。请记住,当存在多个具有相同定义但名称不同的索引时,将抛出 IndexExistsExceptions
。
Spring Data for Pivotal GemFire 只能通过使用 Pivotal GemFire 的 API 来完成此操作,方法是先删除现有的 Index
,然后使用新名称重新创建 Index
。删除或后续创建调用可能会失败。无法以原子方式执行这两个操作,如果任一操作失败,则无法回滚此联合操作。
但是,如果成功,则会遇到与 ignoreIfExists
选项之前相同的问题。任何使用查询提示的现有 OQL 查询语句,这些提示通过名称引用旧 Index
,都必须更改。
当抛出 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(用于 Pivotal GemFire 的 Spring Data、Pivotal GemFire 集群配置、Pivotal GemFire 原生 cache.xml
、API 等)时。您绝对应该优先选择一种配置方法并坚持使用它。
但是,IndexNameConflictException
在什么时候抛出?
一种特殊情况是在 PARTITION
区域 (PR) 上定义的 Index
。当在 PARTITION
区域(例如 X
)上定义 Index
时,Pivotal GemFire 会将 Index
定义(和名称)分发到集群中也托管同一 PARTITION
区域(即“X”)的其他对等成员。将此 Index
定义分发到对等成员,以及对等成员随后创建此 Index
,是基于需要了解(即,由托管同一 PR 的对等成员)异步执行的。
在此时间段内,Pivotal GemFire 可能无法识别这些待处理的 PR Indexes
,例如通过调用 QueryService.getIndexes()
和 QueryService.getIndexes(:Region)
,甚至通过 QueryService.getIndex(:Region, indexName:String)
。
因此,SDG 或其他 Pivotal GemFire 缓存客户端应用程序(不涉及 Spring)了解情况的唯一方法是尝试创建 Index
。如果它因 IndexNameConflictException
甚至 IndexExistsException
而失败,则应用程序知道存在问题。这是因为 QueryService
Index
创建会等待待处理的 Index
定义,而其他 Pivotal GemFire API 调用不会。
无论如何,SDG 会尽力而为,并尝试告知您已发生或正在发生的事情,并告诉您纠正措施。鉴于所有 Pivotal GemFire QueryService.createIndex(..)
方法都是同步阻塞操作,因此在抛出这些索引类型异常后,Pivotal GemFire 的状态应该是保持一致且可访问的。因此,SDG 可以根据您的配置检查系统状态并采取相应措施。
在所有其他情况下,SDG 采用快速失败策略。
5.7. 配置 DiskStore
用于 Pivotal GemFire 的 Spring Data 支持通过 disk-store
元素配置和创建 DiskStore
,如下例所示
<gfe:disk-store id="Example" auto-compact="true" max-oplog-size="10"
queue-size="50" time-interval="9999">
<gfe:disk-dir location="/disk/location/one" max-size="20"/>
<gfe:disk-dir location="/disk/location/two" max-size="20"/>
</gfe:disk-store>
DiskStore
实例由区域用于文件系统持久备份和驱逐条目的溢出,以及 WAN 网关的持久备份。多个 Pivotal GemFire 组件可以共享同一 DiskStore
。此外,可以为单个 DiskStore
定义多个文件系统目录,如前例所示。
请参阅 Pivotal GemFire 文档,以全面了解 持久性和溢出 以及 DiskStore
实例上的配置选项。
5.8. 配置快照服务
Spring Data for Pivotal GemFire 通过使用 Pivotal GemFire 的快照服务 支持缓存和区域快照。开箱即用的快照服务支持提供多种便捷功能,以简化 Pivotal GemFire 的 缓存 和 区域 快照服务 API 的使用。
正如 Pivotal GemFire 文档 所述,快照使您可以保存缓存数据并稍后重新加载,这对于在环境之间移动数据非常有用,例如从生产环境移动到暂存或测试环境,以便在受控环境中重现与数据相关的问题。您可以将 Spring Data for Pivotal GemFire 的快照服务支持与 Spring 的 bean 定义配置文件 结合使用,以根据需要加载特定于环境的快照数据。
Spring Data for Pivotal GemFire 对 Pivotal GemFire 快照服务支持从 <gfe-data:snapshot-service>
元素开始,该元素来自 <gfe-data>
XML 命名空间。
例如,您可以定义要加载和保存的缓存范围快照,方法是使用几个快照导入和数据导出定义,如下所示
<gfe-data:snapshot-service id="gemfireCacheSnapshotService">
<gfe-data:snapshot-import location="/absolute/filesystem/path/to/import/fileOne.snapshot"/>
<gfe-data:snapshot-import location="relative/filesystem/path/to/import/fileTwo.snapshot"/>
<gfe-data:snapshot-export
location="/absolute/or/relative/filesystem/path/to/export/directory"/>
</gfe-data:snapshot-service>
您可以根据需要定义任意数量的导入和导出。您可以仅定义导入或仅定义导出。文件位置和目录路径可以是绝对的,也可以相对于 Spring Data for Pivotal GemFire 应用程序(即 JVM 进程的工作目录)。
前面的示例非常简单,在这种情况下定义的快照服务引用具有默认名称 gemfireCache
的 Pivotal GemFire 缓存实例(如 配置缓存 中所述)。如果您将缓存 bean 定义命名为除默认名称之外的其他名称,则可以使用 cache-ref
属性按名称引用缓存 bean,如下所示
<gfe:cache id="myCache"/>
...
<gfe-data:snapshot-service id="mySnapshotService" cache-ref="myCache">
...
</gfe-data:snapshot-service>
您还可以通过指定 region-ref
属性来为特定区域定义快照服务,如下所示
<gfe:partitioned-region id="Example" persistent="false" .../>
...
<gfe-data:snapshot-service id="gemfireCacheRegionSnapshotService" region-ref="Example">
<gfe-data:snapshot-import location="relative/path/to/import/example.snapshot/>
<gfe-data:snapshot-export location="/absolute/path/to/export/example.snapshot/>
</gfe-data:snapshot-service>
当指定 region-ref
属性时,Spring Data for Pivotal GemFire 的 SnapshotServiceFactoryBean
会将 region-ref
属性值解析为 Spring 容器中定义的区域 Bean,并创建一个 RegionSnapshotService
。快照导入和导出定义的功能相同。但是,location
必须引用导出中的文件。
Pivotal GemFire 严格要求导入的快照文件在引用之前实际存在。对于导出,Pivotal GemFire 会创建快照文件。如果导出的快照文件已存在,则数据会被覆盖。 |
Spring Data for Pivotal GemFire 在 <gfe-data:snapshot-service> 元素上包含一个 suppress-import-on-init 属性,以禁止配置的快照服务尝试在初始化时将数据导入缓存或区域。这样做很有用,例如,当从一个区域导出的数据用于馈送另一个区域的导入时。
|
5.8.1. 快照位置
对于基于缓存的快照服务(即 CacheSnapshotService
),您通常会向它传递一个包含要加载的所有快照文件(而不是单个快照文件)的目录,正如 CacheSnapshotService
API 中重载的 load
方法所示。
当然,您可以使用重载的 load(:File[], :SnapshotFormat, :SnapshotOptions) 方法来具体指定要加载到 Pivotal GemFire 缓存中的快照文件。
|
但是,Spring Data for Pivotal GemFire 认识到典型的开发人员工作流可能是从一个环境中提取和导出数据到多个快照文件中,将所有这些文件打包,然后方便地将 ZIP 文件移动到另一个环境中进行导入。
因此,Spring Data for Pivotal GemFire 允许您在导入时为基于 cache
的快照服务指定一个 jar 或 zip 文件,如下所示
<gfe-data:snapshot-service id="cacheBasedSnapshotService" cache-ref="gemfireCache">
<gfe-data:snapshot-import location="/path/to/snapshots.zip"/>
</gfe-data:snapshot-service>
Spring Data for Pivotal GemFire 会方便地提取提供的 ZIP 文件,并将其视为目录导入(加载)。
5.8.2. 快照过滤器
定义多个快照导入和导出的真正强大之处是通过使用快照过滤器实现的。快照过滤器实现了 Pivotal GemFire 的 SnapshotFilter
接口,用于过滤区域条目,以便在导入时将其包含到区域中,并在导出时将其包含到快照中。
Spring Data for Pivotal GemFire 允许您在导入和导出时使用快照过滤器,方法是使用 filter-ref
属性或匿名嵌套 Bean 定义,如下例所示
<gfe:cache/>
<gfe:partitioned-region id="Admins" persistent="false"/>
<gfe:partitioned-region id="Guests" persistent="false"/>
<bean id="activeUsersFilter" class="example.gemfire.snapshot.filter.ActiveUsersFilter/>
<gfe-data:snapshot-service id="adminsSnapshotService" region-ref="Admins">
<gfe-data:snapshot-import location="/path/to/import/users.snapshot">
<bean class="example.gemfire.snapshot.filter.AdminsFilter/>
</gfe-data:snapshot-import>
<gfe-data:snapshot-export location="/path/to/export/active/admins.snapshot" filter-ref="activeUsersFilter"/>
</gfe-data:snapshot-service>
<gfe-data:snapshot-service id="guestsSnapshotService" region-ref="Guests">
<gfe-data:snapshot-import location="/path/to/import/users.snapshot">
<bean class="example.gemfire.snapshot.filter.GuestsFilter/>
</gfe-data:snapshot-import>
<gfe-data:snapshot-export location="/path/to/export/active/guests.snapshot" filter-ref="activeUsersFilter"/>
</gfe-data:snapshot-service>
此外,您可以使用 ComposableSnapshotFilter
类来表示更复杂的快照过滤器。此类实现了 Pivotal GemFire 的 SnapshotFilter 接口以及 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. 快照事件
默认情况下,用于 Pivotal GemFire 的 Spring Data 在启动时使用 Pivotal GemFire 的快照服务导入数据,并在关闭时导出数据。但是,您可能希望在 Spring 应用程序中触发基于事件的定期快照,用于导入或导出。
为此,用于 Pivotal GemFire 的 Spring Data 定义了两个其他 Spring 应用程序事件,分别扩展了 Spring 的 ApplicationEvent
类用于导入和导出:ImportSnapshotApplicationEvent
和 ExportSnapshotApplicationEvent
。
这两个应用程序事件可以针对整个 Pivotal GemFire 缓存或针对各个 Pivotal GemFire 区域。这些类中的构造函数接受可选的区域路径名(例如 /Example
)以及零个或多个 SnapshotMetadata
实例。
SnapshotMetadata
数组将覆盖由 <gfe-data:snapshot-import>
和 <gfe-data:snapshot-export>
子元素定义的快照元数据,这些子元素用于快照应用程序事件未明确提供 SnapshotMetadata
的情况。每个单独的 SnapshotMetadata
实例可以定义其自己的 location
和 filters
属性。
在 Spring ApplicationContext
中定义的所有快照服务 bean 都接收导入和导出快照应用程序事件。但是,只有匹配的快照服务 bean 才会处理导入和导出事件。
如果定义的快照服务 bean 是 RegionSnapshotService
,并且其区域引用(由 region-ref
属性确定)与快照应用程序事件指定的区域路径名匹配,则基于区域的 [Import|Export]SnapshotApplicationEvent
匹配。
基于缓存的 [Import|Export]SnapshotApplicationEvent
(即没有区域路径名的快照应用程序事件)触发所有快照服务 bean(包括任何 RegionSnapshotService
bean)分别执行导入或导出。
您可以使用 Spring 的 ApplicationEventPublisher
接口从您的应用程序触发导入和导出快照应用程序事件,如下所示
@Component
public class ExampleApplicationComponent {
@Autowired
private ApplicationEventPublisher eventPublisher;
@Resource(name = "Example")
private Region<?, ?> example;
public void someMethod() {
...
File dataSnapshot = new File(System.getProperty("user.dir"), "/path/to/export/data.snapshot");
SnapshotFilter myFilter = ...;
SnapshotMetadata exportSnapshotMetadata =
new SnapshotMetadata(dataSnapshot, myFilter, null);
ExportSnapshotApplicationEvent exportSnapshotEvent =
new ExportSnapshotApplicationEvent(this, example.getFullPath(), exportSnapshotMetadata)
eventPublisher.publishEvent(exportSnapshotEvent);
...
}
}
在前面的示例中,只有 /Example
区域的快照服务 bean 会拾取并处理导出事件,将经过筛选的“/Example”区域的数据保存到应用程序工作目录的子目录中的 data.snapshot
文件中。
使用 Spring 应用程序事件和消息传递子系统是保持应用程序松散耦合的好方法。您还可以使用 Spring 的 调度 服务定期触发快照应用程序事件。
5.9. 配置函数服务
Spring Data for Pivotal GemFire 提供 注释 支持,用于实现、注册和执行 Pivotal GemFire 函数。
Spring Data for Pivotal GemFire 还提供 XML 命名空间支持,用于注册 Pivotal GemFire 函数 以进行远程函数执行。
有关函数执行框架的更多信息,请参阅 Pivotal GemFire 的 文档。
Pivotal GemFire 函数被声明为 Spring bean,并且必须实现 org.apache.geode.cache.execute.Function
接口或扩展 org.apache.geode.cache.execute.FunctionAdapter
。
命名空间使用熟悉的模式来声明函数,如下例所示
<gfe:function-service>
<gfe:function>
<bean class="example.FunctionOne"/>
<ref bean="function2"/>
</gfe:function>
</gfe:function-service>
<bean id="function2" class="example.FunctionTwo"/>
5.10. 配置 WAN 网关
WAN 网关提供了一种跨地理位置同步 Pivotal GemFire 分布式系统的方法。Spring Data for Pivotal GemFire 提供 XML 命名空间支持,用于配置 WAN 网关,如下例所示。
5.10.1. Pivotal GemFire 7.0 中的 WAN 配置
在以下示例中,GatewaySenders
是通过向区域添加子元素(gateway-sender
和 gateway-sender-ref
)为 PARTITION
区域配置的。GatewaySender
可以注册 EventFilters
和 TransportFilters
。
以下示例还显示了 AsyncEventQueue
的示例配置,它还必须自动连接到区域(未显示)
<gfe:partitioned-region id="region-with-inner-gateway-sender" >
<gfe:gateway-sender remote-distributed-system-id="1">
<gfe:event-filter>
<bean class="org.springframework.data.gemfire.example.SomeEventFilter"/>
</gfe:event-filter>
<gfe:transport-filter>
<bean class="org.springframework.data.gemfire.example.SomeTransportFilter"/>
</gfe:transport-filter>
</gfe:gateway-sender>
<gfe:gateway-sender-ref bean="gateway-sender"/>
</gfe:partitioned-region>
<gfe:async-event-queue id="async-event-queue" batch-size="10" persistent="true" disk-store-ref="diskstore"
maximum-queue-memory="50">
<gfe:async-event-listener>
<bean class="example.AsyncEventListener"/>
</gfe:async-event-listener>
</gfe:async-event-queue>
<gfe:gateway-sender id="gateway-sender" remote-distributed-system-id="2">
<gfe:event-filter>
<ref bean="event-filter"/>
<bean class="org.springframework.data.gemfire.example.SomeEventFilter"/>
</gfe:event-filter>
<gfe:transport-filter>
<ref bean="transport-filter"/>
<bean class="org.springframework.data.gemfire.example.SomeTransportFilter"/>
</gfe:transport-filter>
</gfe:gateway-sender>
<bean id="event-filter" class="org.springframework.data.gemfire.example.AnotherEventFilter"/>
<bean id="transport-filter" class="org.springframework.data.gemfire.example.AnotherTransportFilter"/>
在 GatewaySender
的另一端是对应的 GatewayReceiver
,用于接收网关事件。GatewayReceiver
还可以配置 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 架构进行配置,并使用 gfe-data
XML 架构进行数据访问。有关更多详细信息,请参阅“使用 Spring 容器引导 Pivotal GemFire”。
从 SDG 2.0 开始,基于注释的配置模型还不支持 Pivotal GemFire 的 WAN 组件和拓扑的配置。 |
与 Spring Boot 一样,Spring Data for Pivotal GemFire 的基于注释的配置模型被设计为一种基于约定优于配置的方法,用于使用 Pivotal GemFire。事实上,这种基于注释的配置模型的灵感来自 Spring Boot 以及其他几个 Spring 和 Spring Data 项目。
通过遵循约定,所有注释都为所有配置属性提供合理且明智的默认值。给定注释属性的默认值直接对应于 Pivotal GemFire 中为相同配置属性提供的默认值。
目的是让您能够通过在 Spring @Configuration
或 @SpringBootApplication
类中声明适当的注释来启用 Pivotal GemFire 功能或嵌入式服务,而无需不必要地配置大量属性才能使用该功能或服务。
同样,快速且轻松地入门是主要目标。
但是,如果您需要,可以自定义 Pivotal GemFire 的配置元数据和行为,而 Spring Data for Pivotal GemFire 的基于注释的配置会悄然退出。您只需指定您希望调整的配置属性即可。此外,正如我们稍后将在本文档中看到的那样,有几种方法可以使用注释来配置 Pivotal GemFire 功能或嵌入式服务。
您可以在 org.springframework.data.gemfire.config.annotation
包中找到所有新的 SDG Java Annotations
。
6.2. 使用 Spring 配置 Pivotal GemFire 应用程序
与所有通过使用 @SpringBootApplication
对应用程序类进行注释来启动的 Spring Boot 应用程序一样,Spring Boot 应用程序可以通过声明三个主要注释中的任何一个轻松地成为 Pivotal GemFire 缓存应用程序
-
@ClientCacheApplication
-
@PeerCacheApplication
-
@CacheServerApplication
在使用 Pivotal GemFire 时,这三个注释是 Spring 应用程序开发人员的起点。
为了实现这些注释背后的意图,您必须了解可以使用 Pivotal GemFire 创建两种类型的缓存实例:客户端缓存或对等缓存。
您可以使用 ClientCache
实例将 Spring Boot 应用程序配置为 Pivotal GemFire 缓存客户端,该实例可以与用于管理应用程序数据的现有 Pivotal GemFire 服务器集群进行通信。在使用 Pivotal GemFire 时,客户端-服务器拓扑是最常用的系统架构,您可以通过使用 @ClientCacheApplication
对其进行注释,轻松地使您的 Spring Boot 应用程序成为具有 ClientCache
实例的缓存客户端。
或者,Spring Boot 应用程序可以是 Pivotal GemFire 集群的对等成员。也就是说,应用程序本身只是管理数据的服务器集群中的另一台服务器。当您使用 @PeerCacheApplication
为应用程序类添加注释时,Spring Boot 应用程序将创建一个“嵌入式”对等 Cache
实例。
通过扩展,对等缓存应用程序也可以用作 CacheServer
,允许缓存客户端连接并在服务器上执行数据访问操作。这是通过使用 @CacheServerApplication
注释应用程序类来完成的,而不是使用 @PeerCacheApplication
,后者创建一个对等 Cache
实例以及允许缓存客户端连接的 CacheServer
。
Pivotal GemFire 服务器默认情况下不一定是一个缓存服务器。也就是说,服务器不一定只是因为它是一台服务器就被设置为服务缓存客户端。Pivotal GemFire 服务器可以是集群的成员节点(数据节点),管理数据而不服务任何客户端,而集群中的其他对等成员确实被设置为除了管理数据之外还服务客户端。还可以将集群中的某些对等成员设置为非数据节点,称为 数据访问器,它不存储数据,但充当代理来为客户端提供 CacheServers 服务。Pivotal GemFire 支持许多不同的拓扑和集群排列,但超出了本文档的范围。
|
例如,如果您想创建一个 Spring Boot 缓存客户端应用程序,请从以下内容开始
ClientCache
应用程序@SpringBootApplication
@ClientCacheApplication
class ClientApplication { .. }
或者,如果您想使用嵌入式对等 Cache
实例创建一个 Spring Boot 应用程序,其中您的应用程序将是 Pivotal GemFire 形成的集群(分布式系统)的服务器和对等成员,请从以下内容开始
Cache
应用程序@SpringBootApplication
@PeerCacheApplication
class ServerApplication { .. }
或者,您可以使用 @CacheServerApplication
注释来代替 @PeerCacheApplication
,以创建嵌入式对等 Cache
实例以及在 localhost
上运行的 CacheServer
,监听默认缓存服务器端口 40404
,如下所示
CacheServer
的基于 Spring 的 Pivotal GemFire 嵌入式对等 Cache
应用程序@SpringBootApplication
@CacheServerApplication
class ServerApplication { .. }
6.3. 客户端/服务器应用程序详细信息
客户端可以通过多种方式连接到 Pivotal GemFire 集群中的服务器并与其通信。最常见且推荐的方法是使用 Pivotal GemFire 定位器。
缓存客户端可以连接到 Pivotal GemFire 集群中的一个或多个定位器,而不是直接连接到 CacheServer 。与直接 CacheServer 连接相比,使用定位器连接的优势在于,定位器可提供有关客户端连接到的集群的元数据。此元数据包括诸如哪些服务器包含感兴趣的数据或哪些服务器负载最少的信息。客户端 Pool 与定位器结合使用,还可在 CacheServer 崩溃时提供故障转移功能。通过在客户端 Pool 中启用 PARTITION 区域 (PR) 单跳功能,客户端将直接路由到包含客户端请求和所需数据的服务器。
|
定位器也是集群中的对等成员。定位器实际上构成了 Pivotal GemFire 节点集群。也就是说,由定位器连接的所有节点都是集群中的对等节点,新成员使用定位器加入集群并查找其他成员。 |
默认情况下,当创建 ClientCache
实例时,Pivotal GemFire 会设置一个连接到在 localhost
上运行的 CacheServer
的“DEFAULT”Pool
,该 CacheServer
侦听端口 40404
。CacheServer
侦听端口 40404
,接受所有系统 NIC 上的连接。您无需执行任何特殊操作即可使用客户端-服务器拓扑。只需使用 @CacheServerApplication
为您的服务器端 Spring Boot 应用程序添加注释,并使用 @ClientCacheApplication
为您的客户端 Spring Boot 应用程序添加注释,即可开始使用。
如果您愿意,甚至可以使用 Gfsh 的 start server
命令启动服务器。无论如何启动 Spring Boot @ClientCacheApplication
,它仍然可以连接到服务器。但是,您可能更愿意使用 Spring Data for Pivotal GemFire 方法来配置和启动服务器,因为正确添加注释的 Spring Boot 应用程序类更直观且更容易调试。
作为应用程序开发人员,您无疑希望自定义 Pivotal GemFire 设置的“DEFAULT”Pool
,以便可能连接到一个或多个定位器,如下例所示
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 强制执行的)。
|
您还可以配置其他 Pool
实例(除了使用 @ClientCacheApplication
注释创建 ClientCache
实例时 Pivotal GemFire 提供的“DEFAULT”Pool
之外),方法是使用 @EnablePool
和 @EnablePools
注释。
@EnablePools 是一个复合注释,它聚合单个类上的多个嵌套 @EnablePool 注释。Java 8 及更早版本不允许在单个类上声明多个相同类型的注释。
|
以下示例使用 @EnablePool
和 @EnablePools
注释
Pools
的基于 Spring 的 Pivotal GemFire ClientCache
应用程序@SpringBootApplication
@ClientCacheApplication(logLevel = "info")
@EnablePool(name = "VenusPool", servers = @Server(host = "venus", port = 48484),
min-connections = 50, max-connections = 200, ping-internal = 15000,
prSingleHopEnabled = true, readTimeout = 20000, retryAttempts = 1,
subscription-enable = true)
@EnablePools(pools = {
@EnablePool(name = "SaturnPool", locators = @Locator(host="skullbox", port=20668),
subsription-enabled = true),
@EnablePool(name = "NeptunePool", severs = {
@Server(host = "saturn", port = 41414),
@Server(host = "neptune", port = 42424)
}, min-connections = 25))
})
class ClientApplication { .. }
name
属性是 @EnablePool
注释的唯一必需属性。正如我们稍后将看到的,name
属性的值对应于 Spring 容器中创建的 Pool
bean 的名称以及用于引用相应配置属性的名称。它也是 Pivotal GemFire 注册和使用的 Pool
的名称。
类似地,在服务器上,您可以配置客户端可以连接到的多个 CacheServers
,如下所示
CacheServers
的基于 Spring 的 Pivotal GemFire CacheServer
应用程序@SpringBootApplication
@CacheSeverApplication(logLevel = "info", autoStartup = true, maxConnections = 100)
@EnableCacheServer(name = "Venus", autoStartup = true,
hostnameForClients = "venus", port = 48484)
@EnableCacheServers(servers = {
@EnableCacheServer(name = "Saturn", hostnameForClients = "saturn", port = 41414),
@EnableCacheServer(name = "Neptune", hostnameForClients = "neptune", port = 42424)
})
class ServerApplication { .. }
与 @EnablePools 类似,@EnableCacheServers 是一个复合注释,用于聚合单个类上的多个 @EnableCacheServer 注释。同样,Java 8 及更早版本不允许在单个类上声明多个相同类型的注释。
|
一个细心的读者可能会注意到,在所有情况下,您都为所有主机名、端口和面向配置的注释属性指定了硬编码值。当应用程序得到提升并部署到不同的环境(例如从 DEV 到 QA 到 STAGING 到 PROD)时,这不是理想的。
下一节介绍如何在运行时处理确定的动态配置。
6.4. 配置和引导定位器
除了 Pivotal GemFire Cache 应用程序,您还可以创建 Pivotal GemFire Locator 应用程序。
Pivotal GemFire Locator 是一个 JVM 进程,它允许节点作为对等成员加入 Pivotal GemFire 集群。定位器还使客户端能够发现集群中的服务器。定位器向客户端提供元数据,以在集群中的成员之间均匀地平衡负载,启用单跳数据访问操作以及其他操作。
对定位器的完整讨论超出了本文档的范围。建议读者阅读 Pivotal GemFire 用户指南,以了解有关定位器及其在集群中的角色的更多详细信息。
若要配置和引导独立定位器进程,请执行以下操作
@SpringBootApplication
@LocatorApplication(port = 12345)
class LocatorApplication { ... }
您可以在集群中启动多个定位器。唯一的要求是成员名称在集群中必须唯一。使用 @LocatorApplication
注释的 name
属性相应地命名集群中的成员定位器。或者,您可以在 Spring Boot 的 application.properties
中设置 spring.data.gemfire.locator.name
属性。
此外,如果您在同一台机器上派生多个定位器,则必须确保每个定位器在唯一端口上启动。设置 port
注释属性或 spring.data.gemfire.locator.port
属性。
然后,您可以在集群中启动 1 个或多个 Pivotal GemFire 对等缓存成员,由定位器或定位器加入,同样使用 Spring 配置和引导,如下所示
CacheServer
应用程序由 localhost
上的定位器加入,端口为 12345
@SpringBootApplication
@CacheServerApplication(locators = "localhost[12345]")
class ServerApplication { ... }
同样,您可以启动任意多个 ServerApplication
类,由我们上面的定位器加入。您只需确保成员名称唯一即可。
@LocatorApplication
用于配置和引导独立的 Pivotal GemFire 定位器应用程序进程。此进程只能是定位器,不能是其他任何内容。如果您尝试使用缓存实例启动定位器,SDG 将抛出错误。
如果您想同时启动缓存实例和嵌入式定位器,则应改用 @EnableLocator
注释。
在开发过程中启动嵌入式定位器很方便。但是,强烈建议您在生产环境中运行独立定位器进程以实现高可用性。如果集群中的所有定位器都关闭,则集群将保持完整,但是,没有新成员能够加入集群,这对于线性扩展以满足需求非常重要。
有关更多详细信息,请参阅 配置嵌入式定位器 一节。
6.5. 使用 Configurers
的运行时配置
在设计基于注释的配置模型时的另一个目标是在注释属性中保留类型安全性。例如,如果配置属性可以表示为 int
(例如端口号),则属性的类型应为 int
。
不幸的是,这不利于运行时的动态且可解析的配置。
Spring 的一个更精细的功能是在配置 Spring 容器中的 Bean 时,能够在配置元数据的属性或属性中使用属性占位符和 SpEL 表达式。但是,这将要求所有注释属性都为类型 String
,从而放弃类型安全性,这是不可取的。
因此,Spring Data for Pivotal GemFire 借用了 Spring 中另一个常用的模式,Configurers
。Spring Web MVC 中提供了许多不同的 Configurer
接口,包括 org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer
。
Configurers
设计模式使应用程序开发人员能够在启动时收到一个回调,以自定义组件或 Bean 的配置。框架会回调到用户提供的代码,以在运行时调整配置。此模式更常见的用途之一是根据应用程序的运行时环境提供条件配置。
Spring Data for Pivotal GemFire 提供了几个 Configurer
回调接口,用于在运行时自定义基于注释的配置元数据的不同方面,在注释创建的 Spring 管理 Bean 初始化之前
-
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 组件(例如,使用 ClientCacheFactoryBean
创建和配置 ClientCache
实例)的 FactoryBean
的引用。
SDG FactoryBeans 是 SDG 公共 API 的一部分,如果您没有提供这种新的基于注解的配置模型,那么您将在 Spring 的 基于 Java 的容器配置 中使用它。实际上,注解本身正在使用这些相同的 FactoryBeans 进行配置。因此,从本质上讲,注解是一个提供额外抽象层以方便使用的外观。
|
鉴于 Configurer
可以像任何其他 POJO 一样声明为常规 Bean 定义,您可以组合不同的 Spring 配置选项,例如将 Spring 配置文件与同时使用属性占位符和 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 的重要性(除了在某些情况下需要这样做)。这样做可以从 XML、属性和 Java 中引用 Spring 容器中的 bean。甚至可以将基于注释定义的 bean 注入到应用程序类中,无论出于何种目的,如下面的示例所示
@Component
class MyApplicationComponent {
@Resource(name = "Saturn")
CacheServer saturnCacheServer;
...
}
同样,命名基于注释定义的 bean 允许你编写一个 Configurer
来定制一个特定的“命名”bean,因为 beanName
是传递给回调的 2 个参数之一。
通常,关联的注释属性属性采用两种形式:“命名”属性和“未命名”属性。
以下示例显示了这样的安排
spring.data.gemfire.cache.server.bind-address=10.105.20.1
spring.data.gemfire.cache.server.Venus.bind-address=10.105.20.2
spring.data.gemfire.cache.server.Saturn...
spring.data.gemfire.cache.server.Neptune...
虽然上面有三个命名的 CacheServers
,但还有一个未命名的 CacheServer
属性,为该属性的任何未指定值提供默认值,即使对于“命名”的 CacheServers
也是如此。因此,虽然“Venus”设置并覆盖了自己的 bind-address
,但“Saturn”和“Neptune”从“未命名”的 spring.data.gemfire.cache.server.bind-address
属性继承。
请参阅注释的 Javadoc,了解哪些注释属性支持基于属性的配置,以及它们是否支持“命名”属性而不是默认的“未命名”属性。
6.6.1. Properties
的 Properties
按照通常的 Spring 方式,你甚至可以用其他 Properties
来表示 Properties
,无论这是否是通过以下示例显示在 application.properties
文件中设置的嵌套属性
spring.data.gemfire.cache.server.port=${gemfire.cache.server.port:40404}
以下示例显示在 Java 中设置的嵌套属性
@Bean
CacheServerConfigurer cacheServerPortConfigurer(
@Value("${gemfire.cache.server.port:${some.other.property:40404}}")
int cacheServerPort) {
...
}
属性占位符嵌套可以任意深。 |
6.7. 配置嵌入式服务
Pivotal GemFire 提供了启动应用程序所需的许多不同的嵌入式服务的能力,具体取决于用例。
6.7.1. 配置嵌入式定位器
如前所述,Pivotal GemFire 定位器由客户端用于连接到集群中的服务器并查找服务器。此外,加入现有集群的新成员使用定位器来查找其对等方。
对于应用程序开发人员来说,在他们开发 Spring Boot 和 Spring Data for Pivotal GemFire 应用程序时,启动一个由两个或三个 Pivotal GemFire 服务器组成的小集群通常很方便。与其启动一个单独的定位器进程,你可以使用 @EnableLocator
对 Spring Boot @CacheServerApplication
类进行注释,如下所示
CacheServer
应用程序运行嵌入式定位器@SpringBootApplication
@CacheServerApplication
@EnableLocator
class ServerApplication { .. }
@EnableLocator
注解在 Spring Pivotal GemFire CacheServer
应用程序中启动嵌入式定位器,该应用程序在 localhost
上运行,侦听默认定位器端口 10334
。您可以使用相应的注解属性自定义嵌入式定位器绑定的 host
(绑定地址)和 port
。
或者,您可以在 application.properties
中设置相应的 spring.data.gemfire.locator.host
和 spring.data.gemfire.locator.port
属性来设置 @EnableLocator
属性。
然后,您可以通过以下方式连接到此定位器来启动其他启用了 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
类的运行配置文件中只有一个应设置 -Dspring.profiles.active=embedded-locator
Java 系统属性。然后,您可以更改其他每个运行配置文件的 ..name
和 ..cache.server.port
,并在本地系统上运行一个小型集群(分布式系统)的 Pivotal GemFire 服务器。
@EnableLocator 注解仅作为开发时间注解,而不是应用程序开发人员在生产中使用的内容。我们强烈建议在集群中将定位器作为独立的进程运行。
|
有关 Pivotal GemFire 定位器工作原理的更多详细信息,请参阅 此处。
6.7.2. 配置嵌入式管理器
Pivotal GemFire Manager 是集群中另一个对等成员或节点,负责集群“管理”。管理涉及创建 Regions
、Indexes
、DiskStores
等,以及监视集群组件的运行时操作和行为。
管理器允许 JMX 启用的客户端(例如 Gfsh shell 工具)连接到管理器以管理集群。还可以使用 JDK 提供的工具(例如 JConsole 或 JVisualVM)连接到管理器,因为它们也是 JMX 启用的客户端。
您可能还希望将前面显示的 Spring @CacheServerApplication
也启用为管理器。为此,请使用 @EnableManager
为 Spring @Configuration
或 @SpringBootApplication
类添加注释。
默认情况下,管理器绑定到 localhost
,侦听默认管理器端口 1099
。可以通过注释属性或相应属性配置管理器的几个方面。
以下示例显示如何在 Java 中创建嵌入式管理器
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
由于我们还启用了嵌入式定位器,因此我们可以通过定位器间接连接到管理器。定位器允许 JMX 客户端连接并在集群中查找管理器。如果不存在,定位器将承担管理器的角色。但是,如果不存在定位器,我们需要使用以下内容直接连接到管理器
connect
命令直接连接到管理器gfsh>connect --jmx-manager=localhost[1099]
与 @EnableLocator 注释类似,@EnableManager 注释也仅用于开发时间,而不是应用程序开发人员在生产中使用的东西。我们强烈建议管理器(如定位器)成为集群中独立、独立且专用的进程。
|
有关 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 服务器,请向任何 @PeerCacheApplication
或 @CacheServerApplication
注释的类添加 @EnableHttpService
注释,如下所示
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 服务,请向任何 @PeerCacheApplication
或 @CacheServerApplication
注释的类添加 @EnableMemcachedServer
注释,如下所示
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 服务,请向任何 @PeerCacheApplication
或 @CacheServerApplication
注释的类添加 @EnableRedisServer
注释,如下所示
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(可视化统计信息显示) 工具来分析收集的统计数据。
要启用统计信息,请使用 @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
实现也是有意义的。
通过使用 复合软件设计模式,您可以提供 PdxSerializer
接口的实现,该实现聚合所有应用程序域对象类型特定的 PdxSerializer
实例,但充当单个 PdxSerializer
实例并注册它。
您可以在 Spring 容器中将此复合 PdxSerializer
声明为受管 Bean,并在 @EnablePdx
注释中通过 serializerBeanName
属性按其 Bean 名称引用此复合 PdxSerializer
。Spring Data for Pivotal GemFire 会负责代表您向 Pivotal GemFire 注册它。
以下示例展示了如何创建自定义复合 PdxSerializer
ClientCache
应用程序,使用自定义复合 PdxSerializer
@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 系统属性。
我们建议在将应用程序部署到生产环境时,在 gemfire.properties 文件中设置这些 Pivotal GemFire 属性。但是,在开发期间,可以根据需要单独设置这些属性,以用于原型制作、调试和测试目的。
|
通常无需担心的几个不常用的 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. 配置区域
到目前为止,除了 PDX 之外,我们的讨论一直集中在配置 Pivotal GemFire 的更多管理功能上:创建缓存实例、启动嵌入式服务、启用日志记录和统计信息、配置 PDX 以及使用 gemfire.properties
来影响底层配置和行为。虽然所有这些配置选项都很重要,但没有一个直接与您的应用程序相关。换句话说,我们仍然需要一些地方来存储我们的应用程序数据,并使其普遍可用且可访问。
以前,Pivotal GemFire 的 Spring Data 用户需要通过编写非常冗长的 Spring 配置元数据来显式定义和声明应用程序用来存储数据的区域,无论使用 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 定义,除非您需要更精细的控制。
为了简化区域创建,Pivotal GemFire 的 Spring Data 将 Spring Data 存储库的使用与使用新 @EnableEntityDefinedRegions
注解的基于注解的配置的表达能力相结合。
大多数 Spring Data 应用程序开发人员应该已经熟悉 Spring Data 存储库抽象 和 Pivotal GemFire 的 Spring Data 实现/扩展,该实现/扩展已专门定制以优化 Pivotal GemFire 的数据访问操作。 |
首先,应用程序开发人员从定义应用程序的域对象(实体)开始,如下所示
@Region("Books")
class Book {
@Id
private ISBN isbn;
private Author author;
private Category category;
private LocalDate releaseDate;
private Publisher publisher;
private String title;
}
接下来,通过扩展 Spring Data Commons org.springframework.data.repository.CrudRepository
接口来定义 Books
的基本存储库,如下所示
interface BookRepository extends CrudRepository<Book, ISBN> { .. }
org.springframe.data.repository.CrudRepository
是一个数据访问对象 (DAO),它提供基本数据访问操作 (CRUD) 以及对简单查询(例如 findById(..)
)的支持。你可以通过在存储库接口上声明查询方法来定义更多复杂查询(例如,List<BooK> findByAuthor(Author author);
)。
在底层,Spring Data for Pivotal GemFire 在 Spring 容器启动时提供应用程序存储库接口的实现。只要你遵循约定,SDG 甚至会实现你定义的查询方法。
现在,当你定义 Book
类时,你还指定了 Book
实例映射(存储)的区域,方法是在实体类型上声明 Spring Data for Pivotal GemFire 映射注释 @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 存储库时,从实体类创建区域最有用。Spring Data for Pivotal GemFire 的存储库支持通过 @EnableGemfireRepositories 注释启用,如前一个示例所示。
|
目前,只有明确用 @Region 注释的实体类才会被扫描并创建区域。如果实体类未明确用 @Region 映射,则不会创建区域。
|
默认情况下,@EnableEntityDefinedRegions
注释从 @EnableEntityDefinedRegions
注释声明所在的配置类的包开始递归扫描实体类。
但是,可以通过设置 basePackages
属性(其中包含应用程序实体类的包名)来限制扫描期间的搜索。
或者,你可以使用更类型安全的 basePackageClasses
属性,通过将属性设置为包含实体类的包中的实体类型或使用专门为标识要扫描的包而创建的非实体占位符类来指定要扫描的包。
以下示例展示了如何指定要扫描的实体类型
@SpringBootApplication
@ClientCacheApplication
@EnableGemfireRepositories
@EnableEntityDefinedRegions(basePackageClasses = {
example.app.books.domain.Book.class,
example.app.customers.domain.Customer.class
})
class ClientApplication { .. }
除了指定扫描的开始位置(如 Spring 的 @ComponentScan
注解),您还可以使用与 org.springframework.context.annotation.ComponentScan.Filter
注解完全相同的语义指定 include
和 exclude
过滤器。
有关更多详细信息,请参阅 @EnableEntityDefinedRegions
注解 Javadoc。
6.12.1. 配置特定类型的区域
Pivotal GemFire 支持许多不同的 类型的区域。每种类型都对应于区域的 DataPolicy
,它准确地确定区域中的数据将如何被管理(即分布、复制等)。
其他配置设置(如区域的 scope )也会影响数据的管理方式。有关更多详细信息,请参阅 Pivotal GemFire 用户指南中的 “存储和分布选项”。
|
当您使用通用 @Region
映射注解对应用程序域对象类型进行注解时,Spring Data for Pivotal GemFire 将决定创建哪种类型的区域。SDG 的默认策略在确定要创建的区域类型时会考虑缓存类型。
例如,如果您使用 @ClientCacheApplication
注解将应用程序声明为 ClientCache
,则 SDG 默认创建客户端 PROXY
Region
。或者,如果您使用 @PeerCacheApplication
或 @CacheServerApplication
注解将应用程序声明为对等 Cache
,则 SDG 默认创建服务器 PARTITION
Region
。
当然,您始终可以在必要时覆盖默认值。为了覆盖 Spring Data for Pivotal GemFire 应用的默认值,引入了四个新的区域映射注解
-
@ClientRegion
-
@LocalRegion
-
@PartitionRegion
-
@ReplicateRegion
@ClientRegion
映射注解特定于客户端应用程序。上面列出的所有其他区域映射注解只能在具有嵌入式对等 Cache
的服务器应用程序中使用。
有时,客户端应用程序需要创建和使用仅限本地的区域,可能需要从其他区域聚合数据,以便在本地分析数据并执行由应用程序代表用户执行的某些功能。在这种情况下,除非其他应用程序需要访问结果,否则无需将数据分发回服务器。此区域甚至可能是临时的,在使用后丢弃,这可以通过区域本身上的空闲超时 (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. 配置的集群定义区域
除了 @EnableEntityDefinedRegions
注释之外,Spring Data for Pivotal GemFire 还提供了反向注释 @EnableClusterDefinedRegions
。与其根据应用程序用例 (UC) 和要求(最常见且最合乎逻辑的方法)定义和驱动的实体类来构建区域,或者,您可以从 ClientCache
应用程序将连接到的集群中已定义的区域中声明区域。
这允许您使用服务器群集作为数据定义的主要来源来集中配置,并确保群集的所有客户端应用程序都具有一个一致的配置。当快速扩展大量相同客户端应用程序实例以处理云管理环境中的增加的负载时,这特别有用。
这个想法是,用户使用 Pivotal GemFire 的 Gfsh CLI shell 工具定义区域,而不是让客户端应用程序驱动数据字典。这具有额外的优势,即当向群集添加其他对等体时,它们也将拥有并共享相同的配置,因为 Pivotal GemFire 的 群集配置服务 会记住它。
例如,用户可以在 Gfsh 中定义一个区域,如下所示
gfsh>create region --name=Books --type=PARTITION
Member | Status
--------- | --------------------------------------
ServerOne | Region "/Books" created on "ServerOne"
ServerTwo | Region "/Books" created on "ServerTwo"
gfsh>list regions
List of regions
---------------
Books
gfsh>describe region --name=/Books
..........................................................
Name : Books
Data Policy : partition
Hosting Members : ServerTwo
ServerOne
Non-Default Attributes Shared By Hosting Members
Type | Name | Value
------ | ----------- | ---------
Region | size | 0
| data-policy | PARTITION
使用 Pivotal GemFire 的 群集配置服务,添加到服务器群集以处理增加的负载(在后端)的任何其他对等成员也将具有相同的配置,例如
gfsh>list members
Name | Id
--------- | ----------------------------------------------
Locator | 10.0.0.121(Locator:68173:locator)<ec><v0>:1024
ServerOne | 10.0.0.121(ServerOne:68242)<v3>:1025
ServerTwo | 10.0.0.121(ServerTwo:68372)<v4>:1026
gfsh>start server --name=ServerThree --log-level=config --server-port=41414
Starting a Geode Server in /Users/you/geode/cluster/ServerThree...
...
Server in /Users/you/geode/cluster/ServerThree... on 10.0.0.121[41414] as ServerThree is currently online.
Process ID: 68467
Uptime: 3 seconds
Geode Version: 1.2.1
Java Version: 1.8.0_152
Log File: /Users/you/geode/cluster/ServerThree/ServerThree.log
JVM Arguments: -Dgemfire.default.locators=10.0.0.121[10334]
-Dgemfire.use-cluster-configuration=true
-Dgemfire.start-dev-rest-api=false
-Dgemfire.log-level=config
-XX:OnOutOfMemoryError=kill -KILL %p
-Dgemfire.launcher.registerSignalHandlers=true
-Djava.awt.headless=true
-Dsun.rmi.dgc.server.gcInterval=9223372036854775806
Class-Path: /Users/you/geode/cluster/apache-geode-1.2.1/lib/geode-core-1.2.1.jar
:/Users/you/geode/cluster/apache-geode-1.2.1/lib/geode-dependencies.jar
gfsh>list members
Name | Id
----------- | ----------------------------------------------
Locator | 10.0.0.121(Locator:68173:locator)<ec><v0>:1024
ServerOne | 10.0.0.121(ServerOne:68242)<v3>:1025
ServerTwo | 10.0.0.121(ServerTwo:68372)<v4>:1026
ServerThree | 10.0.0.121(ServerThree:68467)<v5>:1027
gfsh>describe member --name=ServerThree
Name : ServerThree
Id : 10.0.0.121(ServerThree:68467)<v5>:1027
Host : 10.0.0.121
Regions : Books
PID : 68467
Groups :
Used Heap : 37M
Max Heap : 3641M
Working Dir : /Users/you/geode/cluster/ServerThree
Log file : /Users/you/geode/cluster/ServerThree/ServerThree.log
Locators : 10.0.0.121[10334]
Cache Server Information
Server Bind :
Server Port : 41414
Running : true
Client Connections : 0
如您所见,“ServerThree”现在具有“Books”区域。如果任何或所有服务器宕机,当它们重新启动时,它们将具有相同的配置以及“Books”区域。
在客户端,可能会启动许多书店客户端应用程序实例来针对在线书店服务处理书籍。“Books”区域可能是实现书店应用程序服务所需的许多不同区域之一。SDG 允许从群集定义客户端应用程序区域,如下所示,而不是必须单独创建和配置每个区域
@EnableClusterDefinedRegions
从群集定义客户端区域@ClientCacheApplication
@EnableClusterDefinedRegions
class BookStoreClientApplication {
public static void main(String[] args) {
....
}
...
}
@EnableClusterDefinedRegions 只能在客户端上使用。
|
您可以使用 clientRegionShortcut 注解属性来控制在客户端上创建的区域类型。默认情况下,会创建一个客户端 PROXY 区域。将 clientRegionShortcut 设置为 ClientRegionShortcut.CACHING_PROXY 以实现“近缓存”。此设置适用于从群集定义区域创建的所有客户端区域。如果您想控制从群集上定义的区域创建的客户端区域的各个设置(如数据策略),那么您可以使用 RegionConfigurer 根据区域名称实现自定义逻辑。
|
然后,在您的应用程序中使用“Books”区域变得很简单。您可以直接注入“Books”区域,如下所示
@org.springframework.stereotype.Repository
class BooksDataAccessObject {
@Resource(name = "Books")
private Region<ISBN, Book> books;
// implement CRUD and queries with the "Books" Region
}
或者,还可以基于应用程序域类型(实体)Book
定义一个 Spring Data Repository 定义,该定义映射到“Books”区域,如下所示
interface BookRepository extends CrudRepository<Book, ISBN> {
...
}
然后,可以将自定义的BooksDataAccessObject
或BookRepository
注入到应用程序服务组件中,以执行所需的任何业务功能。
6.12.3. 配置驱逐
使用 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. 配置到期
除了驱逐之外,还可以使用到期通过允许存储在区域中的条目到期来管理内存。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 提供以下到期注释
-
到期
-
空闲超时到期
-
生存时间到期
应用程序域对象类型可以注释一个或多个到期注释,如下所示
@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. 配置压缩
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 。当然,如果您使用其他压缩库,则需要在应用程序的类路径中包含该压缩库的依赖项。此外,您需要实现 Pivotal GemFire 的 Compressor 接口以调整您选择的压缩库,将其定义为 Spring 压缩器中的 bean,并将 compressorBeanName 设置为此自定义 bean 定义。
|
有关更多详细信息,请参阅 @EnableCompression
注释 Javadoc。
有关 Pivotal GemFire 压缩的更多详细信息,请参阅 此处。
6.12.6. 配置堆外内存
减轻 JVM 堆内存压力和最大程度减少 GC 活动的另一种有效方法是使用 Pivotal GemFire 的堆外内存支持。
条目不会存储在 JVM 堆中,而是存储在系统的主内存中。堆外内存通常在以下情况下效果最佳:要存储的对象大小一致,大多数小于 128K,并且不需要频繁反序列化,如 Pivotal GemFire 用户指南 中所述。
要启用堆外内存,请使用 @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. 配置磁盘存储
或者,您可以将区域配置为将数据持久化到磁盘。您还可以将区域配置为在区域条目被逐出时将数据溢出到磁盘。在这两种情况下,都需要一个 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
都具有许多属性以及关联的配置属性,以自定义在运行时创建的 DiskStores
。
此外,@EnableDiskStores
注解定义了某些适用于所有从 @EnableDiskStore
注解(由 @EnableDiskStores
注解本身组成)创建的 DiskStores
的通用 DiskStore
属性。各个 DiskStore
配置会覆盖特定全局设置,但 @EnableDiskStores
注解方便地定义了适用于由该注解聚合的所有 DiskStores
的通用配置属性。
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 区域持久性和溢出(使用 DiskStores)的更多详细信息。
6.12.8. 配置索引
除非可以访问数据,否则在区域中存储数据并没有多大用处。
除了 Region.get(key)
操作(尤其是在预先知道键的情况下),通常通过对包含数据的区域执行查询来检索数据。使用 Pivotal GemFire 时,查询是使用对象查询语言 (OQL) 编写的,客户端希望访问的特定数据集在查询的谓词中表示(例如,SELECT * FROM /Books b WHERE b.author.name = 'Jon Doe'
)。
通常,没有索引的查询效率低下。在没有索引的情况下执行查询时,Pivotal GemFire 会执行相当于全表扫描的操作。
Spring Data for Pivotal GemFire 使得在存储和访问数据的区域上创建索引变得很容易。我们可以在 Java 中创建 Index
bean 定义,如下所示,而不是像以前那样使用 Spring 配置显式声明 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
实体类,我们可以注释 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
注释的字段或属性在存储条目时用作区域中的键。
-
@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 Name><Field/Property Name><Index Type>Idx
。例如,author
索引的名称将是 BooksAuthorHashIdx
。
要启用索引,请使用 @EnableIndexing
对应用程序类进行注释,如下所示
@SpringBootApplication
@PeerCacheApplication
@EnableEntityDefinedRegions
@EnableIndexing
class ServerApplication { .. }
除非还声明了 @EnableEntityDefinedRegions ,否则 @EnablingIndexing 注释不起作用。从本质上讲,索引是从实体类类型上的字段或属性定义的,必须扫描实体类以检查实体的字段和属性是否存在索引注释。如果没有此扫描,将找不到索引注释。我们还强烈建议您限制扫描范围。
|
虽然 Pivotal GemFire 存储库中的 Spring Data 还不(尚未)支持 Lucene 查询,但 SDG 通过使用熟悉的 Spring 模板设计模式为 Pivotal GemFire Lucene 查询提供了全面的支持。
最后,我们用几个使用索引时需要记住的额外提示来结束本节
-
虽然执行 OQL 查询不需要 OQL 索引,但执行基于 Lucene 文本的搜索需要 Lucene 索引。
-
OQL 索引不会持久化到磁盘。它们仅保留在内存中。因此,当 Pivotal GemFire 节点重新启动时,必须重建索引。
-
您还需要了解维护索引相关的开销,特别是由于索引仅存储在内存中,尤其是在更新区域条目时。索引“维护”可以配置为异步任务。
在重新启动必须重建索引的 Spring 应用程序时,您可以使用的另一个优化方法是首先预先定义所有索引,然后一次创建所有索引,在 Spring Data for Pivotal GemFire 中,这是在 Spring 容器刷新时发生的。
您可以预先定义索引,然后通过将 @EnableIndexing
注释上的 define
属性设置为 true
来一次创建所有索引。
有关更多详细信息,请参阅 Pivotal GemFire 用户指南中的“一次创建多个索引”。
创建合理的索引是一项重要的任务,因为设计不当的索引可能弊大于利。
请参阅 @Indexed
注释和 @LuceneIndexed
注释 Javadoc,了解完整配置选项列表。
可以在 此处 找到有关 Pivotal GemFire OQL 查询的更多详细信息。
可以在 此处 找到有关 Pivotal GemFire 索引的更多详细信息。
可以在 此处 找到有关 Pivotal GemFire Lucene 查询的更多详细信息。
6.13. 配置连续查询
Pivotal GemFire 的另一个非常重要且有用的特性是 连续查询。
在物联网的世界中,事件和数据流无处不在。能够处理大量数据流并实时响应事件是许多应用程序日益重要的要求。自动驾驶汽车就是一个例子。能够实时接收、过滤、转换、分析和处理数据是实时应用程序的关键差异化因素和特征。
幸运的是,Pivotal GemFire 在这方面走在了时代的前列。通过使用连续查询 (CQ),客户端应用程序可以表达其感兴趣的数据或事件,并注册侦听器来处理和处理发生的事件。客户端应用程序可能感兴趣的数据表示为 OQL 查询,其中查询谓词用于过滤或识别感兴趣的数据。当数据发生更改或添加,并且它与已注册 CQ 的查询谓词中定义的条件匹配时,客户端应用程序会收到通知。
Spring Data for Pivotal GemFire 使得定义和注册 CQ 变得很容易,同时还提供了一个关联的侦听器来处理和处理 CQ 事件,而无需 Pivotal GemFire 管道的所有繁琐工作。SDG 基于注释的新型 CQ 配置建立在 连续查询侦听器容器 中现有的连续查询支持之上。
例如,假设一家图书出版商希望注册兴趣并接收任何时候对 Book
的订单(需求)超过当前库存(供应)的通知。那么出版商的印刷应用程序可能会注册以下 CQ
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 的函数注释 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 区域。在调用任何缓存注释的应用程序服务方法之前,该区域必须存在。这适用于 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
注释(如“配置区域”中所述的 @EnableEntityDefinedRegions
注释)会检查整个 Spring 应用程序,缓存带注释的服务组件,以识别应用程序在运行时所需的所有缓存,并在应用程序启动时为这些缓存创建 Pivotal GemFire 中的区域。
创建的区域是创建区域的应用程序进程的本地区域。如果应用程序是 peer Cache
,则区域仅存在于应用程序节点上。如果应用程序是 ClientCache
,则 SDG 会创建客户端 PROXY
区域,并期望群集中的服务器上已存在具有相同名称的区域。
SDG 无法使用 Spring CacheResolver 确定服务方法所需的缓存,以解析操作在运行时使用的缓存。
|
SDG 还支持应用程序服务组件上的 JCache (JSR-107) 缓存注释。有关可用于替代 JCache 缓存注释的等效 Spring 缓存注释,请参阅核心 Spring Framework Reference Guide。 |
有关在 Spring 的缓存抽象中使用 Pivotal GemFire 作为缓存提供程序的更多详细信息,请参阅 “对 Spring 缓存抽象的支持” 部分。
有关 Spring 缓存抽象的更多详细信息,请参阅 此处。
6.15. 配置群集配置推送
这可能是 Spring Data for Pivotal GemFire 中最激动人心的新功能。
当客户端应用程序类用 @EnableClusterConfiguration
注释时,客户端应用程序在 Spring 容器中定义并声明为 bean 的任何区域或索引都会“推送”到客户端连接到的服务器群集。不仅如此,而且此“推送”以一种方式执行,即 Pivotal GemFire 在使用 HTTP 时会记住客户端推送的配置。如果群集中的所有节点都宕机,它们会以与之前相同的配置重新启动。如果向群集添加新服务器,它将获取相同的配置。
从某种意义上来说,此功能与使用 Gfsh 手动在集群中的所有服务器上创建区域和索引并无太大区别。但现在,有了 Spring Data for Pivotal GemFire,您不再需要使用 Gfsh 来创建区域和索引。您的 Spring Boot 应用程序在 Spring Data for Pivotal GemFire 的支持下,已包含创建区域和索引所需的所有配置元数据。
当您使用 Spring Data Repository 抽象时,我们了解您的应用程序所需的所有区域(例如由 @Region
注释的实体类定义的区域)和索引(例如由 @Indexed
注释的实体字段和属性定义的索引)。
当您使用 Spring 的缓存抽象时,我们还了解应用程序的服务组件所需的缓存注释中标识的所有缓存的所有区域。
从本质上讲,您只需使用 Spring 框架开发应用程序,并使用其所有 API 和功能(无论以注释元数据、Java、XML 或其他方式表示,无论用于配置、映射或其他目的),即可告诉我们所有我们需要了解的信息。
关键在于,您可以专注于应用程序的业务逻辑,同时使用框架的功能和支持基础设施(例如 Spring 的缓存抽象、Spring Data 存储库、Spring 的事务管理等),而 Spring Data for Pivotal GemFire 将负责 Pivotal GemFire 管道,而这些管道是这些框架功能所必需的。
将配置从客户端推送到集群中的服务器,并让集群记住该配置,这在一定程度上是通过使用 Pivotal GemFire 的 集群配置 服务实现的。Pivotal GemFire 的集群配置服务也是 Gfsh 用于记录用户从 shell 向集群发出的模式相关更改(例如,gfsh> create region --name=Example --type=PARTITION
)的相同服务。
当然,由于集群可能“记住”客户端在上次运行中推送的先前配置,因此 Spring Data for Pivotal GemFire 会小心不要破坏服务器中已定义的任何现有区域和索引。这尤其重要,例如,当区域已包含数据时!
目前,没有选项可以覆盖任何现有区域或索引定义。要重新创建区域或索引,您必须使用 Gfsh 首先销毁区域或索引,然后重新启动客户端应用程序,以便将配置再次推送到服务器。或者,您可以使用 Gfsh 手动(重新)定义区域和索引。 |
与 Gfsh 不同,Spring Data for Pivotal GemFire 仅支持从客户端在服务器上创建区域和索引。对于高级配置和用例,您应使用 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 数据存储库、Spring 的缓存抽象(其中区域和索引不仅在客户端创建,还推送到集群中的服务器)。
从那里,您只需要执行以下操作
-
定义使用映射和索引注释进行注释的应用程序的域模型对象。
-
定义存储库接口以支持基本数据访问操作和对每个实体类型的简单查询。
-
定义包含处理实体的业务逻辑的服务组件。
-
在需要缓存、事务行为等的服务方法上声明适当的注释。
在这种情况下,没有任何内容与应用程序后端服务(例如 Pivotal GemFire)中所需的架构和管道相关。数据库用户具有类似的功能。现在,Spring 和 Pivotal GemFire 开发人员也拥有这些功能。
当与以下 Spring Data for Pivotal GemFire 注释结合使用时,此应用程序几乎不费吹灰之力便真正开始起飞
-
@EnableContinuousQueries
-
@EnableGemfireFunctionExecutions
-
@EnableGemfireCacheTransactions
有关更多详细信息,请参阅 @EnableClusterConfiguration
注释 Javadoc。
6.16. 配置 SSL
与通过网络传输数据进行序列化同样重要的是在传输过程中保护数据安全。当然,在 Java 中实现此目标的常用方法是使用安全套接字扩展 (SSE) 和传输层安全性 (TLS)。
要启用 SSL,请使用 @EnableSsl
注释应用程序类,如下所示
ClientCache
应用程序@SpringBootApplication
@ClientCacheApplication
@EnableSsl
public class ClientApplication { .. }
然后,您需要设置必要的 SSL 配置属性或特性:密钥库、用户名/密码等。
您可以使用 SSL 单独配置不同的 Pivotal GemFire 组件(GATEWAY
、HTTP
、JMX
、LOCATOR
和 SERVER
),或者使用 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 应用程序类进行注释,并定义一个或多个 Apache ShiroRealms
作为 Spring 容器中的 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
默认情况下,当 Spring Boot 的 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
或群集配置)中定义的区域,并将这些区域自动注册为 Spring 容器中的 bean。此注释对应于 SDG XML 命名空间中的<gfe:auto-region-lookup>
元素。更多详细信息可以在此找到。在使用 Spring 和 Spring Data for Pivotal GemFire 时,用户通常应该更喜欢 Spring 配置。请参阅“配置区域”和“配置群集配置推送”。 -
@EnableBeanFactoryLocator
:启用 SDGGemfireBeanFactoryLocator
功能,该功能仅在使用外部配置元数据(例如cache.xml
)时才有用。例如,如果您在cache.xml
中定义的区域上定义了CacheLoader
,则仍然可以使用 Spring 配置中定义的关系数据库DataSource
bean 自动装配此CacheLoader
。此注释利用了此 SDG 功能,如果您有大量旧配置元数据(例如cache.xml
文件),则可能很有用。 -
@EnableGemFireAsLastResource
:在全局 - JTA 事务管理中与 Pivotal GemFire 一起讨论。 -
@EnableMcast
:启用 Pivotal GemFire 的旧对等发现机制,该机制使用基于 UDP 的多播网络。(已弃用。请改用 Pivotal GemFire 定位器。请参阅“配置嵌入式定位器”。 -
@EnableRegionDataAccessTracing
:用于调试目的。此注释通过注册一个 AOP 方面来启用对区域执行的所有数据访问操作的跟踪,该方面代理在 Spring 容器中声明为 bean 的所有区域,拦截区域操作并记录事件。
6.19. 结论
正如我们在前几部分中了解到的,Spring Data for Pivotal GemFire 的基于注释的新配置模型提供了巨大的能力。希望它能达到其目标,让您在将 Pivotal GemFire 与 Spring 一起使用时快速且轻松地入门。
请记住,当您使用新注释时,您仍然可以使用 Java 配置或 XML 配置。您甚至可以通过在 Spring 的 @Configuration
或 @SpringBootApplication
类中使用 Spring 的 @Import
和 @ImportResource
注释来组合所有这三种方法。当您明确提供一个 bean 定义时,该定义原本应由 Spring Data for Pivotal GemFire 使用其中一个注释提供,基于注释的配置就会退出。
在某些情况下,您甚至可能需要退回到 Java 配置,如在 例如,您需要 Java 或 XML 配置的另一个情况是在配置 Pivotal GemFire WAN 组件时,目前没有任何注释配置支持。但是,定义和注册 WAN 组件只需要在 Spring |
注释并不是为了处理每种情况。注释的目的是帮助您快速且轻松地启动并运行,尤其是在开发期间。
我们希望您会喜欢这些新功能!
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 ,只需用 @CacheServerApplication 注释替换 @PeerCacheApplication 注释。这将在“localhost”上启动一个 CacheServer ,侦听默认 CacheServer 端口 40404 。
|
请参阅 使用 Spring 配置 Pivotal GemFire 应用程序 以获取更多详细信息。
6.20.3. 配置嵌入式定位器
使用 @EnableLocator
注释您的 Spring @PeerCacheApplication
或 @CacheServerApplication
类,以启动一个嵌入式定位器,该定位器绑定到侦听默认定位器端口 10334
的所有网卡,如下所示
@SpringBootApplication
@CacheServerApplication
@EnableLocator
public class ServerApplication {
public static void main(String[] args) {
SpringApplication.run(ServerApplication.class, args);
}
}
@EnableLocator 只能与 Pivotal GemFire 服务器应用程序一起使用。
|
请参阅 配置嵌入式定位器 以获取更多详细信息。
6.20.4. 配置嵌入式管理器
使用 @EnableManager
注释您的 Spring @PeerCacheApplication
或 @CacheServerApplication
类,以启动一个嵌入式管理器,该管理器绑定到侦听默认管理器端口 1099
的所有网卡,如下所示
@SpringBootApplication
@CacheServerApplication
@EnableManager
public class ServerApplication {
public static void main(String[] args) {
SpringApplication.run(ServerApplication.class, args);
}
}
@EnableManager 只能与 Pivotal GemFire 服务器应用程序一起使用。
|
请参阅 配置嵌入式管理器 以获取更多详细信息。
6.20.5. 配置嵌入式 HTTP 服务器
使用 @EnableHttpService
注释 Spring @PeerCacheApplication
或 @CacheServerApplication
类,以启动嵌入式 HTTP 服务器(Jetty),侦听端口 7070
,如下所示
@SpringBootApplication
@CacheServerApplication
@EnableHttpService
public class ServerApplication {
public static void main(String[] args) {
SpringApplication.run(ServerApplication.class, args);
}
}
@EnableHttpService 只能与 Pivotal GemFire 服务器应用程序一起使用。
|
请参阅 配置嵌入式 HTTP 服务器 了解更多详情。
6.20.6. 配置嵌入式 Memcached 服务器
使用 @EnableMemcachedServer
注释 Spring @PeerCacheApplication
或 @CacheServerApplication
类,以启动嵌入式 Memcached 服务器(Gemcached),侦听端口 11211
,如下所示
@SpringBootApplication
@CacheServerApplication
@EnableMemcachedServer
public class ServerApplication {
public static void main(String[] args) {
SpringApplication.run(ServerApplication.class, args);
}
}
@EnableMemcachedServer 只能与 Pivotal GemFire 服务器应用程序一起使用。
|
请参阅 配置嵌入式 Memcached 服务器 (Gemcached) 了解更多详情。
6.20.7. 配置嵌入式 Redis 服务器
使用 @EnableRedisServer
注释 Spring @PeerCacheApplication
或 @CacheServerApplication
类,以启动嵌入式 Redis 服务器,侦听端口 6379
,如下所示
@SpringBootApplication
@CacheServerApplication
@EnableRedisServer
public class ServerApplication {
public static void main(String[] args) {
SpringApplication.run(ServerApplication.class, args);
}
}
@EnableRedisServer 只能与 Pivotal GemFire 服务器应用程序一起使用。
|
您必须在 Spring [Boot] 应用程序类路径中显式声明 org.apache.geode:geode-redis 模块。
|
请参阅 配置嵌入式 Redis 服务器 了解更多详情。
6.20.8. 配置日志记录
要配置或调整 Pivotal GemFire 日志记录,请使用 @EnableLogging
注释 Spring、Pivotal GemFire 客户端或服务器应用程序类,如下所示
@SpringBootApplication
@ClientCacheApplication
@EnableLogging(logLevel="trace")
public class ClientApplication {
public static void main(String[] args) {
SpringApplication.run(ClientApplication.class, args);
}
}
默认 log-level 为“config”。此外,此注释不会调整应用程序中的日志级别,仅适用于 Pivotal GemFire。
|
请参阅 配置日志记录 了解更多详情。
6.20.9. 配置统计信息
要在运行时收集 Pivotal GemFire 统计信息,请使用 @EnableStatistics
注释你的 Spring、Pivotal GemFire 客户端或服务器应用程序类,如下所示
@SpringBootApplication
@ClientCacheApplication
@EnableStatistics
public class ClientApplication {
public static void main(String[] args) {
SpringApplication.run(ClientApplication.class, args);
}
}
请参阅 配置统计信息 以了解更多详细信息。
6.20.10. 配置 PDX
要启用 Pivotal GemFire PDX 序列化,请使用 @EnablePdx
注释你的 Spring、Pivotal GemFire 客户端或服务器应用程序类,如下所示
@SpringBootApplication
@ClientCacheApplication
@EnablePdx
public class ClientApplication {
public static void main(String[] args) {
SpringApplication.run(ClientApplication.class, args);
}
}
Pivotal GemFire PDX 序列化是 Java 序列化的替代方法,具有许多附加优点。其中之一是,它可以轻松地使你的所有应用程序域模型类型可序列化,而无需实现 java.io.Serializable 。
|
默认情况下,SDG 配置 MappingPdxSerializer 以序列化你的应用程序域模型类型,这不需要开箱即用的任何特殊配置即可正确识别需要序列化的应用程序域对象,然后执行序列化,因为 MappingPdxSerializer 中的逻辑基于 Spring Data 的映射基础架构。请参阅 MappingPdxSerializer 以了解更多详细信息。
|
请参阅 @EnablePdx
Javadoc。
请参阅 配置 PDX 以了解更多详细信息。
6.20.11. 配置 SSL
要启用 Pivotal GemFire SSL,请使用 @EnableSsl
注释你的 Spring、Pivotal GemFire 客户端或服务器应用程序类,如下所示
@SpringBootApplication
@ClientCacheApplication
@EnableSsl(components = SERVER)
public class ClientApplication {
public static void main(String[] args) {
SpringApplication.run(ClientApplication.class, args);
}
}
最少,Pivotal GemFire 要求你使用适当的配置属性或属性指定密钥库和信任库。密钥库和信任库配置属性或属性都可以引用同一个 KeyStore 文件。此外,如果文件已得到保护,则需要指定用户名和密码来访问 KeyStore 文件。
|
Pivotal GemFire SSL 允许你配置需要 TLS 的系统特定组件,例如客户端/服务器、定位器、网关等。另外,你可以指定 Pivotal GemFire 的所有组件都使用 SSL,即“ALL”。 |
请参阅 @EnableSsl
Javadoc。
请参阅 配置 SSL 以了解更多详细信息。
6.20.12. 配置安全
要启用 Pivotal GemFire 安全,请使用 @EnableSecurity
注释你的 Spring、Pivotal GemFire 客户端或服务器应用程序类,如下所示
@SpringBootApplication
@ClientCacheApplication
@EnableSecurity
public class ClientApplication {
public static void main(String[] args) {
SpringApplication.run(ClientApplication.class, args);
}
}
在服务器上,您必须配置对身份验证凭据的访问。您可以实现 Pivotal GemFire SecurityManager 接口或声明 1 个或多个 Apache Shiro Realms 。有关更多详细信息,请参阅 配置服务器安全性。
|
在客户端上,您必须配置用户名和密码。有关更多详细信息,请参阅 配置客户端安全性。 |
有关更多详细信息,请参阅 配置安全性。
6.20.13. 配置 Pivotal GemFire 属性
要配置功能导向 SDG 配置注释未涵盖的其他低级别 Pivotal GemFire 属性,请使用 @GemFireProperties
注释您的 Spring、Pivotal GemFire 客户端或服务器应用程序类,如下所示
@SpringBootApplication
@PeerCacheApplication
@EnableGemFireProperties(
cacheXmlFile = "/path/to/cache.xml",
conserveSockets = true,
groups = "GroupOne",
remoteLocators = "lunchbox[11235],mailbox[10101],skullbox[12480]"
)
public class ServerApplication {
public static void main(String[] args) {
SpringApplication.run(ServerApplication.class, args);
}
}
一些 Pivotal GemFire 属性仅限客户端,而另一些属性仅限服务器。请查看 Pivotal GemFire 文档,了解每个属性的适当用途。 |
有关更多详细信息,请参阅 配置 Pivotal GemFire 属性。
6.20.14. 配置缓存
要在 Spring 的 缓存抽象 中将 Pivotal GemFire 用作缓存提供程序,并让 SDG 为应用程序服务组件所需的缓存自动创建 Pivotal GemFire 区域,请使用 @EnableGemfireCaching
和 @EnableCachingDefinedRegions
注释您的 Spring、Pivotal GemFire 客户端或服务器应用程序类,如下所示
@SpringBootApplication
@ClientCacheApplication
@EnableCachingDefinedRegions
@EnableGemfireCaching
public class ClientApplication {
public static void main(String[] args) {
SpringApplication.run(ClientApplication.class, args);
}
}
然后,只需继续定义需要缓存的应用程序服务,如下所示
@Service
public class BookService {
@Cacheable("Books")
public Book findBy(ISBN isbn) {
...
}
}
@EnableCachingDefinedRegions 是可选的。也就是说,如果您愿意,可以手动定义您的区域。
|
请参阅 配置 Spring 的缓存抽象 了解更多详情。
6.20.15. 为持久化应用程序配置区域、索引、存储库和实体
为了简化创建 Spring、Pivotal GemFire 持久化客户端或服务器应用程序,请使用 @EnableEntityDefinedRegions
、@EnableGemfireRepositories
和 @EnableIndexing
对应用程序类进行注释,如下所示
@SpringBootApplication
@ClientCacheApplication
@EnableEntityDefinedRegions(basePackageClasses = Book.class)
@EnableGemfireRepositories(basePackageClasses = BookRepository.class)
@EnableIndexing
public class ClientApplication {
public static void main(String[] args) {
SpringApplication.run(ClientApplication.class, args);
}
}
使用 @EnableIndexing 注释时,需要 @EnableEntityDefinedRegions 注释。请参阅 配置索引 了解更多详情。
|
接下来,定义实体类并使用 @Region
映射注释指定将存储实体的区域。使用 @Indexed
注释定义应用程序查询中使用的实体字段上的索引,如下所示
package example.app.model;
import ...;
@Region("Books")
public class Book {
@Id
private ISBN isbn;
@Indexed;
private Author author;
@Indexed
private LocalDate published;
@LuceneIndexed
private String title;
}
@EnableEntityDefinedRegions 使用 @Region("Books") 实体类注释来确定应用程序所需的区域。请参阅 配置特定于类型的区域 和 POJO 映射 了解更多详情。
|
最后,使用简单查询定义 CRUD 存储库,以持久化和访问 Books
,如下所示
package example.app.repo;
import ...;
public interface BookRepository extends CrudRepository {
List<Book> findByAuthorOrderByPublishedDesc(Author author);
}
请参阅 Spring Data for Pivotal GemFire 存储库 了解更多详情。 |
请参阅 @Region
Javadoc。
请参阅 @Indexed
Javadoc。
请参阅 配置区域 了解更多详情。
请参阅 Spring Data for Pivotal GemFire 存储库 了解更多详情。
6.20.16. 从集群定义的区域配置客户端区域
或者,您可以使用 @EnableClusterDefinedRegions
从已在集群中定义的区域定义客户端 [*PROXY] 区域,如下所示
@SpringBootApplication
@ClientCacheApplication
@EnableClusterDefinedRegions
@EnableGemfireRepositories
public class ClientApplication {
public static void main(String[] args) {
SpringApplication.run(ClientApplication.class, args);
}
...
}
请参阅 配置的集群定义区域 了解更多详情。
6.20.17. 配置函数
Pivotal GemFire 函数在分布式计算场景中非常有用,在该场景中,需要数据的潜在昂贵计算可以在群集中的节点上并行执行。在这种情况下,将逻辑带到数据所在(存储)的位置比请求和获取数据以供计算处理更有效率。
使用 @EnableGemfireFunctions
和 @GemfireFunction
注释来启用 Pivotal GemFire 函数定义,这些定义实现为 POJO 上的方法,如下所示
@PeerCacheApplication
@EnableGemfireFunctions
class ServerApplication {
public static void main(String[] args) {
SpringApplication.run(ServerApplication.class, args);
}
@GemfireFunction
Integer computeLoyaltyPoints(Customer customer) {
...
}
}
使用 @EnableGemfireFunctionExecutions
和 1 个函数调用注释:@OnMember
、@OnMembers
、@OnRegion
、@OnServer
和 @OnServers
。
@ClientCacheApplication
@EnableGemfireFunctionExecutions(basePackageClasses = CustomerRewardsFunction.class)
class ClientApplication {
public static void main(String[] args) {
SpringApplication.run(ClientApplication.class, args);
}
}
@OnRegion("Customers")
interface CustomerRewardsFunctions {
Integer computeLoyaltyPoints(Customer customer);
}
有关更多详细信息,请参阅 函数执行的注释支持。
6.20.18. 配置连续查询
实时事件流处理正成为数据密集型应用程序中越来越重要的任务,主要是为了及时响应用户请求。Pivotal GemFire 连续查询 (CQ) 将帮助您轻松实现这项相当复杂的任务。
通过使用 @EnableContinuousQueries
注释您的应用程序类并定义您的 CQ 以及关联的事件处理程序来启用 CQ,如下所示
@ClientCacheApplication
@EnableContinuousQueries
class ClientApplication {
public static void main(String[] args) {
SpringApplication.run(ClientApplication.class, args);
}
}
然后,通过使用 @ContinousQuery
注释关联的处理程序方法来定义您的 CQ,如下所示
@Service
class CustomerService {
@ContinuousQuery(name = "CustomerQuery", query = "SELECT * FROM /Customers c WHERE ...")
public void process(CqEvent event) {
...
}
}
每当发生事件将 Customer
数据更改为与您的连续 OQL 查询 (CQ) 中的谓词匹配时,都会调用 process
方法。
Pivotal GemFire CQ 仅是客户端功能。 |
6.20.19. 配置集群配置
使用 Pivotal GemFire 作为 Pivotal GemFire ClientCache
应用程序开发 Spring Data 应用程序时,在开发期间将服务器配置为在客户端/服务器拓扑中与客户端匹配非常有用。事实上,Pivotal GemFire 要求当客户端上有“/Example”PROXY Region
时,服务器中存在一个名称匹配的 Region
(即“Example”)。
可以使用 Gfsh 创建应用程序所需的所有 Region 和索引,或者,在运行 Spring Data 应用程序时,只需推送使用 Pivotal GemFire 开发时已表达的配置元数据即可。
这与使用 @EnableClusterConfiguration(..)
注释主应用程序类一样简单。
@EnableClusterConfiguration
@ClientCacheApplication
@EnableClusterConfiguration(useHttp = true)
class ClientApplication {
...
}
大多数情况下,在使用客户端/服务器拓扑时,尤其是在生产环境中,集群的服务器将使用 Gfsh 启动。在这种情况下,通常使用 HTTP(S) 将配置元数据(例如 Region 和索引定义)发送到集群。使用 HTTP 时,配置元数据将被发送到集群中的 Manager,并在集群中的服务器节点中一致地进行分布。 |
为了使用 @EnableClusterConfiguration ,必须在 Spring 应用程序类路径中声明 org.springframework:spring-web 依赖项。
|
参见 配置集群配置推送 了解更多详情。
6.20.20. 配置 GatewayReceivers
不同 Pivotal GemFire 集群之间的数据复制是一种日益重要的容错和高可用性 (HA) 机制。Pivotal GemFire WAN 复制是一种允许一个 Pivotal GemFire 集群以可靠且容错的方式将其数据复制到另一个 Pivotal GemFire 集群的机制。
Pivotal GemFire WAN 复制需要配置两个组件
-
GatewayReceiver
- WAN 复制组件,用于从远程 Pivotal GemFire 集群的GatewaySender
接收数据。 -
GatewaySender
- WAN 复制组件,用于向远程 Pivotal GemFire 集群的GatewayReceiver
发送数据。
要启用 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 节点上进行配置。
|
在上述示例中,应用程序配置了 2 个区域,Region1
和 Region2
。此外,将配置两个 GatewaySenders
来为这两个区域提供服务。GatewaySender1
将配置为复制 Region1
的数据,而 GatewaySender2
将配置为复制 Region2
的数据。
如演示所示,每个 GatewaySender
属性都可以在每个 EnableGatewaySender
注释上进行配置。
还可以采用更通用的“默认”属性方法,其中所有属性都在 EnableGatewaySenders
注释上进行配置。这样,可以在父注释上设置一组通用的默认值,然后根据需要在子注释上覆盖这些值,如下所示
@CacheServerApplication
@EnableGatewaySenders(gatewaySenders = {
@EnableGatewaySender(name = "GatewaySender", transportFilters = "transportBean1", regions = "Region2"),
@EnableGatewaySender(name = "GatewaySender2")},
manualStart = true, remoteDistributedSystemId = 2,
diskSynchronous = false, batchConflationEnabled = true, parallel = true, persistent = true,
diskStoreReference = "someDiskStore", orderPolicy = OrderPolicyType.PARTITION, alertThreshold = 1234, batchSize = 1002,
eventFilters = "SomeEventFilter", batchTimeInterval = 2000, dispatcherThreads = 22, maximumQueueMemory = 400,
socketBufferSize = 16384, socketReadTimeout = 4000, regions = { "Region1", "Region2" },
transportFilters = { "transportBean2", "transportBean1" })
class MySpringApplication { .. }
当 regions 属性为空或未填充时,GatewaySender 将自动附加到应用程序中配置的每个 Region 。
|
7. 使用 Pivotal GemFire API
配置 Pivotal GemFire 缓存和区域后,可以在应用程序对象中注入和使用它们。本章介绍了与 Spring 的事务管理功能和 DAO 异常层次结构的集成。本章还涵盖了对 Pivotal GemFire 托管对象的依赖注入的支持。
7.1. GemfireTemplate
与 Spring 提供的许多其他高级抽象一样,Spring Data for Pivotal GemFire 提供了一个 模板 来简化 Pivotal GemFire 数据访问操作。该类提供了包含常见区域操作的多个方法,但也提供了使用 GemfireCallback
在不处理 Pivotal GemFire 检查异常的情况下针对本机 Pivotal GemFire API 执行 代码的功能。
模板类需要一个 Pivotal GemFire Region
,并且一旦配置好,它就是线程安全的,并且可以在多个应用程序类中重复使用
<bean id="gemfireTemplate" class="org.springframework.data.gemfire.GemfireTemplate" p:region-ref="SomeRegion"/>
配置模板后,开发人员可以将其与 GemfireCallback
一起使用,以直接使用 Pivotal GemFire Region
,而无需处理检查异常、线程或资源管理问题
template.execute(new GemfireCallback<Iterable<String>>() {
public Iterable<String> doInGemfire(Region region)
throws GemFireCheckedException, GemFireException {
Region<String, String> localRegion = (Region<String, String>) region;
localRegion.put("1", "one");
localRegion.put("3", "three");
return localRegion.query("length < 5");
}
});
为了访问 Pivotal GemFire 查询语言的全部功能,开发人员可以使用 find
和 findUnique
方法,与 query
方法相比,这些方法可以在多个区域中执行查询、执行投影等。
当查询通过 SelectResults
选择多项时,应使用 find
方法,而后者 findUnique
,顾名思义,仅在返回一个对象时使用。
7.2. 异常转换
使用新的数据访问技术不仅需要适应新的 API,而且还需要处理特定于该技术异常。
为了适应异常处理案例,Spring Framework 提供了与技术无关且一致的 异常层次结构,它将应用程序从专有异常(通常是“已检查”的异常)抽象为一组重点运行时异常。
如 Spring Framework 文档中所述,异常转换可以通过使用 @Repository
注释和 AOP 通过定义 PersistenceExceptionTranslationPostProcessor
bean,透明地应用于你的数据访问对象 (DAO)。只要声明了 CacheFactoryBean
,在使用 Pivotal GemFire 时也会启用相同的异常转换功能,例如使用 <gfe:cache/>
或 <gfe:client-cache>
声明,它充当异常转换器,并由 Spring 基础结构自动检测并相应使用。
7.3. 本地、缓存事务管理
Spring Framework 最流行的功能之一是 事务管理。
如果你不熟悉 Spring 的事务抽象,那么我们强烈建议 阅读有关 Spring 的事务管理 基础结构,因为它提供了一致的 编程模型,可以在多个 API 中透明地工作,并且可以通过编程方式或声明方式(最流行的选择)进行配置。
对于 Pivotal GemFire,Spring Data for Pivotal GemFire 提供了一个专门的、按缓存划分的 PlatformTransactionManager
,一旦声明,便允许通过 Spring 以原子方式执行区域操作
<gfe:transaction-manager id="txManager" cache-ref="myCache"/>
如果 Pivotal GemFire 缓存是在默认名称 gemfireCache 下定义的,则可以通过消除 cache-ref 属性来进一步简化上面的示例。与其他 Spring Data for Pivotal GemFire 命名空间元素一样,如果未配置缓存 bean 名称,则将使用上述命名约定。此外,如果未明确指定,则事务管理器名称为“gemfireTransactionManager”。
|
目前,Pivotal GemFire 支持具有已提交读隔离的乐观事务。此外,为了保证这种隔离,开发人员应避免进行手动修改缓存中存在的值的就地更改。为了防止这种情况发生,事务管理器将缓存配置为默认使用读取时复制语义,这意味着每次执行读取时都会创建一个实际值的克隆。如果需要,可以通过 copyOnRead
属性禁用此行为。
由于在启用读取时复制时会为给定键的值制作一个副本,因此您必须随后调用 Region.put(key, value)
以便以事务方式更新该值。
有关底层 Geode 事务管理器的语义和行为的更多信息,请参阅 Geode CacheTransactionManager Javadoc 以及文档。
7.4 全局 JTA 事务管理
Pivotal GemFire 还可以参与基于 JTA 的全局事务,例如由 Java EE 应用程序服务器(例如 WebSphere Application Server (WAS))使用容器管理事务 (CMT) 以及其他 JTA 资源管理的事务。
但是,与许多其他 JTA“兼容”资源(例如 ActiveMQ 等 JMS 消息代理)不同,Pivotal GemFire 不是 XA 兼容资源。因此,Pivotal GemFire 必须在 JTA 事务(准备阶段)中定位为“最后一个资源”,因为它不实现两阶段提交协议,或者更确切地说,不处理分布式事务。
许多能够进行 CMT 的受管环境在基于 JTA 的事务中维护对“最后一个资源”的非 XA 兼容资源的支持,尽管 JTA 规范中实际上并不需要这样做。有关非 XA 兼容“最后一个资源”的含义的更多信息,请参阅 Red Hat 的文档。事实上,Red Hat 的 JBoss 项目 Narayana 就是这样一个 LGPL 开源实现。Narayana 将其称为“最后一个资源提交优化”(LRCO)。更多详细信息请参见此处。
但是,无论您是在具有支持“最后一个资源”的开源 JTA 事务管理实现的独立环境中使用 Pivotal GemFire,还是在受管环境(例如 Java EE AS,如 WAS)中使用,Spring Data for Pivotal GemFire 都能满足您的需求。
要正确地将 Pivotal GemFire 用作涉及多个事务性资源的 JTA 事务中的“最后资源”,您必须完成一系列步骤。此外,此类安排中只能有一个非 XA 兼容资源(例如 Pivotal GemFire)。
1) 首先,您必须在 Pivotal GemFire 文档中完成步骤 1-4,此处。
上述第 1 步独立于您的 Spring [Boot] 和/或 [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 Data for Pivotal GemFire 的新 @EnableGemFireAsLastResource
注释注释您的 Spring @Configuration
类,而 Spring 的 事务管理基础设施和 Spring Data for Pivotal GemFire 的 @EnableGemFireAsLastResource
注释配置相结合即可完成此操作。
配置如下所示…
@Configuration
@EnableGemFireAsLastResource
@EnableTransactionManagement(order = 1)
class GeodeConfiguration {
...
}
唯一的要求是…
3.1) @EnableGemFireAsLastResource
注释必须在与 Spring 的 @EnableTransactionManagement
注释也指定的同一个 Spring @Configuration
类上声明。
3.2) @EnableTransactionManagement
注释的 order
属性必须显式设置为一个整数值,该值不是 Integer.MAX_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
}
...
}
一旦您的应用程序进入 @Transactional
边界(即调用 MyTransactionService.someTransactionalServiceMethod()
时),Spring 基于 JTA 的 PlatformTransactionManager
会适当地为您处理上述步骤 1 和 4。
步骤 2 和 3 由 Spring Data for Pivotal GemFire 的新 Aspect 处理,这些 Aspect 通过 @EnableGemFireAsLastResource
注释启用。
当然,步骤 3 是您应用程序的责任。
实际上,在配置了适当的日志记录后,您将看到正确的事件顺序…
2017-Jun-22 11:11:37 TRACE TransactionInterceptor - Getting transaction for [example.app.service.MessageService.send]
2017-Jun-22 11:11:37 TRACE GemFireAsLastResourceConnectionAcquiringAspect - Acquiring {data-store-name} Connection
from {data-store-name} JCA ResourceAdapter registered at [gfe/jca]
2017-Jun-22 11:11:37 TRACE MessageService - PRODUCER [ Message :
[{ @type = example.app.domain.Message, id= MSG0000000000, message = SENT }],
JSON : [{"id":"MSG0000000000","message":"SENT"}] ]
2017-Jun-22 11:11:37 TRACE TransactionInterceptor - Completing transaction for [example.app.service.MessageService.send]
2017-Jun-22 11:11:37 TRACE GemFireAsLastResourceConnectionClosingAspect - Closed {data-store-name} Connection @ [Reference [...]]
有关使用 Pivotal GemFire 缓存级别事务的更多详细信息,请参阅此处。
有关在 JTA 事务中使用 Pivotal GemFire 的更多详细信息,请参阅此处。
有关将 Pivotal GemFire 配置为“最后资源”的更多详细信息,请参阅此处。
7.5. TransactionEventListener
在使用事务时,可能需要注册一个侦听器,以便在事务提交之前或之后,或回滚发生后执行某些操作。
Spring Data for Pivotal GemFire 使得创建侦听器变得很容易,这些侦听器将在事务的特定阶段使用 @TransactionalEventListener
注解调用。使用 @TransactionalEventListener
注释的方法(如下所示)将在指定 phase
期间收到从事务方法发布的事件的通知。
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handleAfterCommit(MyEvent event) {
// do something after transaction is committed
}
为了调用上述方法,您必须从事务中发布一个事件,如下所示
@Service
class MyTransactionalService {
@Autowired
private final ApplicationEventPublisher applicationEventPublisher;
@Transactional
public <Return-Type> someTransactionalServiceMethod() {
// Perform business logic interacting with and accessing multiple transactional resources atomically, then...
applicationEventPublisher.publishEvent(new MyEvent(...));
}
...
}
@TransactionalEventListener
注解允许您指定事件处理程序方法将被调用的事务 phase
。选项包括:AFTER_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 简化了 CQ 事件的创建、注册、生命周期和分派,通过使用 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 依赖项。它确实是我们能够并且可以通过以下配置将其变成 EDP 的 POJO。
该类不必实现接口;仅使用接口来更好地展示契约和实现之间的解耦。 |
<?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 接口列出了允许的类型。
|
上面的示例使用 Pivotal GemFire 的 Spring Data 命名空间来声明事件侦听器容器并自动注册侦听器。完整的bean定义如下所示
<!-- this is the Event Driven POJO (MDP) -->
<bean id="eventListener" class="org.springframework.data.gemfire.listener.adapter.ContinuousQueryListenerAdapter">
<constructor-arg>
<bean class="gemfireexample.DefaultEventDelegate"/>
</constructor-arg>
</bean>
<!-- and this is the event listener container... -->
<bean id="gemfireListenerContainer" class="org.springframework.data.gemfire.listener.ContinuousQueryListenerContainer">
<property name="cache" ref="gemfireCache"/>
<property name="queryListeners">
<!-- set of CQ listeners -->
<set>
<bean class="org.springframework.data.gemfire.listener.ContinuousQueryDefinition" >
<constructor-arg value="SELECT * FROM /SomeRegion" />
<constructor-arg ref="eventListener"/>
</bean>
</set>
</property>
</bean>
每次收到事件时,适配器都会在 Pivotal GemFire 事件和所需方法参数之间自动执行类型转换,而无需人工干预。由方法调用引起的任何异常都会被容器捕获并处理(默认情况下,会记录该异常)。
7.7. 连接Declarable
组件
Pivotal GemFire XML 配置(通常称为cache.xml
)允许将用户对象声明为配置的一部分。通常,这些对象是CacheLoaders
或 Pivotal GemFire 支持的其他可插入回调组件。使用本机 Pivotal GemFire 配置,通过 XML 声明的每个用户类型都必须实现Declarable
接口,该接口允许通过Properties
实例将任意参数传递给声明的类。
在本节中,我们将介绍在使用 Spring 在cache.xml
中定义这些可插入组件时如何配置这些组件,同时将 Cache/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>
为了简化解析、转换参数和初始化对象的任务,Pivotal GemFire 的 Spring Data 提供了一个基类 (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
将自动装配声明实例。这意味着,除非该实例提供了任何依赖注入元数据,否则容器将查找对象设置器并尝试自动满足这些依赖关系。但是,开发人员还可以使用 JDK 5 注释向自动装配过程提供其他信息。
我们强烈建议阅读 Spring 文档中的专门章节,以获取有关受支持注释和启用因素的更多信息。 |
例如,可以通过以下方式向假设的 DBLoader
声明注入 Spring 配置的 DataSource
class DBLoader extends WiringDeclarableSupport implements CacheLoader {
// use annotations to 'mark' the needed dependencies
@javax.inject.Inject
private DataSource dataSource;
public Object load(LoaderHelper helper) { ... }
}
<cache-loader>
<class-name>com.company.app.DBLoader</class-name>
<!-- no need to declare any parameters since the class is auto-wired -->
</cache-loader>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
">
<!-- enable annotation processing -->
<context:annotation-config/>
</beans>
通过使用 JSR-330 注释,CacheLoader
代码已简化,因为 DataSource
的位置和创建已外置,并且用户代码仅关注加载过程。DataSource
可能具有事务性、延迟创建、在多个对象之间共享或从 JNDI 检索。这些方面可以通过 Spring 容器轻松配置和更改,而无需触及 DBLoader
代码。
7.8. 支持 Spring 缓存抽象
Spring Data for Pivotal GemFire 提供了 Spring 缓存抽象的实现,以便将 Pivotal GemFire 定位为 Spring 缓存基础架构中的缓存提供程序。
若要将 Pivotal GemFire 用作后端实现,即 Spring 缓存抽象中的“缓存提供程序”,只需将 GemfireCacheManager
添加到配置中
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:cache="http://www.springframework.org/schema/cache"
xmlns:gfe="https://www.springframework.org/schema/gemfire"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/cache https://www.springframework.org/schema/cache/spring-cache.xsd
https://www.springframework.org/schema/gemfire https://www.springframework.org/schema/gemfire/spring-gemfire.xsd
">
<!-- enable declarative caching -->
<cache:annotation-driven/>
<gfe:cache id="gemfire-cache"/>
<!-- declare GemfireCacheManager; must have a bean ID of 'cacheManager' -->
<bean id="cacheManager" class="org.springframework.data.gemfire.cache.GemfireCacheManager"
p:cache-ref="gemfire-cache">
</beans>
如果使用默认缓存 Bean 名称(即“gemfireCache”),则 CacheManager Bean 定义上的 cache-ref 属性不是必需的,即 <gfe:cache> 没有显式 ID。
|
当声明 GemfireCacheManager
(单例)bean 实例并启用声明式缓存(在 XML 中使用 <cache:annotation-driven/>
或在 JavaConfig 中使用 Spring 的 @EnableCaching
注解),Spring 缓存注解(例如 @Cacheable
)会标识将使用 Pivotal GemFire 区域将数据缓存到内存中的“缓存”。
这些缓存(即区域)必须在使用它们的缓存注解之前存在,否则会发生错误。
例如,假设您有一个客户服务应用程序,其中 CustomerService
应用程序组件执行缓存…
@Service
class CustomerService {
@Cacheable(cacheNames="Accounts", key="#customer.id")
Account createAccount(Customer customer) {
...
}
然后您需要以下配置。
XML
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:cache="http://www.springframework.org/schema/cache"
xmlns:gfe="https://www.springframework.org/schema/gemfire"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/cache https://www.springframework.org/schema/cache/spring-cache.xsd
https://www.springframework.org/schema/gemfire https://www.springframework.org/schema/gemfire/spring-gemfire.xsd
">
<!-- enable declarative caching -->
<cache:annotation-driven/>
<bean id="cacheManager" class="org.springframework.data.gemfire.cache.GemfireCacheManager">
<gfe:cache/>
<gfe:partitioned-region id="accountsRegion" name="Accounts" persistent="true" ...>
...
</gfe:partitioned-region>
</beans>
JavaConfig
@Configuration
@EnableCaching
class ApplicationConfiguration {
@Bean
CacheFactoryBean gemfireCache() {
return new CacheFactoryBean();
}
@Bean
GemfireCacheManager cacheManager() {
GemfireCacheManager cacheManager = GemfireCacheManager();
cacheManager.setCache(gemfireCache());
return cacheManager;
}
@Bean("Accounts")
PartitionedRegionFactoryBean accountsRegion() {
PartitionedRegionFactoryBean accounts = new PartitionedRegionFactoryBean();
accounts.setCache(gemfireCache());
accounts.setClose(false);
accounts.setPersistent(true);
return accounts;
}
}
当然,您可以自由选择您喜欢的任何区域类型(例如 REPLICATE、PARTITION、LOCAL 等)。
有关Spring 缓存抽象的更多详细信息,请参阅 文档。
8. 使用 Pivotal GemFire 序列化
为了提高 Pivotal GemFire 内存数据网格的整体性能,Pivotal GemFire 支持一个专用的序列化协议,称为 PDX,它比标准 Java 序列化更快,并提供更紧凑的结果,此外还可以在各种语言平台(Java、C++ 和 .NET)上透明地工作。
本章讨论了 Spring Data for Pivotal GemFire 简化和改进 Java 中 Pivotal GemFire 自定义序列化的各种方式。
8.1. 连接反序列化的实例
序列化对象具有瞬态数据是很常见的。瞬态数据通常依赖于它在某个时间点所在的系统或环境。例如,DataSource
是特定于环境的。序列化此类信息是无用的,甚至可能是危险的,因为它位于某个 VM 或计算机上。对于这种情况,Spring Data for Pivotal GemFire 提供了一个特殊的 Instantiator
,它为 Pivotal GemFire 在反序列化期间创建的每个新实例执行连接。
通过这种机制,您可以依靠 Spring 容器来注入和管理某些依赖项,从而可以轻松地将瞬态数据与持久性数据分开,并以透明的方式拥有丰富的域对象。
Spring 用户可能会发现这种方法类似于 @Configurable
的方法)。WiringInstantiator
的工作方式类似于 WiringDeclarableSupport
,它会首先尝试将 Bean 定义定位为布线模板,否则会回退到自动布线。
请参阅上一部分(布线 Declarable
组件)以了解有关布线功能的更多详细信息。
要使用 SDG Instantiator
,请将其声明为 Bean,如下例所示
<bean id="instantiator" class="org.springframework.data.gemfire.serialization.WiringInstantiator">
<!-- DataSerializable type -->
<constructor-arg>org.pkg.SomeDataSerializableClass</constructor-arg>
<!-- type id -->
<constructor-arg>95</constructor-arg>
</bean>
在 Spring 容器启动期间,一旦它被初始化,Instantiator
就会默认向 Pivotal GemFire 序列化系统注册自身,并在反序列化期间由 Pivotal GemFire 创建的 SomeDataSerializableClass
的所有实例上执行布线。
8.2. 自动生成自定义 Instantiators
对于数据密集型应用程序,随着数据流入,每个计算机上可能会创建大量实例。Pivotal GemFire 使用反射来创建新类型,但对于某些场景,这可能会很昂贵。一如既往,最好执行分析以量化这种情况是否属实。对于这种情况,Spring Data for Pivotal GemFire 允许自动生成 Instantiator
类,该类在不使用反射的情况下实例化新类型(使用默认构造函数)。以下示例演示如何创建实例化器
<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
)生成了两个 Instantiators
,并将它们在用户 ID 1025
和 1026
下注册到 Pivotal GemFire。这两个 Instantiators
避免使用反射,并直接通过 Java 代码创建实例。
9. POJO 映射
本节涵盖
9.1. 对象映射基础知识
本节涵盖 Spring Data 对象映射、对象创建、字段和属性访问、可变性和不可变性的基础知识。请注意,本节仅适用于不使用底层数据存储的对象映射的 Spring Data 模块(如 JPA)。此外,请务必查阅特定于存储的部分以了解特定于存储的对象映射,如索引、自定义列或字段名称等。
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 方法弄得一团糟。如果你需要这些方法,最好将它们设为包保护,以便它们只能被有限数量的共置类型调用。仅使用构造函数实现比属性填充快 30%。
-
提供一个 all-args 构造函数 — 即使你不能或不想将你的实体建模为不可变值,提供一个将实体的所有属性(包括可变属性)作为参数的构造函数仍然有价值,因为这允许对象映射跳过属性填充以获得最佳性能。
-
使用工厂方法而不是重载构造函数来避免
@PersistenceConstructor
— 由于需要一个 all-argument 构造函数来获得最佳性能,我们通常希望公开更多应用程序用例特定的构造函数,这些构造函数省略自动生成标识符等内容。这是一个既定的模式,即使用静态工厂方法来公开 all-args 构造函数的这些变体。 -
确保你遵守允许使用生成的实例化器和属性访问器类的约束 —
-
对于要生成的标识符,仍然使用 final 字段与 wither 方法结合使用 —
-
使用 Lombok 避免样板代码 — 由于持久性操作通常需要一个接受所有参数的构造函数,因此它们的声明变成了样板参数到字段赋值的乏味重复,最好使用 Lombok 的
@AllArgsConstructor
来避免这种情况。
9.1.4. Kotlin 支持
Spring Data 调整了 Kotlin 的具体内容以允许对象创建和变异。
Kotlin 对象创建
支持实例化 Kotlin 类,默认情况下所有类都是不可变的,并且需要显式属性声明来定义可变属性。考虑以下 data
类 Person
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("People")
public class Person {
@Id Long id;
String firstname;
String lastname;
@PersistenceConstructor
public Person(String firstname, String lastname) {
// …
}
…
}
@Region
注释可用于定制存储 Person
类的实例的区域。@Id
注释可用于注释应作为缓存区域键使用的属性,标识区域条目。@PersistenceConstructor
注释有助于消除歧义,即多个可能可用的构造函数,获取参数并显式标记注释为构造函数的构造函数作为用于构造实体的构造函数。在没有或仅具有单个构造函数的应用程序域类中,可以省略注释。
除了将实体存储在顶级区域中之外,还可以将实体存储在子区域中,如下例所示
@Region("/Users/Admin")
public class Admin extends User {
…
}
@Region("/Users/Guest")
public class Guest extends User {
…
}
请务必使用 Pivotal GemFire 区域的完整路径,如 Spring Data for Pivotal GemFire XML 命名空间通过使用 <*-region>
元素的 id
或 name
属性定义的那样。
9.2.1. 按区域类型进行实体映射
除了 @Region
注释之外,Spring Data for Pivotal GemFire 还识别特定于类型的区域映射注释:@ClientRegion
、@LocalRegion
、@PartitionRegion
和 @ReplicateRegion
。
在功能上,这些注释在 SDG 映射基础架构中与通用 @Region
注释的处理方式完全相同。但是,这些附加映射注释在 Spring Data for Pivotal GemFire 的注释配置模型中非常有用。当与 Spring @Configuration
注释类上的 @EnableEntityDefinedRegions
配置注释结合使用时,可以在本地缓存中生成区域,无论应用程序是客户端还是对等方。
这些注释让你可以更具体地指定应用程序实体类应映射到的区域类型,并且还对区域的数据管理策略产生影响(例如,分区(也称为分片)与复制数据)。
将这些特定于类型的区域映射注释与 SDG 注释配置模型一起使用,可以让你不必在配置中显式定义这些区域。
9.3. 存储库映射
作为在实体类上使用 @Region
注释来指定实体存储在其中的区域的替代方法,你还可以对实体的 Repository
接口指定 @Region
注释。有关更多详细信息,请参阅 Spring Data for Pivotal GemFire 存储库。
但是,假设你想要将 Person
记录存储在多个 Pivotal GemFire 区域中(例如,People
和 Customers
)。然后,你可以按如下方式定义相应的 Repository
接口扩展
@Region("People")
public interface PersonRepository extends GemfireRepository<Person, String> {
…
}
@Region("Customers")
public interface CustomerRepository extends GemfireRepository<Person, String> {
...
}
然后,单独使用每个存储库,你可以将实体存储在多个 Pivotal GemFire 区域中,如下面的示例所示
@Service
class CustomerService {
CustomerRepository customerRepo;
PersonRepository personRepo;
Customer update(Customer customer) {
customerRepo.save(customer);
personRepo.save(customer);
return customer;
}
你甚至可以将 update
服务方法包装在 Spring 托管事务中,作为本地缓存事务或全局事务。
9.4. MappingPdxSerializer
Spring Data for Pivotal GemFire 提供了一个自定义 PdxSerializer
实现,称为 MappingPdxSerializer
,它使用 Spring Data 映射元数据来自定义实体序列化。
该序列化程序还允许你使用 Spring Data EntityInstantiator
抽象来自定义实体实例化。默认情况下,序列化程序使用 ReflectionEntityInstantiator
,它使用映射实体的持久性构造函数。持久性构造函数是默认构造函数、单一声明的构造函数或显式注释为 @PersistenceConstructor
的构造函数。
为了为构造函数参数提供参数,序列化程序使用 Spring 的 @Value
注释从提供的 PdxReader
读取具有命名构造函数参数的字段,显式地标识,如下面的示例所示
@Value
public class Person {
public Person(@Value("#root.thing") String firstName, @Value("bean") String lastName) {
…
}
}
以这种方式注释的实体类从 PdxReader
读取“thing”字段,并将其作为构造函数参数 firstname
的参数值传递。lastName
的值是名称为“bean”的 Spring bean。
除了 EntityInstantiators
提供的自定义实例化逻辑和策略之外,MappingPdxSerializer
还提供了远远超出 Pivotal GemFire 自身的 ReflectionBasedAutoSerializer
的功能。
虽然 Pivotal GemFire 的 ReflectionBasedAutoSerializer
方便地使用 Java 反射填充实体,并使用正则表达式识别序列化器应处理(序列化和反序列化)的类型,但它无法像 MappingPdxSerializer
那样执行以下操作
-
根据实体字段或属性名称和类型注册自定义
PdxSerializer
对象。 -
方便地识别 ID 属性。
-
自动处理只读属性。
-
自动处理瞬态属性。
-
允许以
null
和类型安全的方式进行更健壮的类型过滤(例如,不限于仅使用正则表达式表示类型)。
我们现在更详细地探讨 MappingPdxSerializer
的每个功能。
9.4.1. 自定义 PdxSerializer 注册
MappingPdxSerializer
使您能够根据实体的字段或属性名称和类型注册自定义 PdxSerializers
。
例如,假设您已定义一个实体类型,将 User
建模如下
package example.app.security.auth.model;
public class User {
private String name;
private Password password;
...
}
虽然用户的名称可能不需要任何特殊逻辑来序列化值,但另一方面,序列化密码可能需要额外的逻辑来处理该字段或属性的敏感性质。
也许您希望在通过网络(在客户端和服务器之间)发送值时保护密码,而不仅仅是 TLS,并且您只想存储加盐哈希。在使用 MappingPdxSerializer
时,您可以注册一个自定义 PdxSerializer
来处理用户的密码,如下所示
PdxSerializers
Map<?, PdxSerializer> customPdxSerializers = new HashMap<>();
customPdxSerializers.put(Password.class, new SaltedHashPasswordPdxSerializer());
mappingPdxSerializer.setCustomPdxSerializers(customPdxSerializers);
在将应用程序定义的 SaltedHashPasswordPdxSerializer
实例与 Password
应用程序域模型类型注册后,MappingPdxSerializer
将咨询自定义 PdxSerializer
以序列化和反序列化所有 Password
对象,无论包含对象如何(例如,User
)。
但是,假设您只想在 User
对象上自定义 Passwords
的序列化。为此,您可以通过指定 Class
字段或属性的完全限定名称为 User
类型注册自定义 PdxSerializer
,如下例所示
PdxSerializers
Map<?, PdxSerializer> customPdxSerializers = new HashMap<>();
customPdxSerializers.put("example.app.security.auth.model.User.password", new SaltedHashPasswordPdxSerializer());
mappingPdxSerializer.setCustomPdxSerializers(customPdxSerializers);
请注意使用完全限定的字段或属性名称(即 example.app.security.auth.model.User.password
)作为自定义 PdxSerializer
注册密钥。
您可以使用更具逻辑性的代码片段来构造注册密钥,例如以下代码:User.class.getName().concat(".password"); 。我们建议您使用此方法,而不是前面显示的示例。前面的示例尝试尽可能明确地说明注册的语义。
|
9.4.2. 映射 ID 属性
与 Pivotal GemFire 的 ReflectionBasedAutoSerializer
类似,SDG 的 MappingPdxSerializer
也能够确定实体的标识符。但是,MappingPdxSerializer
通过使用 Spring Data 的映射元数据来实现此目的,具体来说,通过使用 Spring Data 的 @Id
注释来查找指定为标识符的实体属性。或者,任何未显式使用 @Id
注释的字段或属性也指定为实体的标识符。
例如
class Customer {
@Id
Long id;
...
}
在这种情况下,Customer
id
字段在 PDX 类型元数据中标记为标识符字段,方法是在序列化期间调用 PdxSerializer.toData(..)
方法时使用 PdxWriter.markIdentifierField(:String)
。
9.4.3. 映射只读属性
如果您的实体定义了一个只读属性,会发生什么情况?
首先,了解什么是“只读”属性非常重要。如果您按照 JavaBeans 规范(Spring 也是如此)来定义 POJO,则可以定义具有只读属性的 POJO,如下所示
package example;
class ApplicationDomainType {
private AnotherType readOnly;
public AnotherType getReadOnly() [
this.readOnly;
}
...
}
readOnly
属性是只读的,因为它不提供 setter 方法。它只具有 getter 方法。在这种情况下,readOnly
属性(不要与 readOnly
DomainType
字段混淆)被视为只读。
因此,在反序列化期间,MappingPdxSerializer
不会尝试在 PdxSerializer.fromData(:Class<ApplicationDomainType>, :PdxReader)
方法中填充 ApplicationDomainType
实例时为此属性设置值,特别是如果 PDX 序列化字节中存在值。
这在您可能返回某种实体类型的视图或投影并且只想设置可写状态的情况下非常有用。实体的视图或投影可能基于授权或其他一些条件。关键是,您可以根据应用程序的用例和要求适当利用此功能。如果您希望始终写入字段或属性,只需定义一个 setter 方法即可。
9.4.4. 映射瞬态属性
同样,当实体定义了 transient
属性时会发生什么?
您希望在序列化实体时,实体的 transient
字段或属性不会序列化到 PDX。这正是发生的情况,这与 Pivotal GemFire 自身的 ReflectionBasedAutoSerializer
不同,后者会通过 Java 反射序列化从对象访问的所有内容。
MappingPdxSerializer
不会序列化任何被限定为瞬态的字段或属性,无论使用 Java 自身的 transient
关键字(对于类实例字段)还是使用 @Transient
Spring Data 注释(对于字段或属性)。
例如,您可以按如下方式定义具有瞬态字段和属性的实体
package example;
class Process {
private transient int id;
private File workingDirectory;
private String name;
private Type type;
@Transient
public String getHostname() {
...
}
...
}
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
,用于筛选或排除以下包中的类型
-
java.*
-
com.gemstone.gemfire.*
-
org.apache.geode.*
-
org.springframework.*
此外,在调用 PdxSerializer.toData(:Object, :PdxWriter)
和 PdxSerializer.fromData(:Class<?>, :PdxReader)
方法时,MappingPdxSerializer
会筛选 null
对象和 null
类类型。
通过简单定义一个 Predicate
并将其添加到 MappingPdxSerializer
中,可以非常轻松地为其他类类型或整个类型包添加排除项,如前所述。
MappingPdxSerializer.setExcludeTypeFilters(:Predicate<Class<?>>)
方法是累加的,这意味着它使用 Predicate.and(:Predicate<Class<?>>)
方法将应用程序定义的类型筛选器与上述现有的预定义类型筛选器 Predicates
组成在一起。
但是,如果你想包含一个类类型(例如,java.security Principal
),而该类类型被排除类型筛选器隐式排除,该怎么办?请参阅 包含类型筛选。
包含类型筛选
如果你想显式包含一个类类型,或覆盖一个类类型筛选器,该筛选器隐式排除了应用程序所需的类类型(例如,java.security.Principal
,该类类型在 MappingPdxSerializer
上默认情况下被 java.*
包排除类型筛选器排除),那么只需定义适当的 Predicate
并使用 MappingPdxSerializer.setIncludeTypeFilters(:Predicate<Class<?>>)
方法将其添加到序列化器中,如下所示
Predicate<Class<?>> principalTypeFilter =
type -> java.security.Principal.class.isAssignableFrom(type);
mappingPdxSerializer.setIncludeTypeFilters(principalTypeFilters);
同样,MappingPdxSerializer.setIncludeTypeFilters(:Predicate<Class<?>>)
方法与 setExcludeTypeFilters(:Predicate<Class<?>>)
一样,也是累加的,因此使用 Predicate.or(:Predicate<Class<?>>)
组成任何传递的类型筛选器。这意味着你可以根据需要多次调用 setIncludeTypeFilters(:Predicate<Class<?>>)
。
当存在包含类型筛选器时,MappingPdxSerializer
会决定是否对类类型的实例进行反序列化/序列化,具体取决于类类型是否未被隐式排除或类类型是否被显式包含,以返回 true 的为准。然后,将适当地序列化或反序列化类类型的实例。
例如,当 Predicate<Class<Principal>>
的类型筛选器被显式注册如前所示时,它将取消对 java.*
包类型的隐式排除类型筛选器。
10. Pivotal GemFire 存储库的 Spring 数据
Spring Data for Pivotal GemFire 提供对使用 Spring Data Repository 抽象的支持,以便轻松将实体持久化到 Pivotal GemFire,并执行查询。此处提供了 Repository 编程模型的一般介绍 here。
10.1. Spring XML 配置
要引导 Spring Data Repositories,请使用 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>
前面的配置片段查找配置基本包下的接口,并为这些接口创建 Repository 实例,这些实例由 SimpleGemFireRepository
支持。
除非将应用程序域类正确映射到已配置的区域,否则引导过程将失败。 |
10.2. Spring 基于 Java 的配置
或者,许多开发人员更喜欢使用 Spring 的 基于 Java 的容器配置。
使用此方法,你可以使用 SDG @EnableGemfireRepositories
注解来引导 Spring Data Repositories,如下例所示
@EnableGemfireRepositories
引导 Spring Data for Pivotal GemFire Repositories@SpringBootApplication
@EnableGemfireRepositories(basePackages = "com.example.acme.repository")
class SpringDataApplication {
...
}
与其使用 basePackages
属性,你可能更愿意使用类型安全的 basePackageClasses
属性。basePackageClasses
允许你通过仅指定一个应用程序 Repository 接口类型来指定包含所有应用程序 Repository 类别的包。考虑在每个包中创建一个特殊的无操作标记类或接口,除了标识此属性引用的应用程序 Repository 的位置之外,它没有其他用途。
除了 basePackages 和 basePackageClasses
属性之外,与 Spring 的 @ComponentScan
注解类似,@EnableGemfireRepositories
注解提供基于 Spring 的 ComponentScan.Filter
类型的包含和排除筛选器。你可以使用 filterType
属性按不同方面进行筛选,例如应用程序 Repository 类型是否用特定注解进行了注解,或是否扩展了特定类类型等。有关更多详细信息,请参阅 FilterType
Javadoc。
@EnableGemfireRepositories
注解还允许你通过使用 namedQueriesLocation
属性指定驻留在 Java Properties
文件中的命名 OQL 查询的位置。属性名称必须与 Repository 查询方法的名称匹配,属性值是你希望在调用 Repository 查询方法时执行的 OQL 查询。
如果你的应用程序需要一个或多个 自定义存储库实现,则可以将 repositoryImplementationPostfix
属性设置为备用值(默认为 Impl
)。此功能通常用于扩展 Spring Data Repository 基础设施,以实现数据存储未提供的功能(例如 SDG)。
使用 Pivotal GemFire 执行联接时,需要自定义存储库实现的一个示例。SDG 存储库不支持联接。对于 Pivotal GemFire PARTITION
区域,必须在并置的 PARTITION
区域上执行联接,因为 Pivotal GemFire 不支持“分布式”联接。此外,Equi-Join OQL 查询必须在 Pivotal GemFire 函数内执行。有关 Pivotal GemFire Equi-Join 查询 的更多详细信息,请参阅此处。
SDG 的存储库基础设施扩展的许多其他方面也可以自定义。有关所有配置设置的更多详细信息,请参阅@EnableGemfireRepositories
Javadoc。
10.3. 执行 OQL 查询
Spring Data for Pivotal GemFire 存储库支持定义查询方法,以便针对受管理实体映射到的区域轻松执行 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 的存储库基础架构直接不支持的扩展。
Spring Data Commons 的存储库基础架构目标之一是作为最低公分母,以维护对当今应用程序开发中可用和正在使用的最广泛数据存储的支持和可移植性。从技术上讲,这意味着开发人员可以通过重复使用其现有的特定于应用程序的存储库接口,在应用程序中访问 Spring Data Commons 支持的多个不同数据存储——这是一种方便且强大的抽象。
为了支持 Pivotal GemFire 的 OQL 查询语言扩展并保留跨不同数据存储的可移植性,Spring Data for Pivotal GemFire 通过使用 Java 注释添加了对 OQL 查询扩展的支持。这些注释被其他 Spring Data 存储库实现(例如 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 区域以及一个 `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
Spring Data for Pivotal GemFire 的存储库扩展在将 OQL 注释扩展与 `@Query` 注释结合使用时,会小心不创建冲突的声明。
另一个示例,假设您在 `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` 注释是多余的,没有其他效果。
鉴于可能有多个客户的声誉值相同,从而降低了索引的有效性,因此 `ReputationIdx` 索引可能不是最明智的索引。请明智地选择索引和其他优化,因为不当或选择不当的索引可能会对您的性能产生相反的影响,因为维护索引会产生开销。 `ReputationIdx` 仅用于示例的目的。 |
10.5. 查询后处理
由于使用了 Spring Data 存储库抽象,因此用于定义特定于数据存储的查询(例如 OQL)的查询方法约定既简单又方便。但是,有时仍然需要检查甚至可能修改从存储库查询方法生成的查询。
自 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);
}
提供其他默认方法,让你可以类似于 java.util.function.Function.andThen(:Function) 和 java.util.function.Function.compose(:Function) 的工作方式来组合 QueryPostProcessor
实例。
此外,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
。
您可能希望为存储库查询方法定义的特定查询创建一个 QueryPostProcessor
。例如,假设您希望将从 CustomerRepository.findByLastNameLike(:String)
查询方法生成的 OQL 查询限制为仅返回五个结果,同时按升序对 Customers
按 firstName
排序。为此,您可以定义一个自定义 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 存储库约定来实现相同的效果。例如,可以按如下方式定义相同的查询
interface CustomerRepository extends CrudRepository<Customer, Long> {
@Limit(5)
List<Customer> findDistinctByLastNameLikeOrderByFirstNameDesc(String lastName);
}
但是,如果您无法控制应用程序 CustomerRepository
接口定义,那么 QueryPostProcessor
(即 OrderedLimitedCustomerByLastNameQueryPostProcessor
)会很方便。
如果您希望确保 LoggingQueryPostProcessor
始终位于其他应用程序定义的 QueryPostProcessors
之后,这些 QueryPostProcessors
可能已在 Spring ApplicationContext
中声明和注册为 bean,则可以通过覆盖 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
,并按任何顺序将它们应用于所有或特定的应用程序存储库接口,并且可以通过向 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 的公有方法绑定到函数。用作函数的 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) { ... }
一般规则是,一旦解析了任何附加参数(即,区域数据和过滤器),剩余的参数必须与预期的 Function 方法参数在顺序和类型上完全对应。方法的返回类型必须是 void 或可序列化的类型(如 java.io.Serializable
、DataSerializable
或 PdxSerializable
)。后者也是调用参数的要求。
区域数据通常应定义为 Map
,以方便单元测试,但如有必要,也可以是 Region 类型。如前例所示,如果需要控制如何将结果返回给客户端,也可以传递 FunctionContext
本身或 ResultSender
。
11.2.1. 函数实现的注释
以下示例展示了如何使用 SDG 的 Function 注释将 POJO 方法公开为 Pivotal GemFire Function
@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 Function 都使用 @GemfireFunction
注释。在前一个示例中,使用了 Spring 的 @Component
注释,但你可以使用 Spring 支持的任何方法(例如 XML 配置或在使用 Spring Boot 时使用 Java 配置类)注册 bean。这允许 Spring 容器创建此类的实例,并将其包装在 PojoFunctionWrapper
中。Spring 为使用 @GemfireFunction
注释的每个方法创建一个包装器实例。每个包装器实例共享同一个目标对象实例,以调用相应的方法。
POJO Function 类是 Spring bean 的事实可能带来其他好处。由于它与 Pivotal GemFire 组件(例如缓存和区域)共享 ApplicationContext ,因此如有必要,可以将这些组件注入到类中。
|
Spring 创建包装器类,并将 Function 注册到 Pivotal GemFire 的 FunctionService
中。用于注册每个 Function 的 Function ID 必须是唯一的。通过使用约定,它默认为简单(不合格)的方法名称。可以使用 @GemfireFunction
注释的 id
属性显式定义名称。
@GemfireFunction
注释还提供了其他配置属性:HA
和 optimizedForWrite
,它们对应于 Pivotal GemFire 的 Function
接口定义的属性。
如果 POJO Function 方法的返回类型是 void
,则 hasResult
属性会自动设置为 false
。否则,如果方法返回一个值,则 hasResult
属性会设置为 true
。即使对于 void
方法返回类型,GemfireFunction
注释的 hasResult
属性也可以设置为 true
以覆盖此约定,如之前所示的 functionWithContext
方法中所示。据推测,你的目的是直接使用 ResultSender
向调用者发送结果。
最后,GemfireFunction
注释支持 requiredPermissions
属性,该属性指定执行函数所需的权限。默认情况下,所有函数都需要 DATA:WRITE
权限。该属性接受一个字符串数组,允许您根据应用程序和/或函数 UC 的要求修改权限。每个资源权限应采用以下格式:<RESOURCE>:<OPERATION>:[Target]:[Key]
。
RESOURCE
可以是 {data-store-javadoc]/org/apache/geode/security/ResourcePermission.Resource.html[ResourcePermission.Resource
] 枚举值之一。OPERATION
可以是 {data-store-javadoc]/org/apache/geode/security/ResourcePermission.Operation.html[ResourcePermission.Operation
] 枚举值之一。Target
可以是区域的名称或 {data-store-javadoc]/org/apache/geode/security/ResourcePermission.Target.html[ResourcePermission.Target
] 枚举值之一(可选)。最后,Key
是 Target
区域中的有效键(如果指定)。
PojoFunctionWrapper
实现 Pivotal GemFire 的 Function
接口,绑定方法参数,并在其 execute()
方法中调用目标方法。它还使用 ResultSender
将方法的返回值发送回调用方。
11.2.2. 批处理结果
如果返回类型是数组或 Collection
,则必须考虑如何返回结果。默认情况下,PojoFunctionWrapper
一次返回整个数组或 Collection
。如果数组或 Collection
中的元素数量很大,则可能会产生性能损失。要将有效负载分成更小、更易于管理的块,您可以设置 batchSize
属性,如前面所示的 function2
中所示。
如果您需要对 ResultSender 进行更多控制,特别是如果方法本身使用过多的内存来创建 Collection ,则可以传入 ResultSender 或通过 FunctionContext 访问它,并在方法中直接使用它将结果发送回调用方。
|
11.2.3. 启用注释处理
根据 Spring 标准,您必须显式激活 @GemfireFunction
注释的注释处理。以下示例使用 XML 激活注释处理
<gfe:annotation-driven/>
以下示例通过注释 Java 配置类来激活注释处理
@Configuration
@EnableGemfireFunctions
class ApplicationConfiguration { ... }
11.3. 执行函数
调用远程函数的进程需要提供函数的 ID、调用参数、执行目标(onRegion
、onServers
、onServer
、onMember
或 onMembers
)和(可选)过滤器集。通过使用 Spring Data for Pivotal GemFire,您只需要定义一个受注释支持的接口。Spring 为该接口创建一个动态代理,该代理使用 FunctionService
创建 Execution
、调用 Execution
,并(如果需要)将结果强制转换为定义的返回类型。此技术类似于 Spring Data for Pivotal GemFire 的 Repository 扩展工作的方式。因此,一些配置和概念应该是熟悉的。
通常,单个接口定义映射到多个函数执行,每个函数执行对应于接口中定义的一个方法。
11.3.1. 函数执行的注释
为了支持客户端函数执行,提供了以下 SDG 函数注解:@OnRegion
、@OnServer
、@OnServers
、@OnMember
和 @OnMembers
。这些注解对应于 Pivotal GemFire 的 FunctionService
类提供的 Execution
实现。
每个注解都公开适当的属性。这些注解还提供了一个可选的 resultCollector
属性,其值为实现 ResultCollector
接口的 Spring Bean 的名称,用于执行。
代理接口将所有声明的方法绑定到相同的执行配置。虽然预计单方法接口很常见,但接口中的所有方法都由相同的代理实例支持,因此所有方法都共享相同的配置。 |
以下清单显示了一些示例
@OnRegion(region="SomeRegion", resultCollector="myCollector")
public interface FunctionExecution {
@FunctionId("function1")
String doIt(String s1, int i2);
String getString(Object arg1, @Filter Set<Object> keys);
}
默认情况下,函数 ID 是简单(不合格)的方法名称。@FunctionId
注解可用于将此调用绑定到不同的函数 ID。
11.3.2. 启用注释处理
客户端使用 Spring 的类路径组件扫描功能来发现带注解的接口。要在 XML 中启用函数执行注解处理,请在 XML 配置中插入以下元素
<gfe-data:function-executions base-package="org.example.myapp.gemfire.functions"/>
function-executions
元素在 gfe-data
XML 命名空间中提供。base-package
属性是必需的,以避免扫描整个类路径。可以提供其他过滤器,如 Spring 中所述 参考文档。
或者,您可以按如下方式注释您的 Java 配置类
@EnableGemfireFunctionExecutions(basePackages = "org.example.myapp.gemfire.functions")
11.4. 程序化函数执行
使用上一节中定义的函数执行带注解的接口,只需将您的接口自动装配到将调用函数的应用程序 Bean 中即可
@Component
public class MyApplication {
@Autowired
FunctionExecution functionExecution;
public void doSomething() {
functionExecution.doIt("hello", 123);
}
}
或者,您可以直接使用函数执行模板。在以下示例中,GemfireOnRegionFunctionTemplate
创建了一个 onRegion
函数 Execution
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
实例(即 <gfe:client-cache/>
)调用的。这意味着函数参数也必须是可序列化的。在群集中对等成员函数(如 @OnMember(s)
)之间进行调用时也是如此。任何形式的 distribution
都要求在客户端和服务器(或对等方)之间传输的数据是序列化的。
现在,如果您已将 Pivotal GemFire 配置为使用 PDX 进行序列化(例如,而不是 Java 序列化),则还可以将 Pivotal GemFire 服务器的配置中的 pdx-read-serialized
属性设置为 true
,如下所示
<gfe:cache ... pdx-read-serialized="true"/>
或者,您可以将 Pivotal GemFire 缓存客户端应用程序的 pdx-read-serialized
属性设置为 true
,如下所示
<gfe:client-cache ... pdx-read-serialized="true"/>
这样做会导致从缓存(即区域)读取的所有值以及在客户端和服务器(或对等方)之间传递的信息保持序列化的形式,包括但不限于函数参数。
Pivotal GemFire 仅序列化您已使用 Pivotal GemFire 的 ReflectionBasedAutoSerializer
或具体地(且建议)使用“自定义”Pivotal GemFire PdxSerializer
特别配置(注册)的应用程序域对象类型。如果您使用 Spring Data for Pivotal GemFire 的 Repository 扩展,您甚至可能想要考虑使用 Spring Data for Pivotal GemFire 的 MappingPdxSerializer
,它使用实体的映射元数据来确定从序列化到 PDX 实例的应用程序域对象中的数据。
然而,不太明显的是,Pivotal GemFire 自动处理 Java Enum
类型,无论它们是否已显式配置(即使用 ReflectionBasedAutoSerializer
、正则表达式模式和 classes
参数注册,或由“自定义”Pivotal GemFire PdxSerializer
处理),尽管 Java 枚举实现了 java.io.Serializable
。
因此,当您在注册了 Pivotal GemFire 函数(包括 Spring Data for Pivotal GemFire 函数注释的 POJO 类)的 Pivotal GemFire 服务器上将 pdx-read-serialized
设置为 true
时,在调用函数 Execution
时可能会遇到意外的行为。
调用函数时,您可能会传递以下参数
orderProcessingFunctions.process(new Order(123, customer, LocalDateTime.now(), items), OrderSource.ONLINE, 400);
然而,服务器上的 Pivotal GemFire 函数获取以下内容
process(regionData, order:PdxInstance, :PdxInstanceEnum, 400);
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
来对您的序列化策略保持更精细的控制。
最后,Spring Data for Pivotal GemFire 谨慎地不转换您的函数参数,如果您将函数参数泛型化处理或作为 Pivotal GemFire 的 PDX 类型之一处理,如下所示
@GemfireFunction
public Object genericFunction(String value, Object domainObject, PdxInstanceEnum enum) {
...
}
Spring Data for Pivotal GemFire 仅当相应的应用程序域类型在类路径上并且函数注释的 POJO 方法期望它时,才将 PDX 类型的数据转换为相应的应用程序域类型。
有关自定义、组合的特定于应用程序的 Pivotal GemFire PdxSerializers
以及基于方法签名的适当 POJO 函数参数类型处理的良好示例,请参阅 Spring Data for Pivotal GemFire 的 ClientCacheFunctionExecutionWithPdxIntegrationTest
类。
12. Apache Lucene 集成
Pivotal GemFire 与 Apache Lucene 集成,以便您可以使用 Lucene 查询对存储在 Pivotal GemFire 中的数据进行索引和搜索。基于搜索的查询还包括浏览查询结果的能力。
此外,Spring Data for Pivotal GemFire 添加了对基于 Spring Data Commons 投影基础设施的查询投影的支持。此功能允许根据应用程序需要将查询结果投影到一流的应用程序域类型中。
在运行任何基于 Lucene 搜索的查询之前,必须创建一个 Lucene Index
。可以在 Spring(Data for Pivotal GemFire)XML 配置中创建 LuceneIndex
,如下所示
<gfe:lucene-index id="IndexOne" fields="fieldOne, fieldTwo" region-path="/Example"/>
此外,Apache Lucene 允许为每个字段指定 分析器,并且可以按以下示例中所示进行配置
<gfe:lucene-index id="IndexTwo" lucene-service-ref="luceneService" region-path="/AnotherExample">
<gfe:field-analyzers>
<map>
<entry key="fieldOne">
<bean class="example.AnalyzerOne"/>
</entry>
<entry key="fieldTwo">
<bean class="example.AnalyzerTwo"/>
</entry>
</map>
</gfe:field-analyzers>
</gfe:lucene-index>
Map
可以指定为顶级 bean 定义,并使用嵌套的 <gfe:field-analyzers>
元素中的 ref
属性进行引用,如下所示:<gfe-field-analyzers ref="refToTopLevelMapBeanDefinition"/>
。
Spring Data for Pivotal GemFire 的 LuceneIndexFactoryBean
API 和 SDG 的 XML 命名空间还允许在创建 LuceneIndex
时指定 org.apache.geode.cache.lucene.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 配置中声明或定义 LuceneIndex
,在 @Configuration
类中,如下例所示
@Bean(name = "Books")
@DependsOn("bookTitleIndex")
PartitionedRegionFactoryBean<Long, Book> booksRegion(GemFireCache gemfireCache) {
PartitionedRegionFactoryBean<Long, Book> peopleRegion =
new PartitionedRegionFactoryBean<>();
peopleRegion.setCache(gemfireCache);
peopleRegion.setClose(false);
peopleRegion.setPersistent(false);
return peopleRegion;
}
@Bean
LuceneIndexFactoryBean bookTitleIndex(GemFireCache gemFireCache,
LuceneSerializer luceneSerializer) {
LuceneIndexFactoryBean luceneIndex = new LuceneIndexFactoryBean();
luceneIndex.setCache(gemFireCache);
luceneIndex.setFields("title");
luceneIndex.setLuceneSerializer(luceneSerializer);
luceneIndex.setRegionPath("/Books");
return luceneIndex;
}
@Bean
CustomLuceneSerializer myLuceneSerialier() {
return new CustomeLuceneSerializer();
}
Pivotal GemFire、Apache Lucene 集成和支持有一些限制。
首先,只能在 Pivotal GemFire PARTITION
区域上创建 LuceneIndex
。
其次,必须在应用 LuceneIndex
的区域之前创建所有 LuceneIndexes
。
为了帮助确保在 Spring 容器中定义的所有已声明 LuceneIndexes 在其应用的区域之前创建,SDG 包含 org.springframework.data.gemfire.config.support.LuceneIndexRegionBeanFactoryPostProcessor 。您可以在 XML 配置中使用 <bean class="org.springframework.data.gemfire.config.support.LuceneIndexRegionBeanFactoryPostProcessor"/> 注册此 Spring 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 。
|
现在,一旦我们有了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());
}
}
此外,根据您的应用程序视图,您可能有一个单一的接口来将人员表示为客户
,如下所示
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;
}
那么您可以查询人员作为人员
对象,如下所示
List<Person> people = luceneTemplate.query("lastName: D*", "lastName", Person.class);
或者,您可以查询客户
类型的页面
,如下所示
Page<Customer> customers = luceneTemplate.query("lastName: D*", "lastName", 100, 20, Customer.class);
然后可以使用页面
来获取结果的各个页面,如下所示
List<Customer> firstPage = customers.getContent();
方便的是,Spring Data Commons 页面
接口还实现了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 的存储库基础架构扩展,以便可以将 Lucene 查询表示为应用程序 存储库
接口上的方法,这与 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 ApplicationContext
通常,基于 Spring 的应用程序通过使用 Spring Data for Pivotal GemFire 的功能来 引导 Pivotal GemFire。通过指定使用 Spring Data for Pivotal GemFire XML 命名空间的 <gfe:cache/>
元素,将在与应用程序相同的 JVM 进程中创建单个嵌入式 Pivotal GemFire 对等 Cache
实例,并使用默认设置对其进行初始化。
但是,有时需要(可能作为 IT 组织施加的要求)由提供的 Pivotal GemFire 工具套件完全管理和操作 Pivotal GemFire,可能使用 Gfsh。通过使用 Gfsh,Pivotal GemFire 引导你的 Spring ApplicationContext
,而不是相反。Pivotal GemFire 而不是使用 Spring Boot 的应用程序服务器或 Java 主类进行引导并托管你的应用程序。
Pivotal GemFire 不是应用程序服务器。此外,在涉及 Pivotal GemFire 缓存配置时,使用此方法存在限制。 |
13.1. 使用 Pivotal GemFire 引导使用 Gfsh 启动的 Spring 上下文
为了在使用 Gfsh 启动 Pivotal GemFire 服务器时在 Pivotal GemFire 中引导 Spring ApplicationContext
,你必须使用 Pivotal GemFire 的 initalizer 功能。initializer 块可以声明一个应用程序回调,该回调在 Pivotal GemFire 初始化缓存后启动。
initializer 在 initializer 元素中声明,方法是使用 Pivotal GemFire 本机 cache.xml
的最小片段。为了引导 Spring ApplicationContext
,需要一个 cache.xml
文件,这与引导配置了组件扫描的 Spring ApplicationContext
(例如 <context:component-scan base-packages="…"/>
)所需的 Spring XML 配置的最小片段非常相似。
幸运的是,框架已经方便地提供了这样一个 initializer: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>
然后,在以命令行选项形式指定正确配置和构建的 CLASSPATH
和 cache.xml
文件(如前所述)以在 Gfsh 中启动 Pivotal GemFire 服务器时,命令行如下所示
gfsh>start server --name=ExampleServer --log-level=config ...
--classpath="/path/to/application/classes.jar:/path/to/spring-data-geode-<major>.<minor>.<maint>.RELEASE.jar"
--cache-xml-file="/path/to/geode/cache.xml"
application-context.xml
可以是任何有效的 Spring 配置元数据,包括所有 SDG XML 命名空间元素。此方法的唯一限制是无法使用 SDG XML 命名空间配置 Pivotal GemFire 缓存。换句话说,不能指定任何 <gfe:cache/>
元素属性(例如 cache-xml-location
、properties-ref
、critical-heap-percentage
、pdx-serializer-ref
、lock-lease
等)。如果使用,则会忽略这些属性。
原因是 Pivotal GemFire 本身已在调用初始化程序之前创建并初始化了缓存。因此,缓存已存在,并且由于它是“单例”,因此无法重新初始化或对其任何配置进行扩充。
13.2 延迟连接 Pivotal GemFire 组件
Spring Data for Pivotal GemFire 已提供对自动连接 Pivotal GemFire 组件(例如 CacheListeners
、CacheLoaders
、CacheWriters
等)的支持,这些组件由 Pivotal GemFire 在 cache.xml
中声明和创建,方法是使用 SDG 的 WiringDeclarableSupport
类,如 使用自动连接和注释进行配置 中所述。但是,这仅在 Spring 执行自举时有效(即当 Spring 自举 Pivotal GemFire 时)。
当 Pivotal GemFire 自举 Spring ApplicationContext
时,这些 Pivotal GemFire 应用程序组件不会被注意到,因为 Spring ApplicationContext
尚未存在。Spring ApplicationContext
直到 Pivotal GemFire 调用初始化程序块才创建,而该块仅在所有其他 Pivotal GemFire 组件(缓存、区域等)已创建并初始化后才发生。
为了解决这个问题,引入了一个新的 LazyWiringDeclarableSupport
类。此新类了解 Spring ApplicationContext
。此抽象基类的目的是,任何实现类都会注册自身,以便由 Pivotal GemFire 在调用初始化程序后最终创建的 Spring 容器进行配置。从本质上讲,这给了 Pivotal GemFire 应用程序组件一个机会,以便使用 Spring 容器中定义的 Spring bean 进行配置和自动装配。
为了让 Spring 容器自动装配 Pivotal GemFire 应用程序组件,您应该创建一个应用程序类,该类扩展 LazyWiringDeclarableSupport
并注释需要作为 Spring bean 依赖项提供的任何类成员,如下面的示例所示
public class UserDataSourceCacheLoader extends LazyWiringDeclarableSupport
implements CacheLoader<String, User> {
@Autowired
private DataSource userDataSource;
...
}
如上文中的 CacheLoader
示例所示,您可能必须(尽管很少)在 Pivotal GemFire cache.xml
中定义一个 Region 和一个 CacheListener
组件。CacheLoader
可能需要访问应用程序存储库(或 Spring ApplicationContext
中定义的 JDBC DataSource
)才能在启动时将 Users
加载到 Pivotal GemFire REPLICATE
Region 中。
小心
以这种方式混合 Pivotal GemFire 和 Spring 容器的不同生命周期时要小心。并非所有用例和场景都受支持。Pivotal GemFire cache.xml
配置类似于以下内容(来自 SDG 的测试套件)
<?xml version="1.0" encoding="UTF-8"?>
<cache xmlns="http://geode.apache.org/schema/cache"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://geode.apache.org/schema/cache https://geode.apache.org/schema/cache/cache-1.0.xsd"
version="1.0">
<region name="Users" refid="REPLICATE">
<region-attributes initial-capacity="101" load-factor="0.85">
<key-constraint>java.lang.String</key-constraint>
<value-constraint>org.springframework.data.gemfire.repository.sample.User</value-constraint>
<cache-loader>
<class-name>
org.springframework.data.gemfire.support.SpringContextBootstrappingInitializerIntegrationTest$UserDataStoreCacheLoader
</class-name>
</cache-loader>
</region-attributes>
</region>
<initializer>
<class-name>org.springframework.data.gemfire.support.SpringContextBootstrappingInitializer</class-name>
<parameter name="basePackages">
<string>org.springframework.data.gemfire.support.sample</string>
</parameter>
</initializer>
</cache>
14. 示例应用程序
示例应用程序现维护在 Spring Pivotal GemFire 示例 存储库中。 |
Spring Data for Pivotal GemFire 项目还包括一个示例应用程序。名为“Hello World”,此示例应用程序演示如何在 Spring 应用程序中配置和使用 Pivotal GemFire。在运行时,此示例提供了一个 shell,可让您针对数据网格运行各种命令。它为不熟悉基本组件或 Spring 和 Pivotal GemFire 概念的开发人员提供了一个极好的起点。
此示例与发行版捆绑在一起,并且基于 Maven。您可以将其导入任何支持 Maven 的 IDE(例如 Spring Tool Suite)或从命令行运行它们。
14.1. Hello World
“Hello World”示例应用程序演示了 Spring Data for Pivotal GemFire 项目的核心功能。它引导 Pivotal GemFire,对其进行配置,针对缓存执行任意命令,并在应用程序退出时将其关闭。可以同时启动应用程序的多个实例并协同工作,共享数据,而无需任何用户干预。
在 Linux 下运行
如果您在启动 Pivotal GemFire 或示例时遇到网络问题,请尝试将以下系统属性 java.net.preferIPv4Stack=true 添加到命令行(例如,-Djava.net.preferIPv4Stack=true )。有关替代(全局)修复方法(尤其是在 Ubuntu 上),请参阅 SGF-28。
|
14.1.1. 启动和停止示例
“Hello World” 示例应用程序被设计为一个独立的 Java 应用程序。它具有一个 main
类,可以通过 IDE(在 Eclipse 或 STS 中,通过 Run As/Java Application
)或通过 Maven 使用 mvn exec:java
从命令行启动。如果正确设置了类路径,您还可以直接对生成的工件使用 java
。
要停止示例,请在命令行中键入 exit
或按 Ctrl+C
停止 JVM 并关闭 Spring 容器。
14.1.2. 使用示例
启动后,示例将创建一个共享数据网格并允许您针对它发出命令。输出应类似于以下内容
INFO: Created {data-store-name} Cache [Spring {data-store-name} World] v. X.Y.Z
INFO: Created new cache region [myWorld]
INFO: Member xxxxxx:50694/51611 connecting to region [myWorld]
Hello World!
Want to interact with the world ? ...
Supported commands are:
get <key> - retrieves an entry (by key) from the grid
put <key> <value> - puts a new entry into the grid
remove <key> - removes an entry (by key) from the grid
...
例如,要将新项目添加到网格,可以使用以下命令
-> Bold Section qName:emphasis level:5, chunks:[put 1 unu] attrs:[role:bold]
INFO: Added [1=unu] to the cache
null
-> Bold Section qName:emphasis level:5, chunks:[put 1 one] attrs:[role:bold]
INFO: Updated [1] from [unu] to [one]
unu
-> Bold Section qName:emphasis level:5, chunks:[size] attrs:[role:bold]
1
-> Bold Section qName:emphasis level:5, chunks:[put 2 two] attrs:[role:bold]
INFO: Added [2=two] to the cache
null
-> Bold Section qName:emphasis level:5, chunks:[size] attrs:[role:bold]
2
可以同时运行多个实例。启动后,新 VM 会自动看到现有区域及其信息,如下例所示
INFO: Connected to Distributed System ['Spring {data-store-name} World'=xxxx:56218/49320@yyyyy]
Hello World!
...
-> Bold Section qName:emphasis level:5, chunks:[size] attrs:[role:bold]
2
-> Bold Section qName:emphasis level:5, chunks:[map] attrs:[role:bold]
[2=two] [1=one]
-> Bold Section qName:emphasis level:5, chunks:[query length = 3] attrs:[role:bold]
[one, two]
我们鼓励您尝试使用示例,启动(和停止)任意多个实例,并在一个实例中运行各种命令,并查看其他实例如何做出反应。为了保留数据,至少需要一个实例始终处于活动状态。如果所有实例都已关闭,则网格数据将被完全销毁。
14.1.3. Hello World 示例说明
“Hello World” 示例同时使用 Spring XML 和注释进行配置。初始引导配置是 app-context.xml
,其中包括在 cache-context.xml
文件中定义的缓存配置,并对 Spring 组件扫描执行类路径 组件。
缓存配置定义了 Pivotal GemFire 缓存、区域,以及出于说明目的,充当记录器的 CacheListener
。
主要 Bean 是 HelloWorld
和 CommandProcessor
,它们依赖于 GemfireTemplate
与分布式结构进行交互。这两个类都使用注释来定义其依赖关系和生命周期回调。
资源
附录
附录 A:命名空间参考
<repositories />
元素
<repositories />
元素触发 Spring 数据存储库基础设施的设置。最重要的属性是 base-package
,它定义要扫描 Spring 数据存储库接口的包。请参阅“[repositories.create-instances.spring]”。下表描述了 <repositories />
元素的属性
名称 | 说明 |
---|---|
|
在自动检测模式下,定义要扫描以扩展 |
|
定义自动检测自定义存储库实现的后缀。名称以配置的后缀结尾的类被视为候选。默认为 |
|
确定用于创建查找器查询的策略。有关详细信息,请参阅“[repositories.query-methods.query-lookup-strategies]”。默认为 |
|
定义搜索包含外部定义查询的属性文件的位置。 |
|
是否应考虑嵌套存储库接口定义。默认为 |
附录 B:填充器命名空间参考
<populator /> 元素
<populator />
元素允许通过 Spring 数据存储库基础设施填充数据存储。[1]
名称 | 说明 |
---|---|
|
应填充查找存储库中对象的文本文件的位置。 |
附录 C:存储库查询关键字
支持的查询关键字
下表列出了 Spring Data 存储库查询派生机制通常支持的关键字。但是,请查阅特定于存储的文档以了解支持的关键字的确切列表,因为此处列出的某些关键字可能不受特定存储支持。
逻辑关键字 | 关键字表达式 |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
附录 D:存储库查询返回类型
支持的查询返回类型
下表列出了 Spring Data 存储库通常支持的返回类型。但是,请查阅特定于存储的文档以了解支持的返回类型的确切列表,因为此处列出的某些类型可能不受特定存储支持。
地理空间类型(例如 GeoResult 、GeoResults 和 GeoPage )仅适用于支持地理空间查询的数据存储。
|
返回类型 | 说明 |
---|---|
|
表示无返回值。 |
基本类型 |
Java 基本类型。 |
包装类型 |
Java 包装类型。 |
|
一个唯一的实体。期望查询方法至多返回一个结果。如果找不到结果,则返回 |
|
一个 |
|
一个 |
|
一个 |
|
一个 Java 8 或 Guava |
|
一个 Scala 或 Vavr |
|
一个 Java 8 |
|
|
实现 |
公开一个构造函数或 |
Vavr |
Vavr 集合类型。有关详细信息,请参阅 [repositories.collections-and-iterables.vavr]。 |
|
一个 |
|
一个 Java 8 |
|
一个 |
|
一个大小合适的数据块,指示是否有更多数据可用。需要一个 |
|
一个带有附加信息(例如结果总数)的 |
|
一个带有附加信息(例如到参考位置的距离)的结果条目。 |
|
一个 |
|
一个带有 |
|
一个 Project Reactor |
|
一个 Project Reactor |
|
使用响应式存储库发出单个元素的 RxJava |
|
使用响应式存储库发出零个或一个元素的 RxJava |
|
使用响应式存储库发出零个、一个或多个元素的 RxJava |