我已经创建了一个带有Geode/GemFire缓存的Spring Boot应用程序。我想使用我的Spring Boot应用程序连接到Gfsh创建的区域。在我的applation-context. xml
中,我使用了带有Gfsh创建区域ID的gfe:lookup-region
。
在我的Java配置文件中,我使用LookupRegion onFactoryBean
来获取对外部定义的Region的引用。
在我的SpringBootApplication
bootstrap类中,我成功地写入了我的存储库,因为我可以读回我保存的所有对象。但是使用Gfsh工具或Pulse工具,我看不到我缓存的数据记录(或者我保存的数据记录数)。
你能在这里提供一些见解吗?此外,我也尝试在我的配置文件中使用LocalRegion onFactoryBean
,但这种方法也不起作用。
谢谢。
application-context.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:gfe-data="http://www.springframework.org/schema/data/gemfire"
xmlns:gfe="http://www.springframework.org/schema/gemfire"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/data/gemfire http://www.springframework.org/schema/data/gemfire/spring-data-gemfire.xsd http://www.springframework.org/schema/gemfire http://www.springframework.org/schema/gemfire/spring-gemfire.xsd">
<context:component-scan base-package="com.example.geode"></context:component-scan>
<util:properties id="gemfireProperties" location="context:geode.properties"/>
<!-- <context:property-placeholder location="context:geode.properties"/>
<bean id="log-level"><property name="log-level" value="${log-level}"/></bean>
<bean id="mcast-port"><property name="mcast-port" value="${mcast-port}"/></bean>
<bean id="name"><property name="name" value="${name}"/></bean>-->
<gfe:annotation-driven/>
<gfe-data:function-executions base-package="com.example.geode.config"/>
<!-- Declare GemFire Cache -->
<!-- <gfe:cache/> -->
<gfe:cache properties-ref="gemfireProperties"/>
<!-- Local region for being used by the Message -->
<!-- <gfe:replicated-region id="employee" value-constraint="com.example.geode.model.Employee" data-policy="REPLICATE"/>-->
<gfe:lookup-region id="employee" value-constraint="com.example.geode.model.Employee" data-policy="REPLICATE"/>
<!-- <gfe:local-region id="employee" value-constraint="com.example.geode.model.Employee" data-policy="REPLICATE"/>-->
<!-- Search for GemFire repositories -->
<gfe-data:repositories base-package="com.example.geode.repository"/>
</beans>
GeodeConfiguration.java:
//imports not included
@Configuration
@ComponentScan
@EnableCaching
@EnableGemfireRepositories//(basePackages = "com.example.geode.repository")
@EnableGemfireFunctions
@EnableGemfireFunctionExecutions//(basePackages = "com.example.geode.function")
@PropertySource("classpath:geode.properties")
public class GeodeConfiguration {
@Autowired
private EmployeeRepository employeeRepository;
@Autowired
private FunctionExecution functionExecution;
@Value("${log-level}")
private String loglevel;
@Value("${mcast-port}")
private String mcastPort;
@Value("${name}")
private String name;
Properties gemfireProperties() {
Properties gemfireProperties = new Properties();
gemfireProperties.setProperty(loglevel, loglevel);
gemfireProperties.setProperty(mcastPort, mcastPort);
gemfireProperties.setProperty(name, name);
return gemfireProperties;
}
@Bean
CacheFactoryBean gemfireCache() {
return new CacheFactoryBean();
}
@Bean
GemfireCacheManager cacheManager() {
GemfireCacheManager cacheManager = new GemfireCacheManager();
try {
CacheFactoryBean cacheFactory = gemfireCache();
//gemfireProperties();
//cacheFactory.setProperties(gemfireProperties());
cacheManager.setCache(cacheFactory.getObject()); //gemfireCache().getObject());
} catch (Exception ex) {
ex.printStackTrace();
}
return cacheManager;
}
@Bean(name="employee")
//@Autowired
LookupRegionFactoryBean<String, Employee> getRegion(final GemFireCache cache)
throws Exception {
//CacheTypeAwareRegionFactoryBean<String, Employee> region = new CacheTypeAwareRegionFactoryBean<>();//GenericRegionFactoryBean<> //LocalRegionFactoryBean<>();
LookupRegionFactoryBean<String, Employee> region = new LookupRegionFactoryBean<>();//GenericRegionFactoryBean<> //LocalRegionFactoryBean<>();
region.setRegionName("employee");
try {
region.setCache(gemfireCache().getObject());
} catch (Exception e) {
e.printStackTrace();
}
//region.setClose(false);
region.setName("employee");
//region.setAsyncEventQueues(new AsyncEventQueue[]{gemfireQueue});
//region.setPersistent(false);
//region.setDataPolicy(org.apache.geode.cache.DataPolicy.REPLICATE); //PRELOADED); //REPLICATE);
region.afterPropertiesSet();
return region;
}
}
BasicGeodeApplication.java:
//imports not provided
@EnableGemfireRepositories
@SpringBootApplication
@ComponentScan("com.example.geode")
//@EnableCaching
@EnableGemfireCaching
@EnableEntityDefinedRegions(basePackageClasses = Employee.class)
@SuppressWarnings("unused")
//@CacheServerApplication(name = "server2", locators = "localhost[10334]",
// autoStartup = true, port = 41414)
public class BasicGeodeApplication {
@Autowired
private EmployeeRepository employeeRepository;
@Autowired
private EmployeeService employeeService;
private static ConfigurableApplicationContext context;
public static void main(String[] args) {
context = SpringApplication.run(BasicGeodeApplication.class, args);
BasicGeodeApplication bga = new BasicGeodeApplication();
}
@Bean
public ApplicationRunner run(EmployeeRepository employeeRepository) {
return args -> {
Employee bob = new Employee("Bob", 80.0);
Employee sue = new Employee("Susan", 95.0);
Employee jane = new Employee("Jane", 85.0);
Employee jack = new Employee("Jack", 90.0);
List<Employee> employees = Arrays.asList(bob, sue, jane, jack);
employees.sort(Comparator.comparing(Employee::getName));
for (Employee employee : employees) {
//employeeService.saveEmployee(employee);
employeeRepository.save(employee);
}
System.out.println("\nList of employees:");
employees //Arrays.asList(bob.getName(), sue.getName(), jane.getName(), jack.getName());
.forEach(person -> System.out.println("\t" + employeeRepository.findByName(person.getName())));
System.out.println("\nQuery salary greater than 80k:");
stream(employeeRepository.findBySalaryGreaterThan(80.0).spliterator(), false)
.forEach(person -> System.out.println("\t" + person));
System.out.println("\nQuery salary less than 95k:");
stream(employeeRepository.findBySalaryLessThan(95.0).spliterator(), false)
.forEach(person -> System.out.println("\t" + person));
System.out.println("\nQuery salary greater than 80k and less than 95k:");
stream(employeeRepository.findBySalaryGreaterThanAndSalaryLessThan(80.0, 95.0).spliterator(), false)
.forEach(person -> System.out.println("\t" + person));
};
}
@Service
class EmployeeService {
@Autowired
private EmployeeRepository employeeRepository;
@CachePut(cacheNames = "employee", key = "#id")
//@PutMapping("/")
void saveEmployee(Employee employee) {
employeeRepository.save(employee);
}
Employee findEmployee(String name) {
return null;
}
//employeeRepository.findByName(person.getName())));
}
}
EmployeeRepository.java:
@Repository("employeeRepository")
//@DependsOn("gemfireCache")
public interface EmployeeRepository extends CrudRepository<Employee, String> {
Employee findByName(String name);
Iterable<Employee> findBySalaryGreaterThan(double salary);
Iterable<Employee> findBySalaryLessThan(double salary);
Iterable<Employee> findBySalaryGreaterThanAndSalaryLessThan(double salary1, double salary2);
}
Employee.java:
// imports not included
@Entity
@Region("employee")
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
@javax.persistence.Id
private Long id;
public String name;
public double salary;
protected Employee() {}
@PersistenceConstructor
public Employee(String name, double salary) {
this.name = name;
this.salary = salary;
}
@Override
public String toString() {
return name + " salary is: " + salary;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
}
如果您的Spring Boot应用程序在尝试查找后实际上使用了在Gfsh中创建的区域,我会非常惊讶。这也从运行应用程序后使用Gfsh或Pulse时看不到区域中的数据这一事实中可以明显看出。
此外,从您的配置中不完全清楚您的Spring Boot应用程序是否加入了以Gfsh启动的集群,因为您没有共享geode.properties
文件的内容。
注意:我们稍后会回到您的属性文件,因为您如何引用它(即在的
位置
属性中使用context:
您使用的是哪个版本的Spring Boot和Spring Data Geode/GemFire(SDG)?
通过XML配置文件中的SpringXML模式引用来判断,您使用的是Spring 3.0!?您不应该在模式位置声明中引用版本。将根据您在应用程序类路径上导入的Spring JAR对不合格的模式版本进行版本控制(例如使用Maven)。
无论如何,我问的原因是,我对SDG进行了许多更改,导致它在配置不明确或不完整(例如)的情况下快速失败。
在你的例子中,如果区域不存在,查找会立即失败。而且,我很确定区域不存在,因为SDG默认情况下会禁用对等缓存节点/应用程序上的集群配置。因此,您在Gfsh中创建的任何区域都不能立即用于您的Spring Boot应用程序。
所以,让我们来看看一个简单的例子。为了便于使用和方便,我将主要使用SDG的新注释配置模型和一些Java配置。我鼓励你阅读SDG参考指南中的这一章,因为你的配置到处都是,我很确定你混淆了实际发生的事情。
这是我的Spring Boot、Apache Geode应用程序…
@SpringBootApplication
@SuppressWarnings("unused")
public class ClusterConfiguredGeodeServerApplication {
public static void main(String[] args) {
SpringApplication.run(ClusterConfiguredGeodeServerApplication.class, args);
}
@Bean
ApplicationRunner runner(GemfireTemplate customersTemplate) {
return args -> {
Customer jonDoe = Customer.named("Jon Doe").identifiedBy(1L);
customersTemplate.put(jonDoe.getId(), jonDoe);
};
}
@PeerCacheApplication(name = "ClusterConfiguredGeodeServerApplication")
@EnablePdx
static class GeodeConfiguration {
@Bean("Customers")
LookupRegionFactoryBean<Long, Customer> customersRegion(GemFireCache gemfireCache) {
LookupRegionFactoryBean<Long, Customer> customersRegion = new LookupRegionFactoryBean<>();
customersRegion.setCache(gemfireCache);
return customersRegion;
}
@Bean("CustomersTemplate")
GemfireTemplate customersTemplate(@Qualifier("Customers") Region<?, ?> customers) {
return new GemfireTemplate(customers);
}
}
@Data
@RequiredArgsConstructor(staticName = "named")
static class Customer {
@Id
private Long id;
@NonNull
private String name;
Customer identifiedBy(Long id) {
this.id = id;
return this;
}
}
}
我正在使用Spring DataLovelace RC1
(其中包括Apache Geode的Spring Data2.1.0. RC1
)。我还在使用Spring Boot2.0.3.RELEASE
,它拉取了核心Spring Framework5.0.7。RELEASE
,全部在Java8上。
我省略了包装和进口声明。
我正在使用Project Lombok来定义我的客户
类。
我有一个嵌套的GeodeConfiguration
类,用于将Spring Boot应用程序配置为能够加入Apache Geode集群的“对等”Cache
成员。但是,它还不是任何集群的一部分!
最后,我配置了一个“客户”区域,它在Spring上下文中“查找”。
当我启动此应用程序时,它会失败,因为无论如何还没有定义“客户”区域……
Caused by: org.springframework.beans.factory.BeanInitializationException: Region [Customers] in Cache [GemFireCache[id = 2126876651; isClosing = false; isShutDownAll = false; created = Thu Aug 02 13:43:07 PDT 2018; server = false; copyOnRead = false; lockLease = 120; lockTimeout = 60]] not found
at org.springframework.data.gemfire.ResolvableRegionFactoryBean.createRegion(ResolvableRegionFactoryBean.java:146) ~[spring-data-geode-2.1.0.RC1.jar:2.1.0.RC1]
at org.springframework.data.gemfire.ResolvableRegionFactoryBean.afterPropertiesSet(ResolvableRegionFactoryBean.java:96) ~[spring-data-geode-2.1.0.RC1.jar:2.1.0.RC1]
at org.springframework.data.gemfire.LookupRegionFactoryBean.afterPropertiesSet(LookupRegionFactoryBean.java:72) ~[spring-data-geode-2.1.0.RC1.jar:2.1.0.RC1]
...
这是意料之中的!
好的,让我们进入Gfsh并启动一个集群。
你知道你需要用服务器启动定位器来形成集群,对吧?定位器被其他试图加入集群的潜在对等方使用,这样他们就可以首先找到集群。创建“客户”区域需要服务器,因为你不能在定位器上创建区域。
$ echo $GEODE_HOME
/Users/jblum/pivdev/apache-geode-1.6.0
$ gfsh
_________________________ __
/ _____/ ______/ ______/ /____/ /
/ / __/ /___ /_____ / _____ /
/ /__/ / ____/ _____/ / / / /
/______/_/ /______/_/ /_/ 1.6.0
Monitor and Manage Apache Geode
gfsh>start locator --name=LocaorOne --log-level=config
Starting a Geode Locator in /Users/jblum/pivdev/lab/LocaorOne...
....
Locator in /Users/jblum/pivdev/lab/LocaorOne on 10.0.0.121[10334] as LocaorOne is currently online.
Process ID: 41758
Uptime: 5 seconds
Geode Version: 1.6.0
Java Version: 1.8.0_152
Log File: /Users/jblum/pivdev/lab/LocaorOne/LocaorOne.log
JVM Arguments: -Dgemfire.enable-cluster-configuration=true -Dgemfire.load-cluster-configuration-from-dir=false -Dgemfire.log-level=config -Dgemfire.launcher.registerSignalHandlers=true -Djava.awt.headless=true -Dsun.rmi.dgc.server.gcInterval=9223372036854775806
Class-Path: /Users/jblum/pivdev/apache-geode-1.6.0/lib/geode-core-1.6.0.jar:/Users/jblum/pivdev/apache-geode-1.6.0/lib/geode-dependencies.jar
Successfully connected to: JMX Manager [host=10.0.0.121, port=1099]
Cluster configuration service is up and running.
gfsh>start server --name=ServerOne --log-level=config
Starting a Geode Server in /Users/jblum/pivdev/lab/ServerOne...
...
Server in /Users/jblum/pivdev/lab/ServerOne on 10.0.0.121[40404] as ServerOne is currently online.
Process ID: 41785
Uptime: 3 seconds
Geode Version: 1.6.0
Java Version: 1.8.0_152
Log File: /Users/jblum/pivdev/lab/ServerOne/ServerOne.log
JVM Arguments: -Dgemfire.default.locators=10.0.0.121[10334] -Dgemfire.start-dev-rest-api=false -Dgemfire.use-cluster-configuration=true -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/jblum/pivdev/apache-geode-1.6.0/lib/geode-core-1.6.0.jar:/Users/jblum/pivdev/apache-geode-1.6.0/lib/geode-dependencies.jar
gfsh>list members
Name | Id
--------- | --------------------------------------------------------------
LocaorOne | 10.0.0.121(LocaorOne:41758:locator)<ec><v0>:1024 [Coordinator]
ServerOne | 10.0.0.121(ServerOne:41785)<v1>:1025
gfsh>list regions
No Regions Found
gfsh>create region --name=Customers --type=PARTITION --key-constraint=java.lang.Long --value-constraint=java.lang.Object
Member | Status
--------- | ------------------------------------------
ServerOne | Region "/Customers" created on "ServerOne"
gfsh>list regions
List of regions
---------------
Customers
gfsh>describe region --name=Customers
..........................................................
Name : Customers
Data Policy : partition
Hosting Members : ServerOne
Non-Default Attributes Shared By Hosting Members
Type | Name | Value
------ | ----------- | ---------
Region | size | 0
| data-policy | PARTITION
现在,即使我再次运行Spring Boot应用程序,它仍然会以相同的异常失败……
Caused by: org.springframework.beans.factory.BeanInitializationException: Region [Customers] in Cache [GemFireCache[id = 989520513; isClosing = false; isShutDownAll = false; created = Thu Aug 02 14:09:25 PDT 2018; server = false; copyOnRead = false; lockLease = 120; lockTimeout = 60]] not found
at org.springframework.data.gemfire.ResolvableRegionFactoryBean.createRegion(ResolvableRegionFactoryBean.java:146) ~[spring-data-geode-2.1.0.RC1.jar:2.1.0.RC1]
at org.springframework.data.gemfire.ResolvableRegionFactoryBean.afterPropertiesSet(ResolvableRegionFactoryBean.java:96) ~[spring-data-geode-2.1.0.RC1.jar:2.1.0.RC1]
at org.springframework.data.gemfire.LookupRegionFactoryBean.afterPropertiesSet(LookupRegionFactoryBean.java:72) ~[spring-data-geode-2.1.0.RC1.jar:2.1.0.RC1]
...
为什么?
这是因为1)Spring Boot,Apache Geode对等Cache
应用程序(尚未)不是集群的一部分,2)因为默认情况下,SDG不允许Spring配置/引导的Apache Geode对等Cache
应用程序从集群(特别是从集群配置服务)获取它的配置,因此,我们必须配置/启用这两件事。
我们可以让我们的Spring Boot,Apache Geode对等Cache
应用程序通过将@PeerCacheApplication
注释的定位器
属性指定为localhost[10334]
来加入集群。
@PeerCacheApplication(name="…", locators="localhost[10334]")
我们可以让我们的Spring Boot,Apache Geode对等Cache
应用程序通过启用useClusterConfiguration
属性从集群中获取其配置,我们通过将以下Configrer
bean定义添加到我们内部的静态GeodeConfiguration
类来做到这一点,如下所示:
@Bean
PeerCacheConfigurer useClusterConfigurationConfigurer() {
return (beanName, cacheFactoryBean) -> cacheFactoryBean.setUseClusterConfiguration(true);
}
现在,当我们再次运行Spring Boot、Apache Geode对等Cache
应用程序时,我们会看到完全不同的输出。首先,查看我们的对等成员(应用程序)获取集群配置……
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<cache xmlns="http://geode.apache.org/schema/cache" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" copy-on-read="false" is-server="false" lock-lease="120" lock-timeout="60" search-timeout="300" version="1.0" xsi:schemaLocation="http://geode.apache.org/schema/cache http://geode.apache.org/schema/cache/cache-1.0.xsd">
<region name="Customers">
<region-attributes data-policy="partition">
<key-constraint>java.lang.Long</key-constraint>
<value-constraint>java.lang.Object</value-constraint>
</region-attributes>
</region>
</cache>
接下来,您可能已经注意到我启用了PDX,使用SDG的@EnablePdx
注释。这使我们能够轻松地序列化我们的应用程序域模型对象类型(例如客户
),而无需我们的类型过度需要实现java.io。Serializable
。有几个原因可以解释为什么您不一定要实现java.io。无论如何都是Serializable
。使用SDG的@EnablePdx
使用SDG的MappingPdxSerializer
实现,它甚至比Apache Geode的/Pivotal GemFire自己的RefltionBasedAutoSerializer
强大得多。
作为序列化应用程序类型(即Client
)的结果,您将看到此输出…
14:26:48.322 [main] INFO org.apache.geode.internal.cache.PartitionedRegion - Partitioned Region /Customers is created with prId=2
Started ClusterConfiguredGeodeServerApplication in 4.223 seconds (JVM running for 5.574)
14:26:48.966 [main] INFO org.apache.geode.pdx.internal.PeerTypeRegistration - Adding new type: PdxType[dsid=0, typenum=14762571
name=example.app.spring.cluster_config.server.ClusterConfiguredGeodeServerApplication$Customer
fields=[
id:Object:identity:0:idx0(relativeOffset)=0:idx1(vlfOffsetIndex)=-1
name:String:1:1:idx0(relativeOffset)=0:idx1(vlfOffsetIndex)=1]]
14:26:49.002 [main] INFO org.apache.geode.pdx.internal.TypeRegistry - Caching PdxType[dsid=0, typenum=14762571
name=example.app.spring.cluster_config.server.ClusterConfiguredGeodeServerApplication$Customer
fields=[
id:Object:identity:0:idx0(relativeOffset)=0:idx1(vlfOffsetIndex)=-1
name:String:1:1:idx0(relativeOffset)=0:idx1(vlfOffsetIndex)=1]]
我启用PDX的另一个原因是,我不需要将客户
类添加到使用Gfsh启动的服务器(即“ServerOne
”)。它还允许我查询“客户”区域并查看“Jon Doe”已成功添加…
gfsh>describe region --name=Customers
..........................................................
Name : Customers
Data Policy : partition
Hosting Members : ServerOne
Non-Default Attributes Shared By Hosting Members
Type | Name | Value
------ | ----------- | ---------
Region | size | 1
| data-policy | PARTITION
gfsh>
gfsh>query --query="SELECT c.name FROM /Customers c"
Result : true
Limit : 100
Rows : 1
Result
-------
Jon Doe
答对了!成功了!
我甚至不会开始讨论您的配置中的所有问题。我恳求您阅读文档(和Apache Geode的用户指南;即适当的部分),理解概念,查看示例,指南,提出简洁的问题,等等。
这是示例源代码…
https://github.com/jxblum/contacts-application/blob/master/configuration-example/src/main/java/example/app/spring/cluster_config/server/ClusterConfiguredGeodeServerApplication.java
希望这有帮助!
-j