上一篇文章說到了一些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
這裏僅僅進行了簡單的模擬知道怎麼回事,需要怎麼弄,如果想要做到可以生產環境使用還需要做更多內容。