Spring Boot整合Zookeeper实现配置中心
简介使用背景说到配置中心,目前市面上用的较多的配置中心都广为人知,比如百度的Disconf、Spring Cloud Config、携程的Apollo、阿里的Nacos等。由于项目组一直是使用的zookeeper作为配置中心,所以来学习使用。实现原理在Zookeeper建立一个根节点,比如/CONFIG,代表某个配置文件。将配置文件中的信息作为根节点的子节点存储,比如配置项timeout=3000
简介
使用背景
说到配置中心,目前市面上用的较多的配置中心都广为人知,比如百度的Disconf、Spring Cloud Config、携程的Apollo、阿里的Nacos等。由于项目组一直是使用的zookeeper作为配置中心,所以来学习使用。
实现原理
在Zookeeper建立一个根节点,比如/CONFIG,代表某个配置文件。将配置文件中的信息作为根节点的子节点存储,比如配置项timeout=3000,在Zookeeper中展现为:/CONFIG/timeout,节点内容是3000。然后让所有使用到该配置信息的应用机器集成Zookeeper并监控/CONFIG的状态,一旦配置信息也就是子节点发生变化,每台应用机器就会收到ZK的通知,然后从ZK中获取新的配置信息应用到系统中。
实现原理细节可参考:https://cloud.tencent.com/developer/news/365803
Spring Boot整合Zookeeper
引入依赖
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.3.0</version>
</dependency>
Curator是Apache软件基金会下的一个开源框架,目前是Apache下的顶级项目。Curator是ZooKeeper的一个Java客户端库,包括一个高层API框架和一些实用程序,使得开发人员在使用ZooKeeper时更容易,更可靠。
参考:https://blog.csdn.net/hzygcs/article/details/88171620
修改配置application.properties
由于只是简单使用,配置连接即可
zookeeper.url=192.168.1.106:2181
定义配置类
ZookeeperConfig.java
@Configuration
public class ZookeeperConfig {
Properties properties = new Properties();
//可以理解curatorFramework为客户端,基本操作都由它完成
CuratorFramework curatorFramework = null;
TreeCache treeCache = null;
@Value("${zookeeper.url}")
private String zkUrl;
//定义顶级
private final String CONFIG_NAME = "/zookeeper";
//初始化
private void init() {
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
curatorFramework = CuratorFrameworkFactory.newClient(zkUrl, retryPolicy);
treeCache = new TreeCache(curatorFramework, CONFIG_NAME);
}
/**
* 设置属性
* @param key
* @param value
* @throws Exception
*/
public void setProperties(String key, String value) throws Exception {
String propertiesKey = CONFIG_NAME + "/" + key;
Stat stat = curatorFramework.checkExists().forPath(propertiesKey);
if(stat == null) {
curatorFramework.create().forPath(propertiesKey);
}
curatorFramework.setData().forPath(propertiesKey, value.getBytes());
}
/**
* 获取属性
* @param key
* @return
*/
public String getProperties(String key) {
return properties.getProperty(key);
}
@PostConstruct
public void loadProperties() {
try {
init();
curatorFramework.start();
treeCache.start();
// 从zk中获取配置放入本地配置中
Stat stat = curatorFramework.checkExists().forPath(CONFIG_NAME);
if(stat == null) {
curatorFramework.create().forPath(CONFIG_NAME);
}
List<String> configList = curatorFramework.getChildren().forPath(CONFIG_NAME);
for (String configName : configList) {
byte[] value = curatorFramework.getData().forPath(CONFIG_NAME + "/" + configName);
properties.setProperty(configName, new String(value));
}
// 监听属性值变更
treeCache.getListenable().addListener(new TreeCacheListener() {
@Override
public void childEvent(CuratorFramework curatorFramework, TreeCacheEvent treeCacheEvent) throws Exception {
if (Objects.equals(treeCacheEvent.getType(), TreeCacheEvent.Type.NODE_ADDED) ||
Objects.equals(treeCacheEvent.getType(), TreeCacheEvent.Type.NODE_UPDATED)) {
String updateKey = treeCacheEvent.getData().getPath().replace(CONFIG_NAME + "/", "");
properties.setProperty(updateKey, new String(treeCacheEvent.getData().getData()));
System.out.println("数据更新: "+treeCacheEvent.getType()+", key:"+updateKey+",value:"+new String(treeCacheEvent.getData().getData()));
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}
配置测试接口
ZkController.java
@RestController
public class ZkController {
@Autowired
private ZookeeperConfig zookeeperConfig;
@GetMapping("get/{key}")
public String getProperties(@PathVariable String key) {
return zookeeperConfig.getProperties(key);
}
@GetMapping("set/{key}/{value}")
public String setProperties(@PathVariable String key, @PathVariable String value) throws Exception {
zookeeperConfig.setProperties(key, value);
return "配置成功";
}
}
验证
zookeeper节点情况如下:
浏览器中输入:http://localhost:8080/get/hehe 可以获取到,使用工具对其他两个node进行更新,程序控制台也可以查看到更新数据信息:
当验证set的时候,程序报错:
org.apache.zookeeper.KeeperException$UnimplementedException: KeeperErrorCode = Unimplemented for /zookeeper/123
at org.apache.zookeeper.KeeperException.create(KeeperException.java:106) ~[zookeeper-3.5.7.jar:3.5.7]
at org.apache.zookeeper.KeeperException.create(KeeperException.java:54) ~[zookeeper-3.5.7.jar:3.5.7]
at org.apache.zookeeper.ZooKeeper.create(ZooKeeper.java:1637) ~[zookeeper-3.5.7.jar:3.5.7]
at org.apache.curator.framework.imps.CreateBuilderImpl$17.call(CreateBuilderImpl.java:1180) ~[curator-framework-4.3.0.jar:4.3.0]
at org.apache.curator.framework.imps.CreateBuilderImpl$17.call(CreateBuilderImpl.java:1156) ~[curator-framework-4.3.0.jar:4.3.0]
at org.apache.curator.connection.StandardConnectionHandlingPolicy.callWithRetry(StandardConnectionHandlingPolicy.java:67) ~[curator-client-4.3.0.jar:na]
at org.apache.curator.RetryLoop.callWithRetry(RetryLoop.java:81) ~[curator-client-4.3.0.jar:na]
at org.apache.curator.framework.imps.CreateBuilderImpl.pathInForeground(CreateBuilderImpl.java:1153) ~[curator-framework-4.3.0.jar:4.3.0]
at org.apache.curator.framework.imps.CreateBuilderImpl.protectedPathInForeground(CreateBuilderImpl.java:607) ~[curator-framework-4.3.0.jar:4.3.0]
at org.apache.curator.framework.imps.CreateBuilderImpl.forPath(CreateBuilderImpl.java:597) ~[curator-framework-4.3.0.jar:4.3.0]
at org.apache.curator.framework.imps.CreateBuilderImpl.forPath(CreateBuilderImpl.java:575) ~[curator-framework-4.3.0.jar:4.3.0]
at org.apache.curator.framework.imps.CreateBuilderImpl.forPath(CreateBuilderImpl.java:51) ~[curator-framework-4.3.0.jar:4.3.0]
at com.springboot.springbootzookeeper.config.ZookeeperConfig.setProperties(ZookeeperConfig.java:55) ~[classes/:na]
at com.springboot.springbootzookeeper.controller.ZkController.setProperties(ZkController.java:28) ~[classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_101]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_101]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_101]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_101]
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190) ~[spring-web-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) ~[spring-web-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:105) ~[spring-webmvc-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:879) ~[spring-webmvc-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793) ~[spring-webmvc-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040) ~[spring-webmvc-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943) ~[spring-webmvc-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:634) ~[tomcat-embed-core-9.0.35.jar:9.0.35]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) ~[tomcat-embed-core-9.0.35.jar:9.0.35]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-9.0.35.jar:9.0.35]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.35.jar:9.0.35]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.35.jar:9.0.35]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.35.jar:9.0.35]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.35.jar:9.0.35]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.35.jar:9.0.35]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.35.jar:9.0.35]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.35.jar:9.0.35]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.35.jar:9.0.35]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.35.jar:9.0.35]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.35.jar:9.0.35]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.35.jar:9.0.35]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-9.0.35.jar:9.0.35]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541) [tomcat-embed-core-9.0.35.jar:9.0.35]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) [tomcat-embed-core-9.0.35.jar:9.0.35]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [tomcat-embed-core-9.0.35.jar:9.0.35]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [tomcat-embed-core-9.0.35.jar:9.0.35]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [tomcat-embed-core-9.0.35.jar:9.0.35]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:373) [tomcat-embed-core-9.0.35.jar:9.0.35]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) [tomcat-embed-core-9.0.35.jar:9.0.35]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868) [tomcat-embed-core-9.0.35.jar:9.0.35]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1590) [tomcat-embed-core-9.0.35.jar:9.0.35]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.35.jar:9.0.35]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_101]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_101]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.35.jar:9.0.35]
at java.lang.Thread.run(Thread.java:745) [na:1.8.0_101]
2020-06-29 16:57:18.173 INFO 13664 --- [168.1.106:2181)] org.apache.zookeeper.ClientCnxn : Unable to read additional data from server sessionid 0x10000654e3b001a, likely server has closed socket, closing socket connection and attempting reconnect
2020-06-29 16:57:18.273 INFO 13664 --- [ain-EventThread] o.a.c.f.state.ConnectionStateManager : State change: SUSPENDED
感谢
https://blog.csdn.net/forget_me_not1991/article/details/80902055
https://www.jianshu.com/p/57ac51e9987f
将curator-recipes版本调整为2.8.0后,可以将节点添加成功。
2020-06-29 17:07:11.622 INFO 14628 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2020-06-29 17:07:11.625 INFO 14628 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 3 ms
数据更新: NODE_ADDED, key:123,value:192.168.137.1
数据更新: NODE_UPDATED, key:123,value:123
更多推荐
所有评论(0)