升级spring-integration-hazelcast后启动报错:CP Subsystem is not enabled
问题背景
你之前使用spring-integration-hazelcast:1.0.0.RELEASE搭配hazelcast-all:3.12.4时应用运行正常,尝试升级到hazelcast-all:3.12.6和spring-integration-hazelcast:2.0.0.RELEASE后,应用启动失败,抛出如下异常:
cluster-manager_1 | com.hazelcast.core.HazelcastException: CP Subsystem is not enabled! cluster-manager_1 | at com.hazelcast.cp.internal.RaftInvocationManager.completeExceptionallyIfCPSubsystemNotAvailable(RaftInvocationManager.java:113) cluster-manager_1 | at com.hazelcast.cp.internal.RaftInvocationManager.invoke(RaftInvocationManager.java:186) cluster-manager_1 | at com.hazelcast.cp.internal.RaftService.getGroupInfoForProxy(RaftService.java:752) cluster-manager_1 | at com.hazelcast.cp.internal.RaftService.createRaftGroupForProxy(RaftService.java:704) cluster-manager_1 | at com.hazelcast.cp.internal.datastructures.lock.RaftLockService.doCreateProxy(RaftLockService.java:193) cluster-manager_1 | at com.hazelcast.cp.internal.datastructures.lock.RaftLockService.createProxy(RaftLockService.java:177) cluster-manager_1 | at com.hazelcast.cp.internal.datastructures.lock.RaftLockService.createProxy(RaftLockService.java:43) cluster-manager_1 | at com.hazelcast.cp.internal.CPSubsystemImpl.getLock(CPSubsystemImpl.java:76) cluster-manager_1 | at org.springframework.integration.hazelcast.HazelcastLocalInstanceRegistrar.syncConfigurationMultiMap(HazelcastLocalInstanceRegistrar.java:99) cluster-manager_1 | at org.springframework.integration.hazelcast.HazelcastLocalInstanceRegistrar.afterSingletonsInstantiated(HazelcastLocalInstanceRegistrar.java:86) cluster-manager_1 | at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:896) cluster-manager_1 | at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:878) cluster-manager_1 | at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550) cluster-manager_1 | at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:141) cluster-manager_1 | at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:747) cluster-manager_1 | at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397) cluster-manager_1 | at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) cluster-manager_1 | at com.btnty.citm.clustermanager.springboot.ClusterManagerApplication.main(ClusterManagerApplication.java:49) cluster-manager_1 | at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) cluster-manager_1 | at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) cluster-manager_1 | at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) cluster-manager_1 | at java.lang.reflect.Method.invoke(Method.java:498) cluster-manager_1 | at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:48) cluster-manager_1 | at org.springframework.boot.loader.Launcher.launch(Launcher.java:87) cluster-manager_1 | at org.springframework.boot.loader.Launcher.launch(Launcher.java:51) cluster-manager_1 | at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:52)
并且你已经验证:单独升级Hazelcast到3.12.6搭配旧版spring-integration-hazelcast:1.0.0.RELEASE可以正常运行,仅升级spring-integration-hazelcast到2.0.0.RELEASE就会触发该错误。
原因分析
这个问题的核心是spring-integration-hazelcast 2.0.0.RELEASE版本的底层实现发生了变化:
- 旧版本(1.0.0.RELEASE)使用的是Hazelcast普通的分布式锁(基于AP模型的
ILock); - 新版本(2.0.0.RELEASE)则切换为使用Hazelcast CP Subsystem中的Raft Lock(
FencedLock),这种锁提供了强一致性的分布式锁能力,但依赖于Hazelcast的CP子系统。
而在Hazelcast 3.x版本中,CP Subsystem默认是未启用的,所以当新版本的spring-integration-hazelcast尝试获取CP锁时,就会抛出CP Subsystem is not enabled!的异常。
解决方案
针对这个问题,你有两种可行的处理方式:
1. 启用Hazelcast的CP Subsystem
在你的Hazelcast配置中添加CP子系统的配置即可解决问题。
- 如果是XML配置(
hazelcast.xml):
<hazelcast> <!-- 启用CP子系统,生产环境建议设置为3/5等奇数节点数 --> <cp-subsystem> <cp-member-count>3</cp-member-count> <group-size>3</group-size> </cp-subsystem> </hazelcast>
- 如果是Java代码配置:
Config hazelcastConfig = new Config(); CPSubsystemConfig cpSubsystemConfig = hazelcastConfig.getCPSubsystemConfig(); // 设置CP成员数量,单节点测试可设为1,生产环境建议3+ cpSubsystemConfig.setCPMemberCount(3);
注意:单节点测试时可以把cp-member-count设为1,但生产环境必须使用奇数个节点(3、5等),才能保证CP子系统的一致性和容错能力。
2. 回退spring-integration-hazelcast版本(临时方案)
如果暂时不需要强一致性的分布式锁,或者不想改动Hazelcast的配置,可以回退到spring-integration-hazelcast:1.0.0.RELEASE版本,搭配Hazelcast 3.12.6使用——你已经验证过这种组合是可以正常运行的。
原Gradle配置参考(回退版本后):
implementation ("org.springframework.boot:spring-boot-starter-web:2.2.5.RELEASE") implementation ("org.springframework.integration:spring-integration-hazelcast:1.0.0.RELEASE") implementation "com.hazelcast:hazelcast-all:3.12.6" compile group: 'org.springframework.integration', name: 'spring-integration-core', version: '5.2.1.RELEASE'
内容的提问来源于stack exchange,提问作者daravetu

