上一篇文章说到了一些zookeeper的应用场景,本篇将是对这些场景进行代码模拟实战的开篇文章。之前已经在本地搭建起了zk集群,并进行了一些api实践,因此本章主要讲述如何使用zk客户端模拟一个配置中心,推送变更的场景。
初始化配置bean
package com.coderman.zookeeper.clusterdemo.configdemo;
import java.io.Serializable;
/**
* @description:
* @author: Fanchunshuai
* @time: 2020/2/13 15:04
* zookeeper目录规则:/group/ip/appName
* 这里的对象需要实现序列化
*/
public class ServerConfigBean implements Serializable {
/**
* 集群节点目录一级分组
*/
private String group;
/**
* 集群节点数据内容
*/
private String nodeData;
/**
* 集群ip
*/
private String ip;
/**
* 集群中的应用名称
*/
private String appName;
public String getGroup() {
return group;
}
public void setGroup(String group) {
this.group = group;
}
public String getNodeData() {
return nodeData;
}
public void setNodeData(String nodeData) {
this.nodeData = nodeData;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public String getAppName() {
return appName;
}
public void setAppName(String appName) {
this.appName = appName;
}
@Override
public String toString() {
return "ServerConfigBean{" +
"group='" + group + '\'' +
", nodeData='" + nodeData + '\'' +
", ip='" + ip + '\'' +
", appName='" + appName + '\'' +
'}';
}
}
构建配置管理器
package com.coderman.zookeeper.clusterdemo.configdemo;
import org.I0Itec.zkclient.IZkDataListener;
import org.I0Itec.zkclient.ZkClient;
/**
* @description:
* @author: Fanchunshuai
* @time: 2020/2/14 15:45
* 配置管理器
*/
public class ConfigManager {
private static StringBuffer buffer = new StringBuffer();
private static final int CONNECT_TIMEOUT = 5000;
static {
buffer.append("192.168.1.8:2184,");
buffer.append("192.168.1.8:2181,");
buffer.append("192.168.1.8:2182,");
buffer.append("192.168.1.8:2183,");
buffer.append("192.168.1.8:2185");
}
/**
* 初始化数据
* 一般需要将配置文件从配置管理平台导入到zookeeper集群,或者从别的文件等来源将配置数据持久化到zk
*
* @return
*/
public ServerConfigBean getServerConfigBeanFromDB(){
ServerConfigBean serverConfigBean = new ServerConfigBean();
serverConfigBean.setAppName("app1");
serverConfigBean.setIp("10.0.0.1");
serverConfigBean.setNodeData("a,b,b,c,d");
serverConfigBean.setGroup("b2bgroup");
return serverConfigBean;
}
/**
* 第一次初始化数据节点,并将配置文件数据同步到zookeeper中
* 这里相当于服务端的服务接口
*/
public void syncConfigToZookeeper(ServerConfigBean serverConfigBean){
ZkClient zkClient = new ZkClient(buffer.toString(),CONNECT_TIMEOUT);
String path = "/"+serverConfigBean.getGroup()+"/"+serverConfigBean.getIp()+"/"+serverConfigBean.getAppName();
if(!zkClient.exists(path)){
//递归创建持久节点
zkClient.createPersistent(path,true);
zkClient.writeData(path,serverConfigBean);
}
zkClient.close();
}
/**
* 更新配置到zookeeper集群
* 这里相当于服务端的服务接口
* @param serverConfigBean
*/
public void updateConfigToZookeeper(ServerConfigBean serverConfigBean){
ZkClient zkClient = new ZkClient(buffer.toString(),CONNECT_TIMEOUT);
String path = "/"+serverConfigBean.getGroup()+"/"+serverConfigBean.getIp()+"/"+serverConfigBean.getAppName();
zkClient.writeData(path,serverConfigBean);
zkClient.close();
}
/**
* 从zookeeper集群中获取数据配置
* 这里相当于客户端的服务接口
* @return
*/
public ServerConfigBean getConfigFromZookeeper(ServerConfigBean serverConfigBean){
ZkClient zkClient = new ZkClient(buffer.toString(),CONNECT_TIMEOUT);
final ServerConfigBean[] serverConfigBeanx = {serverConfigBean};
String path = getPath(serverConfigBean);
//先读取数据
serverConfigBeanx[0] = zkClient.readData(path);
zkClient.subscribeDataChanges(path,new IZkDataListener (){
@Override
public void handleDataChange(String s, Object o) throws Exception {
System.out.println("config data changed = "+s);
//如果数据有变化则更新
serverConfigBeanx[0] = (ServerConfigBean)o;
System.out.println( serverConfigBeanx[0].toString());
}
@Override
public void handleDataDeleted(String s) throws Exception {
System.out.println("config data deleted = "+s);
}
});
return serverConfigBeanx[0];
}
private String getPath (ServerConfigBean serverConfigBean){
return "/"+serverConfigBean.getGroup()+"/"+serverConfigBean.getIp()+"/"+serverConfigBean.getAppName();
}
}
演示主代码
package com.coderman.zookeeper.clusterdemo.configdemo;
import com.alibaba.fastjson.JSON;
/**
* @description:
* @author: Fanchunshuai
* @time: 2020/2/14 16:16
* 配置中心变更演示案例
*/
public class ConfigChangeDemo {
public static void main(String[] args) {
ConfigManager configManager = new ConfigManager();
ServerConfigBean serverConfigBean = configManager.getServerConfigBeanFromDB();
configManager.syncConfigToZookeeper(serverConfigBean);
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
ServerConfigBean serverConfigBean1 = configManager.getConfigFromZookeeper(serverConfigBean);
System.out.println(JSON.toJSONString(serverConfigBean1));
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//更新数据
serverConfigBean1.setNodeData("sdfasdfasdfasdf");
//重新同步
configManager.updateConfigToZookeeper(serverConfigBean1);
//重新获取数据
ServerConfigBean serverConfigBean2 = configManager.getConfigFromZookeeper(serverConfigBean);
System.out.println(JSON.toJSONString(serverConfigBean2));
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//多线程更新,查看数据监听变化
//这里的线程其实就是数据操作变更的客户端
new Thread(()->{
//更新数据
serverConfigBean1.setNodeData("sdfasdfasddddfasdf");
//重新同步
configManager.updateConfigToZookeeper(serverConfigBean1);
ServerConfigBean serverConfigBean3 = configManager.getConfigFromZookeeper(serverConfigBean);
System.out.println("serverConfigBean3 = "+JSON.toJSONString(serverConfigBean3));
},"thread1").start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
//更新数据
serverConfigBean1.setNodeData("sddfasdf");
//重新同步
configManager.updateConfigToZookeeper(serverConfigBean1);
ServerConfigBean serverConfigBean4 = configManager.getConfigFromZookeeper(serverConfigBean);
System.out.println("serverConfigBean4 = "+JSON.toJSONString(serverConfigBean4));
},"thread2").start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
//更新数据
serverConfigBean1.setNodeData("sdfasdfasddddddddfasdf");
//重新同步
configManager.syncConfigToZookeeper(serverConfigBean1);
ServerConfigBean serverConfigBean5 = configManager.getConfigFromZookeeper(serverConfigBean);
System.out.println("serverConfigBean5 = "+JSON.toJSONString(serverConfigBean5));
},"thread3").start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
总结:
zookeeper实现分布式配置服务推送的主要角色有
1.zookeeper集群,相当于存储配置数据的容器
2.zookeeper管理平台,相当于管理平台提供对数据增,删,改的服务接口或者可视化界面,数据源可以是手动输入的也可以是从其他系统或文件中导入获取的。
3.zookeeper的应用客户端,比如我有一个app服务器,服务器端也需要连接zookeeper集群,监听节点变化,由于配置可能随时更新,因此应用客户端捕获到内容变化之后需要更新配置。
如果想要做的更丰富的话,就需要有下面的辅助设施
1.监控:监控zk心跳,zk集群等数据容量,数据延迟等
2.审批:节点审批,数据变更审批
相关链接:
https://www.cnblogs.com/hujunzheng/p/10932153.html
这里仅仅进行了简单的模拟知道怎么回事,需要怎么弄,如果想要做到可以生产环境使用还需要做更多内容。
来源:CSDN
作者:程序男
链接:https://blog.csdn.net/u010504064/article/details/104315282