基于zookeeper实现配置中心
api使用的是curator
不知道curator的同学 请移步:https://blog.csdn.net/qq_33449307/article/details/100562669
实现配置中心有两种模式:push pull
**实现原理:**zookeeper采用的是推拉相结合的方式.客户端向服务端注册自己需要关注的节点.一旦节点数据发生变化.那么服务器端就会向客户端发送watcher事件通知.客户端收到通知后,主动向服务器端更新数据.
适合用配置中心管理的数据的特点:
1.数据量比较小
2.数据内容在运行时发生动态变更
3.集群中的各个机器共享配置
代码实现 解释都在代码里 一看就明白了
public class Config {
private final static String CONNECTSTRING = "39.105.157.149:2181," +
"39.105.157.149:2182," +
"39.105.157.149:2183," +
"39.105.157.149:2184";
private static final String CONFIG_PREFIX = "/config";
public static CuratorFramework curatorFramework = null;
//充当缓存
private Map<String, String> cache = new HashMap<>();
//初始化zookeeper连接
public static synchronized CuratorFramework getInstance() {
if (curatorFramework == null) {
curatorFramework = CuratorFrameworkFactory.builder()
.connectString(CONNECTSTRING)
.sessionTimeoutMs(1000)
.connectionTimeoutMs(1000)
.retryPolicy(new ExponentialBackoffRetry(1000, 1000))
.build();
curatorFramework.start();
}
return curatorFramework;
}
public Config() {
this.curatorFramework = Config.getInstance();
this.init();
}
//监听配置
public void init() {
try {
//获取连接
curatorFramework = Config.getInstance();
//获取CONFIG_PREFIX的子节点 并将数据保存到缓存中
curatorFramework.getChildren().forPath(CONFIG_PREFIX).stream().forEach(k -> {
try {
String value = new String(curatorFramework.getData().forPath(CONFIG_PREFIX + "/" + k));
cache.put(k, value);
} catch (Exception e) {
e.printStackTrace();
}
});
//绑定一个监听 cacheData设为true 当事件发生后可以拿到节点发送的内容
//使该配置文件的每个应用机器都需要坚挺 这里只用于演示
PathChildrenCache pathChildrenCache = new PathChildrenCache(curatorFramework, CONFIG_PREFIX, true);
//添加监听
pathChildrenCache.getListenable().addListener((curatorFramework1, pathChildrenCacheEvent) -> {
String path = pathChildrenCacheEvent.getData().getPath();
//判断节点是否以制定开头开始
if (path.startsWith(CONFIG_PREFIX)) {
String key = path.replace(CONFIG_PREFIX + "/", "");
switch (pathChildrenCacheEvent.getType()) {
case CHILD_ADDED:
case CHILD_UPDATED: //添加节点或者修改节点数据时 修改缓存数据
cache.put(key, new String(pathChildrenCacheEvent.getData().getData()));
break;
case CHILD_REMOVED: //节点删除时 从缓存中删除数据
cache.remove(key);
break;
}
if (PathChildrenCacheEvent.Type.CHILD_ADDED.equals(pathChildrenCacheEvent.getType()) ||
PathChildrenCacheEvent.Type.CHILD_UPDATED.equals(pathChildrenCacheEvent.getType())) {
cache.put(key, new String(pathChildrenCacheEvent.getData().getData()));
}
// 子节点被删除时 从缓存中删除
if (PathChildrenCacheEvent.Type.CHILD_REMOVED.equals(pathChildrenCacheEvent.getType())) {
cache.remove(key);
}
}
});
//启动监听
pathChildrenCache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT);//POST_INITIALIZED_EVENT模式 会刷新缓存
} catch (Exception e) {
}
}
//保存配置信息
public void save(String name, String value) throws Exception {
curatorFramework = Config.getInstance();
String path = CONFIG_PREFIX + "/" + name;
//判断节点是否存在 如果不存在就创建
Stat stat = curatorFramework.checkExists().forPath(path);
if (stat == null) //如果不存在该节点就创建
curatorFramework.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath(path);
curatorFramework.setData().forPath(path, value.getBytes());
//缓存中存入数据
cache.put(name, value);
}
//获取配置信息
public String getCacheConfig(String name) {
return cache.get(name);
}
//测试main方法
public static void main(String[] args) throws Exception {
Config config = new Config();
// 模拟一个配置项,实际生产中会在系统初始化时从配置文件中加载进来
config.save("timeout", "2000");
for (int i = 0; i < 100; i++) {
System.out.println(config.getCacheConfig("timeout"));
TimeUnit.SECONDS.sleep(3);//每隔三秒打印一次
}
}
}
这里注意 为了检测修改节点数据 需使用其他客户端修改 我们这边会看到数据修改了