这是我参与8月更文挑战的第22天,活动详情查看:8月更文挑战
我们知道 Java 应用程序经常性的会依赖于一些底层的配置信息。在我们实际业务开发中,我们一般都是把一些通用的配置信息去编写成配置文件。我们一般开发一个项目是不是有很多的配置文件,在这些配置文件里面,我们可以去配置一些比如说数据库连接信息,包括 Redis 的一些配置等等。
我们传统的这个实现方式,其实就是把配置文件以及配置信息存储到本地文件或内存中,但是我们现在是集群环境下,如果一旦项目的规模更变大,配置变更比较频繁,那么我们现在这个本地软件和内存方式的配置维护成本就比较高了,因为我们现在是集群模式下,然后如果我们的配置文件变更又比较频繁,那么我们现在每一次停机对这个配置文件内容进行修改,这样是不是维护成本是很高的呀?
Zookeeper 作为配置中心关注点
所以,我们可以使用 Zookeeper 作为分布式的配置中心来解决这些问题。那我们再去使用 Zookeeper 当作配置中心的时候,其实要关注两点:
第一点就是如何把配置信息存到 Zookeeper 中。
第二点就是当我们 Zookeeper 它的一个配置信息一旦发生改变,我们该怎么把这个改变去同步给我们当前集群中其他的 Zookeeper 服务。
Zookeeper 是如何解决这两点:
第一点,我们说它是如何存储的。其实它就是将这个配置信息存到 Zookeeper 中的一个节点中,也就是数据真正的存到的是 Zookeeper 的节点中。
第二点,我们说如何进行这个数据的同步,其实 Zookeeper 就是给这个节点去注册一个数据节点变更的 water监听,也就是对这个节点添加了一个 watch 监听,如果一旦这个节点数据发生变更,那么所有的订阅该节点的客户端都可以获取数据变更通知。
Zookeeper 作为配置中心设计思路
我们现在所举的这个案例是这样的,Java 的应用程序要去连接关系型数据库,连接关系型数据库需要 url、用户名以及密码,我们将 url、用户名以及密码,这些信息呢配置在 Zookeeper 服务内部。当这些信息一旦发生变化之后呢,我们可以通过watch 机制捕获到相应的事件读取新的配置信息。
这个案例我们的设计思路如下:
第一步:编写应用程序,连接 Zookeeper 服务器。
第二步:我们在我们所编写的工具类当中去读取 Zookeeper 服务器当中,我们提前配置在 Zookeeper 当中的相关的一系列配置信息,并把它存入本地变量,在读取信息时,一定要注册这个watcher 监听器。
第三步:当 Zookeeper 当中的配置信息一旦发生变化时,我们通过watcher 监听器捕获到这个数据发生变化了。
第四步:重新去读取配置中心当中的相关数据。
Zookeeper 作为配置中心它的设计思路是大致是这样的。接下来我们具体实现。
使用 Java 代码实现 Zookeeper 注册中心
创建一个 MyConfigCenter 类,让这个类实现 Watcher 接口,重写 process 方法,并且定义了我们要连接 Zookeeper 的服务器 ip 地址和端口号、计数器对象以及连接对象。
// zk的连接串
String IP = "192.168.0.158:2181";
// 计数器对象
CountDownLatch countDownLatch = new CountDownLatch(1);
// 连接对象
static ZooKeeper zooKeeper;
// 重写 process 方法
public void process(WatchedEvent event) {
try {
// 捕获事件状态
if (event.getType() == Event.EventType.None) {
if (event.getState() == Event.KeeperState.SyncConnected) {
System.out.println("连接成功");
countDownLatch.countDown();
} else if (event.getState() == Event.KeeperState.Disconnected) {
System.out.println("连接断开!");
} else if (event.getState() == Event.KeeperState.Expired) {
System.out.println("连接超时!");
// 超时后服务器端已经将连接释放,需要重新连接服务器端
zooKeeper = new ZooKeeper("192.168.0.158:2181", 6000,
new ZKConnectionWatcher());
} else if (event.getState() == Event.KeeperState.AuthFailed) {
System.out.println("验证失败!");
}
// 当配置信息发生变化时
} else if (event.getType() == Event.EventType.NodeDataChanged) {
//initValue();
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
通过命令方式创建要连接的节点。
[zk: 192.168.0.158(CONNECTED) 0] create /config "config"
Created /config
[zk: 192.168.0.158(CONNECTED) 1] create /config/url "192.168.0.158:3306"
Created /config/url
[zk: 192.168.0.158(CONNECTED) 2] create /config/username "root"
Created /config/username
[zk: 192.168.0.158(CONNECTED) 3] create /config/password "root"
Created /config/password
在 MyConfigCenter 类中创建三个成员变量,并生成 get 和 set 方法。
private String url;
private String username;
private String 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;
}
我们来编写一个方法,用于连接 Zookeeper 服务器,读取配置信息。初始化过程中,我们首先做的工作是打开了 Zookeeper 的链接,也就是客户端与服务端之间的链接,并且打开链接之后,去读取了 url、username 以及 password 相应的配置信息。但是别忘了,就是我们的链接在创建的过程当中,它是异步创建的。所以打开链接读取数据之前,我们还得加上计数器对象,通过计数器对象使我们的主线程休眠。
// 连接zookeeper服务器,读取配置信息
public void initValue(){
try {
//创建连接对象
zooKeeper=new ZooKeeper(IP,5000,this);
// 阻塞线程,等待连接的创建成功
countDownLatch.await();
//读取配置信息
this.url=new String(zooKeeper.getData("/config/url",true,null));
this.username=new String(zooKeeper.getData("/config/username",true,null));
this.password=new String(zooKeeper.getData("/config/password",true,null));
}catch (Exception e){
e.printStackTrace();
}
}
当连接创建成功之后,我 process 方法会被调用,这个方法调用的过程当中,我们使用计算器对象通知线程继续向下执行就可以了。
接下来呢我们为当前类去编写编写构造方法,在构造方法当中调用这个初始化数据这个方法。这样的话,我们在创建对象的过程当中,创建 MyConfigCenter 这个对象时,实际上已经初始化到一系列成员变量当中去。
//构造方法
public MyConfigCenter(){
initValue();
}
目前这个程序有没有问题,来编写一个 main 方法。通过这个 main 方法,模拟一个客户端,通过这个客户端去创建这个读取配置信息的工具类,并且通过这个工具类去获取配置信息。
public static void main(String[] args) {
try {
MyConfigCenter myConfigCenter = new MyConfigCenter();
for (int i = 1; i <= 20; i++) {
Thread.sleep(5000);
System.out.println("url:"+myConfigCenter.getUrl());
System.out.println("username:"+myConfigCenter.getUsername());
System.out.println("password:"+myConfigCenter.getPassword());
System.out.println("########################################");
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
最后我们来测试,启动我们的程序,看控制台的输出信息。
连接成功
url:192.168.0.158:3306
username:root
password:root
现在我们在 Zookeeper 客户端通过命令来修改配置信息,看一下控制台输出的信息。
[zk: 192.168.0.158(CONNECTED) 15] set /config/url "192.168.0.158:3308"
连接成功
url:192.168.0.158:3308
username:root
password:root
到目前为止,我们学习完了 Zookeeper 作为配置中心的使用。