我们有一个具有2个定位器和2个缓存节点的GemFire集群。
我们的Spring Boot服务将作为客户端连接到GemFire集群,并将具有客户端区域。我们正在使用Spring Data GemFire使用GemFireXML配置和属性来引导客户端区域。
当GemFire群集关闭时,Spring Boot服务不会启动,因为它无法满足GemFire区域依赖项(未满足DependecyException
)。
有没有一种方法可以松散地将Spring Boot启动和GemFire结合起来?
本质上,我们希望Spring Boot服务即使在GemFire集群关闭时也能启动。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:gfe="http://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 http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/gemfire http://www.springframework.org/schema/gemfire/spring-gemfire.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<util:properties id="gemfireProperties" location="classpath:gemfire.properties"/>
<bean id="autoSerializer" class="org.apache.geode.pdx.ReflectionBasedAutoSerializer">
</bean>
<gfe:client-cache pdx-serializer-ref="autoSerializer" pdx-read-serialized="true" pool-name="POOL" properties-ref="gemfireProperties"/>
<gfe:pool id="POOL" subscription-enabled="true" >
<gfe:locator host="${gf.cache.locator1}" port="${gf.cache.locator1.port}"/>
<gfe:locator host="${gf.cache.locator2}" port="${gf.cache.locator2.port}"/>
</gfe:pool>
<gfe:client-region id="xyz" shortcut="CACHING_PROXY" pool-name="POOL">
<gfe:regex-interest pattern=".*" result-policy="KEYS_VALUES"/>
</gfe:client-region>
</beans>
@ImportResource({"classpath:gemfire-config.xml"})
您所要求的是可能的,但没有一些自定义代码是不行的。
而且,使用基于Java的Spring Container Configuration以及SDG的API比使用Spring(Data GemFire)XML配置或(我没看错吧??你(可能)使用…)GemFireXML配置要容易得多。
但是,首先,我想知道您以何种身份使用Pivotal GemFire,您的Spring Boot应用程序(或服务)不严格要求GemFire运行(服务器端)才能正常运行,因此您的Spring Boot应用程序/服务仍将启动并满足您客户的需求?
显然,在这种情况下,Pivotal GemFire没有被用作您的Spring Boot服务的记录系统(SOR)。但是,如果您只是使用Pivotal GemFire进行“缓存”,也许是作为Spring的缓存抽象中的缓存提供者(或此),这是有意义的吗?这就是你正在做的吗?
总之…
我认为演示这一点的最佳方式是通过集成测试的示例,;-)
我写了一个简单的集成测试,ResilientClientServerIntegrationTest
,其中测试作为应用程序运行(put
/get
data to/from aRegion
,即示例),并演示了它可以“有条件地”在客户端/服务器和仅本地模式之间切换。
测试(或基于Spring的应用程序)在客户端/服务器和仅本地模式之间切换的关键是实现自定义Spring条件,然后在应用程序(客户端)配置类上使用@Con溜须拍马
Spring注释,如下所示。
但是,当服务器集群不可用时,我没有完全禁用GemFire客户端,而是简单地将应用程序(又名测试)切换为仅在客户端本地模式下运行。
我专门通过配置客户端区域来做到这一点,以使用ClientRegion onSHOtcut. LOCAL
设置。然后,我在客户端GemFire对象的配置中使用此设置,例如在“示例”客户端区域上,请参见此处,然后是此处。
现在,如果我运行这个测试,无论我是否有一个(服务器的)GemFire集群正在运行,它都会通过,因为如果没有可用的GemFire集群,那么它将只是在仅本地模式下运行。
如果应用程序可以使用GemFire集群,那么它也将按预期工作并在不更改任何客户端应用程序代码或配置的情况下使用集群,不错吧!
因此,举例来说,假设我使用Gfsh启动一个集群,如下所示…
$ echo $GEMFIRE
/Users/jblum/pivdev/apache-geode-1.6.0
$ gfsh
_________________________ __
/ _____/ ______/ ______/ /____/ /
/ / __/ /___ /_____ / _____ /
/ /__/ / ____/ _____/ / / / /
/______/_/ /______/_/ /_/ 1.6.0
Monitor and Manage Apache Geode
gfsh>
gfsh>start locator --name=LocatorOne --log-level=config
Starting a Geode Locator in /Users/jblum/pivdev/lab/LocatorOne...
.....
Locator in /Users/jblum/pivdev/lab/LocatorOne on 10.99.199.24[10334] as LocatorOne is currently online.
Process ID: 9737
Uptime: 3 seconds
Geode Version: 1.6.0
Java Version: 1.8.0_192
Log File: /Users/jblum/pivdev/lab/LocatorOne/LocatorOne.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.99.199.24, 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.99.199.24[40404] as ServerOne is currently online.
Process ID: 9780
Uptime: 3 seconds
Geode Version: 1.6.0
Java Version: 1.8.0_192
Log File: /Users/jblum/pivdev/lab/ServerOne/ServerOne.log
JVM Arguments: -Dgemfire.default.locators=10.99.199.24[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
---------- | ----------------------------------------------------------------
LocatorOne | 10.99.199.24(LocatorOne:9737:locator)<ec><v0>:1024 [Coordinator]
ServerOne | 10.99.199.24(ServerOne:9780)<v1>:1025
gfsh>create region --name=Example --type=PARTITION
Member | Status
--------- | ----------------------------------------
ServerOne | Region "/Example" created on "ServerOne"
gfsh>list regions
List of regions
---------------
Example
gfsh>describe region --name=/Example
..........................................................
Name : Example
Data Policy : partition
Hosting Members : ServerOne
Non-Default Attributes Shared By Hosting Members
Type | Name | Value
------ | ----------- | ---------
Region | size | 0
| data-policy | PARTITION
现在,我再次运行测试,它通过了,然后我评估集群的状态:
gfsh>describe region --name=/Example
..........................................................
Name : Example
Data Policy : partition
Hosting Members : ServerOne
Non-Default Attributes Shared By Hosting Members
Type | Name | Value
------ | ----------- | ---------
Region | size | 1
| data-policy | PARTITION
gfsh>get --region=Example --key=1 --key-class=java.lang.Integer
Result : true
Key Class : java.lang.Integer
Key : 1
Value Class : java.lang.String
Value : test
酷!成功了!我们的“示例”区域包含一个由我们的测试/应用程序放置的条目。
当然,如果我停止集群并重新运行测试,它仍然会通过,因为代码/配置智能地切换回仅本地模式,无需执行任何操作即可无缝切换。
如果您不清楚/不确定测试是否在做我所说的事情,那么只需注释掉负责A)确定GemFire集群是否可用和B)决定如何处理集群不可用时的情况的@条件注释,在这种情况下,我们只需切换到仅限本地模式。
但是,通过注释掉该条件,您将看到类似于以下内容的异常:
org.apache.geode.cache.client.NoAvailableLocatorsException: Unable to connect to any locators in the list [LocatorAddress [socketInetAddress=localhost/127.0.0.1:10334, hostname=localhost, isIpString=false]]
at org.apache.geode.cache.client.internal.AutoConnectionSourceImpl.findServer(AutoConnectionSourceImpl.java:158)
at org.apache.geode.cache.client.internal.ConnectionFactoryImpl.createClientToServerConnection(ConnectionFactoryImpl.java:234)
at org.apache.geode.cache.client.internal.pooling.ConnectionManagerImpl.borrowConnection(ConnectionManagerImpl.java:242)
at org.apache.geode.cache.client.internal.OpExecutorImpl.execute(OpExecutorImpl.java:148)
at org.apache.geode.cache.client.internal.OpExecutorImpl.execute(OpExecutorImpl.java:127)
at org.apache.geode.cache.client.internal.PoolImpl.execute(PoolImpl.java:782)
at org.apache.geode.cache.client.internal.PutOp.execute(PutOp.java:91)
at org.apache.geode.cache.client.internal.ServerRegionProxy.put(ServerRegionProxy.java:159)
at org.apache.geode.internal.cache.LocalRegion.serverPut(LocalRegion.java:3010)
at org.apache.geode.internal.cache.LocalRegion.cacheWriteBeforePut(LocalRegion.java:3121)
at org.apache.geode.internal.cache.ProxyRegionMap.basicPut(ProxyRegionMap.java:239)
at org.apache.geode.internal.cache.LocalRegion.virtualPut(LocalRegion.java:5631)
at org.apache.geode.internal.cache.LocalRegionDataView.putEntry(LocalRegionDataView.java:152)
at org.apache.geode.internal.cache.LocalRegion.basicPut(LocalRegion.java:5059)
at org.apache.geode.internal.cache.LocalRegion.validatedPut(LocalRegion.java:1597)
at org.apache.geode.internal.cache.LocalRegion.put(LocalRegion.java:1584)
at org.apache.geode.internal.cache.AbstractRegion.put(AbstractRegion.java:413)
at example.tests.spring.data.geode.clientserver.ResilientClientServerIntegrationTests.exampleRegionDataAccessOperationsAreSuccessful(ResilientClientServerIntegrationTests.java:86)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
即No可用性LocatorsException
,因为“默认”是PROXY
(这里也是如此),它期望服务器集群中存在具有相应客户端区域(即“示例”)的集群。
当然,如果您绝对且严格地不希望任何GemFire客户端对象在集群不可用时正常工作,您可以完全禁用[Spring[Boot]]应用程序/服务中的任何GemFire客户端配置。您只需在此处返回false。例如,您只需小心您的应用程序在这种情况下没有自动连接任何GemFire对象。
此外,您也可以使用SpringXML配置实现类似的效果,但是使用基于Java的Spring配置更容易演示,我将其作为练习留给您来弄清楚。
此外,测试集群可用性的逻辑虽然有效(并且硬编码为: P),但很粗糙,我让您添加更多“健壮”的逻辑。
但是,我相信这充分解决了你的问题。
希望这有帮助!
干杯!