配置中心案例
工作中有这样的一个场景:数据库用户名和密码信息放在一个配置文件中,应用读取该配置文件,配置文件信息放入缓存。 若数据库的用户名和密码改变时候,还需要重新加载缓存,比较麻烦。通过zooKeeper可以轻松完成,当数据库发生变化时自动完成缓存同步。
设计思路:
- 连接zookeeper服务器。
- 读取zookeeper中的配置信息,注册watcher监听器,读取配置信息存入本地变量。
- 当zookeeper中的配置信息发生变化时,通过watcher的回调方法捕获数据变化事件。
- 回调中重新获取配置信息,并注册新的监听事件。
获取配置信息之前先创建好配置信息节点数据:
create /config "dbconfig"
create /config/url "jdbc:mysql://localhost:3306/zookeeper"
create /config/username "root"
create /config/password "123456"
package com.huazai.zookeeper.zkexample.config;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import java.util.concurrent.CountDownLatch;
/**
* @author pyh
* @date 2021/5/19 0:20
*/
public class ZookeeperConnection {
public static void main(String[] args) {
connect();
}
public static ZooKeeper zooKeeper;
public static ZooKeeper connect() {
try {
// 由于连接zookeeper服务器是异步连接,需要CountDownLatch阻塞主线程,等待子线程连接结果后反馈给主线程
CountDownLatch countDownLatch = new CountDownLatch(1);
/*
connectString:服务器的ip和端口
sessionTimeout:客户端与服务器之间的会话超时时间,以毫秒为单位的
watcher:监视器对象
*/
zooKeeper = new ZooKeeper("192.168.64.132:2181", 50000, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
if (watchedEvent.getState() == Event.KeeperState.SyncConnected) {
System.out.println("zookeeper异步连接成功");
} else if (watchedEvent.getState() == Event.KeeperState.Disconnected) {
System.out.println("断开连接");
} else if (watchedEvent.getState() == Event.KeeperState.Expired) {
System.out.println("会话超时");
} else if (watchedEvent.getState() == Event.KeeperState.AuthFailed) {
System.out.println("认证失败");
} else if (watchedEvent.getState() == Event.KeeperState.Closed) {
System.out.println("连接关闭");
}
countDownLatch.countDown();
System.out.println("使用构造函数默认的watcher");
System.out.println("path=" + watchedEvent.getPath());
System.out.println("eventType=" + watchedEvent.getType());
}
});
// 主线程阻塞等待连接对象的创建成功
countDownLatch.await();
// 会话编号
System.out.println("客户端sessionId:" + zooKeeper.getSessionId());
return zooKeeper;
} catch (Exception e) {
System.out.println("zookeeper连接异常");
e.printStackTrace();
}
return null;
}
/**
* 关闭zookeeper会话
*/
public static void close() {
if (zooKeeper != null) {
try {
zooKeeper.close();
System.out.println("zookeeper关闭成功");
} catch (InterruptedException e) {
System.out.println("zookeeper关闭失败");
e.printStackTrace();
}
}
}
}
package com.huazai.zookeeper.zkexample.config;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.junit.Test;
import java.util.concurrent.TimeUnit;
/**
* @author pyh
* @date 2021/6/7 22:18
*/
public class ConfigCenter implements Watcher {
private ZooKeeper zooKeeper;
ConfigCenter configCenter;
private DBInfo dbInfo;
public ConfigCenter() {
// 1.初始化连接对象
zooKeeper = ZookeeperConnection.connect();
}
/**
* 3.当zookeeper中的配置信息发生变化时,通过watcher的回调方法捕获数据变化事件
*
* @param event
*/
@Override
public void process(WatchedEvent event) {
try {
// 4.回调中重新获取配置信息,并注册新的监听事件
System.out.println("监听到数据源发生改变,正在获取最新配置信息...");
initValue();
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
public void test() throws Exception {
configCenter = new ConfigCenter();
// 1.模拟程序启动的时候第一遍获取数据源
configCenter.initValue();
TimeUnit.SECONDS.sleep(100);
}
/**
* 2.初始化数据源存入本地变量,并注册监听器
*/
private DBInfo initValue() throws Exception {
String url = getData("/config/url", this);
String username = getData("/config/username", this);
String password = getData("/config/password", this);
dbInfo = new DBInfo(url, username, password);
System.out.println(dbInfo);
return dbInfo;
}
private String getData(final String path, Watcher watcher) throws Exception {
String data = new String(zooKeeper.getData(path, watcher, null));
return data;
}
}
class DBInfo {
private String url;
private String username;
private String password;
public DBInfo() {
}
public DBInfo(String url, String username, String password) {
this.url = url;
this.username = username;
this.password = password;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "DBInfo{" +
"url='" + url + '\'' +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
成功启动程序后,分别执行以下命令,修改/config/password节点数据:
set /config/password "111111"
set /config/password "888888"
set /config/password "666666"
程序控制台输出结果如下: