原标题:Spring认证中国教育管理中心-Apache Geode 的 Spring 数据教程二十四(Spring中国教育管理中心)
Apache Geode 的 Spring 数据教程二十四
- 函数执行的注解支持
Spring Data for Apache Geode 包括注释支持以简化使用 Apache Geode 函数执行的工作。
在幕后,Apache Geode API 提供了实现和注册 Apache Geode 函数的类,这些函数部署在 Apache Geode 服务器上,然后可以由其他对等成员应用程序或从缓存客户端远程调用。
函数可以并行执行,分布在集群中的多个 Apache Geode 服务器中,使用 map-reduce 模式聚合结果并发回调用者。还可以将函数定位为在单个服务器或区域上运行。Apache Geode API 支持使用各种预定义范围远程执行目标函数:区域、成员(组)、服务器等。远程函数的实现和执行,与任何 RPC 协议一样,需要一些样板代码。
Spring Data for Apache Geode 忠实于 Spring 的核心价值主张,旨在隐藏远程函数执行的机制,让您专注于核心 POJO 编程和业务逻辑。为此,Apache Geode 的 Spring Data 引入了注解,以声明性地将 POJO 类的公共方法注册为 Apache Geode 函数,以及使用带注释的接口调用已注册函数(包括远程)的能力。
11.1.实施与执行
有两个单独的问题需要解决:实施和执行。
第一个是函数实现(服务器端),它必须与 交互 FunctionContext 以访问调用参数、 ResultsSender发送结果和其他执行上下文信息。Function 实现通常访问缓存和区域,并FunctionService使用唯一 ID注册 。
调用函数的缓存客户端应用程序不依赖于实现。为了调用一个函数,应用程序实例化一个 Execution 提供函数 ID、调用参数和函数目标,它定义了它的范围:区域、服务器、服务器、成员或成员。如果 Function 产生结果,调用者使用 a ResultCollector 来聚合并获取执行结果。在某些情况下,需要自定义ResultCollector实现,并且可以使用Execution.
'Client' 和 'Server' 在函数执行的上下文中使用,这可能与 Apache Geode 的客户端 - 服务器拓扑中的客户端和服务器具有不同的含义。虽然应用程序使用ClientCache实例调用集群中一个或多个 Apache Geode 服务器上的函数是很常见的,但也可以在对等 (P2P) 配置中执行函数,其中应用程序是成员托管对等Cache实例的集群。请记住,对等成员缓存应用程序受作为集群对等成员的所有约束的约束。
11.2.实现一个功能
使用 Apache Geode API,它FunctionContext提供了一个运行时调用上下文,其中包括客户端的调用参数和ResultSender将结果发送回客户端的实现。此外,如果 Function 在 Region 上执行,FunctionContext则实际上是 的一个实例RegionFunctionContext,它提供附加信息,例如调用 Function 的目标 Region Execution、与关联的任何过滤器(一组特定键),等等在。如果 Region 是PARTITIONRegion,则 Function 应该使用PartitionRegionHelper来提取本地数据集。
通过使用 Spring,您可以编写一个简单的 POJO 并使用 Spring 容器将一个或多个 POJO 的公共方法绑定到一个函数。打算用作函数的 POJO 方法的签名通常必须符合客户端的执行参数。但是在一个Region执行的情况下,也可能会提供Region数据(如果Region是PARTITIONRegion的话,大概数据是保存在本地分区的)。
此外,该函数可能需要应用的过滤器(如果有)。这表明客户端和服务器共享调用参数的合同,但方法签名可能包含附加参数以传递FunctionContext. 一种可能性是客户端和服务器共享一个公共接口,但这不是严格要求的。唯一的限制是方法签名包括与解析附加参数后调用函数相同的调用参数序列。
例如,假设客户端提供 aString和 anint作为调用参数。这些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 方法参数完全对应。该方法的返回类型必须是空的或可序列化类型(作为java.io.Serializable,DataSerializable或PdxSerializable)。后者也是调用参数的要求。
Region 数据通常应定义为Map, 以方便单元测试,但如有必要,也可以是 Region 类型。如前面的示例所示,如果您需要控制如何将结果返回给客户端,则传递FunctionContext本身或传递也是有效ResultSender的。
11.2.1.函数实现的注解
以下示例展示了如何使用 SDG 的函数注释将 POJO 方法公开为 Apache Geode 函数:
@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
@GemfireFunction(hasResult=true)
public void functionWithContext(FunctionContext functionContext) { ... }
}
请注意,类本身必须注册为 Spring bean,并且每个 Apache Geode 函数都使用 @GemfireFunction. 在前面的例子中,使用了 Spring 的@Component注解,但是您可以使用 Spring 支持的任何方法(例如 XML 配置或使用 Spring Boot 时使用 Java 配置类)来注册 bean。这让 Spring 容器可以创建此类的实例并将其包装在 PojoFunctionWrapper. Spring 为每个用 注释的方法创建一个包装器实例@GemfireFunction。每个包装器实例共享相同的目标对象实例以调用相应的方法。
POJO Function 类是 Spring bean 的事实可能提供其他好处。由于它ApplicationContext与 Apache Geode 组件(例如缓存和区域)共享,因此可以在必要时将它们注入到类中。
Spring 创建包装类并将函数注册到 Apache Geode 的FunctionService. 用于注册每个函数的函数 ID 必须是唯一的。通过使用约定,它默认为简单(非限定)方法名称。可以使用注释的id属性显式定义名称@GemfireFunction。
该@GemfireFunction注解还提供了其他配置属性:HAand optimizedForWrite,它们对应于 Apache GeodeFunction接口定义的属性 。
如果 POJO Function 方法的返回类型为void,则该hasResult属性会自动设置为false。否则,如果该方法返回一个值,则hasResult属性设置为true。即使对于void方法返回类型,也可以将GemfireFunction注解的hasResult属性设置true为覆盖此约定,如functionWithContext前面显示的方法所示。据推测,目的是您将ResultSender直接使用将结果发送给调用者。
最后,GemfireFunction注解支持requiredPermissions属性,该属性指定执行函数所需的权限。默认情况下,所有功能都需要DATA:WRITE权限。该属性接受一个字符串数组,允许您根据应用程序和/或功能 UC 的要求修改权限。每个资源权限应采用以下格式:
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] 枚举值中的 1 个。最后,如果指定,Key则可选地是区域中的有效密钥Target。
所述PojoFunctionWrapper器具的Apache的Geode的Function界面,结合方法的参数,并调用在其目标方法execute()的方法。它还通过使用 将方法的返回值发送回调用者ResultSender。
11.2.2.批处理结果
如果返回类型是数组或Collection,则必须考虑如何返回结果。默认情况下,PojoFunctionWrapper返回整个数组或Collection一次。如果数组中的元素数量 或Collection非常大,则可能会导致性能损失。要将有效负载划分为更小、更易于管理的块,您可以设置batchSize属性,如function2前面所示的 中所示。
如果您需要更多地控制ResultSender,特别是如果方法本身会使用太多内存来创建Collection,您可以传入ResultSender或通过 访问它FunctionContext 并直接在方法中使用它以将结果发送回调用者。
11.2.3.启用注释处理
根据 Spring 标准,您必须显式激活注解的注解处理@GemfireFunction 。以下示例使用 XML 激活注释处理:
以下示例通过注释 Java 配置类来激活注释处理:
@Configuration
@EnableGemfireFunctions
class ApplicationConfiguration { ... }
11.3.执行函数
调用远程功能需求的方法,以提供对功能的ID,调用自变量,执行对象(onRegion,onServers,onServer,onMember,或onMembers)和(任选地)一个滤波器集合。通过使用 Spring Data for Apache Geode,您只需定义一个注解支持的接口。Spring 为接口创建一个动态代理,它使用FunctionService来创建Execution,调用Execution,并且(如果需要)将结果强制为定义的返回类型。这种技术类似于 Spring Data for Apache Geode 的 Repository 扩展的工作方式。因此,一些配置和概念应该是熟悉的。
通常,单个接口定义映射到多个 Function 执行,一个对应于接口中定义的每个方法。
11.3.1.函数执行注解
为了支持客户端功能执行,提供了下面的功能SDG注释:@OnRegion, @OnServer,@OnServers,@OnMember,和@OnMembers。这些注释对应于Execution Apache GeodeFunctionService类提供的实现 。
每个注释都公开了适当的属性。这些注释还提供了一个可选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
}
默认情况下,函数 ID 是简单(非限定)方法名称。该@FunctionId注释可被用于此调用绑定到一个不同的功能ID。
11.3.2.启用注释处理
客户端使用 Spring 的类路径组件扫描功能来发现带注释的接口。要在 XML 中启用函数执行注释处理,请在 XML 配置中插入以下元素:
该function-executions元素在gfe-dataXML 命名空间中提供。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创建了一个onRegionFunction Execution:
示例 19. 使用
GemfireOnRegionFunctionTemplate
Set, ?> myFilter = getFilter();
Region, ?> myRegion = getRegion();
GemfireOnRegionOperations template = new GemfireOnRegionFunctionTemplate(myRegion);
String result = template.executeAndExtract("someFunction", myFilter, "hello", "world", 1234);
在内部,FunctionExecutions总是返回一个List. executeAndExtract假定List 包含结果的单例并尝试将该值强制转换为请求的类型。还有一种execute方法可以按List原样返回。第一个参数是函数 ID。过滤器参数是可选的。其余参数是可变参数List。
11.5.使用 PDX 执行函数
将 Spring Data for Apache Geode 的 Function annotation 支持与 Apache Geode 的PDX Serialization结合使用时 ,需要记住一些逻辑上的事情。
正如本节前面所解释的,作为示例,您通常应该使用使用 Spring Data 注释的 POJO 类来定义 Apache Geode Functions,用于 Apache Geode Function annotations,如下所示:
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
...
}
public enum OrderSource {
ONLINE,
PHONE,
POINT_OF_SALE
...
}
当然,你可以定义一个FunctionExecution接口来调用'process'Apache Geode服务器Function,如下:
@OnServer
public interface OrderProcessingFunctions {
Order process(Order order, OrderSource orderSourceEnum, Integer count);
}
显然,这个process(..) Order函数是从客户端用一个ClientCache实例(即
现在,如果您已将 Apache Geode 配置为使用 PDX 进行序列化(例如,而不是 Java 序列化),您还pdx-read-serialized可以true在 Apache Geode 服务器的配置中将该属性设置为,如下所示:
或者,您可以将pdx-read-serialized属性设置true为 Apache Geode 缓存客户端应用程序,如下所示:
这样做会导致从缓存(即区域)读取的所有值以及在客户端和服务器(或对等方)之间传递的信息保持序列化形式,包括但不限于函数参数。
Apache Geode 仅序列化您通过使用 Apache Geode'
sReflectionBasedAutoSerializer或通过使用“自定义”Apache Geode 专门(和推荐) 专门配置(注册)的应用程序域对象类型 PdxSerializer。如果您使用 Spring Data for Apache Geode 的 Repository 扩展,您甚至可能需要考虑使用 Spring Data for Apache Geode's MappingPdxSerializer,它使用实体的映射元数据来确定序列化到 PDX 实例的应用程序域对象中的数据。
然而,不太明显的是 Apache Geode 会自动处理 JavaEnum类型,而不管它们是否被显式配置(即
ReflectionBasedAutoSerializer使用 regex 模式和classes参数注册,或者由“自定义”Apache Geode 处理PdxSerializer),尽管 Java 枚举实现了java.io.Serializable.
因此,当您在注册了 Apache Geode Functions(包括 Spring Data for Apache Geode Function-annotated POJO classes)的 Apache Geode 服务器上设置pdx-read-serialized为时,您true在调用 Function 时可能会遇到令人惊讶的行为Execution。
您可以在调用函数时传递以下参数:
orderProcessingFunctions.process(new Order(123, customer, LocalDateTime.now(), items), OrderSource.ONLINE, 400);
但是,服务器上的 Apache Geode Function 获得以下信息:
process(regionData, order:PdxInstance, :PdxInstanceEnum, 400);
在Order与OrderSource已传递给函数的 PDX实例。同样,这一切都是因为pdx-read-serialized设置为true,这在 Apache Geode 服务器与多个不同客户端交互的情况下可能是必要的(例如,Java 客户端和本机客户端的组合,如 C/C++、C# 等) .
这与 Spring Data for Apache Geode 的强类型函数注释 POJO 类方法签名背道而驰,您可以合理地期望应用程序域对象类型,而不是 PDX 序列化实例。
因此,Apache Geode 的 Spring Data 包括增强的 Function 支持,以自动将 PDX 类型的方法参数转换为由 Function 方法的签名(参数类型)定义的所需应用程序域对象类型。
但是,这也要求您PdxSerializer在注册和使用 Spring Data for Apache Geode Function-annotated POJOs 的 Apache Geode 服务器上显式注册 Apache Geode ,如以下示例所示:
或者,
ReflectionBasedAutoSerializer 为了方便起见,您可以使用 Apache Geode 。当然,我们建议您在可能的情况下使用自定义PdxSerializer来保持对序列化策略的细粒度控制。
最后,如果您将 Function 参数视为一般性或 Apache Geode 的 PDX 类型之一,则 Spring Data for Apache Geode 会注意不要转换您的 Function 参数,如下所示:
@GemfireFunction
public Object genericFunction(String value, Object domainObject, PdxInstanceEnum pdxEnum) {
// ...
}
Spring Data for Apache Geode 将 PDX 类型的数据转换为相应的应用程序域类型,当且仅当相应的应用程序域类型在类路径上并且 Function-annotated POJO 方法需要它时。
有关自定义的、组合的特定于应用程序的 Apache GeodePdxSerializers以及基于方法签名的适当 POJO 函数参数类型处理的一个很好的示例。