统一监听管理的优势?
因为懒, 减轻运维人员的负担, 防止由于疏忽导致集群配置文件出错.
解决思路
- 把公共配置抽取出来
- 对公共配置进行维护
- 修改公共配置后应用不需要重新部署
采用方案
-
公共配置抽取存放于zookeeper中并落地数据库
-
对公共配置修改后发布到zookeeper中并落地数据库
-
对应用开启配置实时监听,zookeeper配置文件一旦被修改,应用可实时监听到并获取
利用ZK监控节点的值来通信, 监听update信号, 内容永远都是json
ConfigBean
package com.bestksl.configFileWatcherDemo.configBean;
public class RedisConfig {
String type;
String url;
String remark;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
}
模拟监听watcher
package com.bestksl.configFileWatcherDemo.configWatcher;
import com.alibaba.fastjson.JSON;
import com.bestksl.configFileWatcherDemo.Path.MyPath;
import com.bestksl.configFileWatcherDemo.configBean.RedisConfig;
import lombok.extern.slf4j.Slf4j;
import net.minidev.json.JSONUtil;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
@Slf4j
public class ConfigWatcher implements PathChildrenCacheListener {
@Override
public void childEvent(CuratorFramework zkCli, PathChildrenCacheEvent event) throws Exception {
if (event.getType().equals(PathChildrenCacheEvent.Type.CHILD_UPDATED)) {
if (event.getData().getPath().equals(MyPath.CONFIG_ROOT + MyPath.REDIS_CONFIG)) {
String configString = new String(event.getData().getData());
log.info("节点信息为: {}", configString);
RedisConfig redisConfig = JSON.parseObject(configString, RedisConfig.class);
if (redisConfig != null) {
String type = redisConfig.getType();
String url = redisConfig.getUrl();
String remark = redisConfig.getRemark();
switch (type) {
case "add":
log.info("监听到新增的配置文件 准备下载" + url);
break;
case "delete":
log.info("监听到删除的配置文件 准备删除" + url);
break;
case "update":
log.info("监听到更新的配置文件 准备更新" + url);
break;
}
}
}
}
}
}
模拟client 1~N
大家可以复制多份模拟集群
package com.bestksl.configFileWatcherDemo.client;
import com.bestksl.configFileWatcherDemo.Path.MyPath;
import com.bestksl.configFileWatcherDemo.configWatcher.ConfigWatcher;
import com.bestksl.tools.ZkTools2;
import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.cache.PathChildrenCache;
import java.util.concurrent.CountDownLatch;
@Slf4j
public class Client1 {
static CountDownLatch countDownLatch = new CountDownLatch(1);
public static void main(String[] args) throws Exception {
ZkTools2 zkTools = new ZkTools2();
CuratorFramework zkCli = zkTools.getCurator();
PathChildrenCache pathChildrenCache = new PathChildrenCache(zkCli, MyPath.CONFIG_ROOT, true);
pathChildrenCache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT);
if (pathChildrenCache.getListenable().size() > 0) {
log.info("wrong number of listener");
return;
}
pathChildrenCache.getListenable().addListener(new ConfigWatcher());
countDownLatch.await();
}
}
初始化节点类
package com.bestksl.configFileWatcherDemo;
import com.bestksl.tools.ZkTools2;
import org.apache.curator.framework.CuratorFramework;
import org.apache.zookeeper.CreateMode;
public class SetNode {
static String json1 = "{\"type\":\"add\",\"url\":\"ftp://110:110:110:110/config/redis.xml\",\"remark\":\"666\"}";
static String json2 = "{\"type\":\"delete\",\"url\":\"ftp://110:110:110:110/config/redis.xml\",\"remark\":\"666\"}";
static String json3 = "{\"type\":\"update\",\"url\":\"ftp://110:110:110:110/config/redis.xml\",\"remark\":\"666\"}";
public static void main(String[] args) throws Exception {
ZkTools2 zkTools = new ZkTools2();
CuratorFramework zkCli = zkTools.getCurator();
zkCli.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath("/config/redis", json1.getBytes());
Thread.sleep(1000 * 3600);
}
}
ZKTools工具类
package com.bestksl.tools;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
public class ZkTools2 {
private String zkAddr = "192.168.1.101:2181,192.168.1.102:2181,192.168.1.103:2181";
private int connectTimeout = 10000;
public CuratorFramework getCurator() {
RetryPolicy retryPolicy = new ExponentialBackoffRetry(connectTimeout, 3);
CuratorFramework client = CuratorFrameworkFactory.newClient(zkAddr, retryPolicy);
client.start();
return client;
}
}
运行结果
22:31:16.322 [main-SendThread(192.168.1.103:2181)] DEBUG org.apache.zookeeper.ClientCnxn - Got WatchedEvent state:SyncConnected type:NodeDataChanged path:/config/redis for session id 0x30001f7eadb001e
22:31:16.339 [main-SendThread(192.168.1.103:2181)] DEBUG org.apache.zookeeper.ClientCnxn - Reading reply session id: 0x30001f7eadb001e, packet:: clientPath:/config/redis serverPath:/config/redis finished:false header:: 9,4 replyHeader:: 9,42949677639,0 request:: '/config/redis,T response:: #7b2274797065223a22757064617465222c2275726c223a226674703a2f2f3131303a3131303a3131303a3131302f636f6e6669672f72656469732e786d6c222c2272656d61726b223a22363636227d,s{42949677629,42949677639,1587262610496,1587263476303,3,0,0,144121699762307091,79,0,42949677629}
22:31:16.345 [Curator-PathChildrenCache-0] INFO com.bestksl.configFileWatcherDemo.configWatcher.ConfigWatcher - 节点信息为: {"type":"update","url":"ftp://110:110:110:110/config/redis.xml","remark":"666"}
22:31:16.438 [Curator-PathChildrenCache-0] INFO com.bestksl.configFileWatcherDemo.configWatcher.ConfigWatcher - 监听到更新的配置文件 准备更新ftp://110:110:110:110/config/redis.xml
22:31:29.680 [main-SendThread(192.168.1.103:2181)] DEBUG org.apache.zookeeper.ClientCnxn - Got ping response for session id: 0x30001f7eadb001e after 2ms.
22:31:42.969 [main-SendThread(192.168.1.103:2181)] DEBUG org.apache.zookeeper.ClientCnxn - Got notification session id: 0x30001f7eadb001e
22:31:42.969 [main-SendThread(192.168.1.103:2181)] DEBUG org.apache.zookeeper.ClientCnxn - Got WatchedEvent state:SyncConnected type:NodeDataChanged path:/config/redis for session id 0x30001f7eadb001e
22:31:42.971 [main-SendThread(192.168.1.103:2181)] DEBUG org.apache.zookeeper.ClientCnxn - Got ping response for session id: 0x30001f7eadb001e after 2ms.
22:31:42.972 [main-SendThread(192.168.1.103:2181)] DEBUG org.apache.zookeeper.ClientCnxn - Reading reply session id: 0x30001f7eadb001e, packet:: clientPath:/config/redis serverPath:/config/redis finished:false header:: 10,4 replyHeader:: 10,42949677640,0 request:: '/config/redis,T response:: #7b2274797065223a2264656c657465222c2275726c223a226674703a2f2f3131303a3131303a3131303a3131302f636f6e6669672f72656469732e786d6c222c2272656d61726b223a22363636227d,s{42949677629,42949677640,1587262610496,1587263502962,4,0,0,144121699762307091,79,0,42949677629}
22:31:42.974 [Curator-PathChildrenCache-0] INFO com.bestksl.configFileWatcherDemo.configWatcher.ConfigWatcher - 节点信息为: {"type":"delete","url":"ftp://110:110:110:110/config/redis.xml","remark":"666"}
22:31:42.974 [Curator-PathChildrenCache-0] INFO com.bestksl.configFileWatcherDemo.configWatcher.ConfigWatcher - 监听到删除的配置文件 准备删除ftp://110:110:110:110/config/redis.xml
22:31:56.310 [main-SendThread(192.168.1.103:2181)] DEBUG org.apache.zookeeper.ClientCnxn - Got ping response for session id: 0x30001f7eadb001e after 0ms.