1.zk用作配置文件,并监听配置变化
@Slf4j
public class ConfigCenter {
private final static String CONNECT_STR="192.168.25.111:2181";
private final static Integer SESSION_TIMEOUT=30*1000;
private static ZooKeeper zooKeeper=null;
//因为new ZooKeeper是异步的,底层是2个守护线程,如果没有countDownLatch有可能发生zk还没有
//连接上,程序就跑到下一步了,也就是相当new zk是一个异步的过程,用countDownLatch可以保证
//建立连接成功
private static CountDownLatch countDownLatch=new CountDownLatch(1);
public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
zooKeeper=new ZooKeeper(CONNECT_STR, SESSION_TIMEOUT, new Watcher() {
@Override
public void process(WatchedEvent event) {
//也就是zk客户端连接后会显示如下信息,这里做一个判断
//WatchedEvent state:SyncConnected type:None path:null
if (event.getType()== Event.EventType.None
&& event.getState() == Event.KeeperState.SyncConnected){
log.info("连接已建立");
countDownLatch.countDown();
}
}
});
countDownLatch.await();
MyConfig myConfig = new MyConfig();
myConfig.setKey("name");
myConfig.setName("lcc");
ObjectMapper objectMapper=new ObjectMapper();
//将默认配置序列化成字节数组
byte[] bytes = objectMapper.writeValueAsBytes(myConfig);
//创建一个zkconfig的节点,默认开放权限的持久节点
String s = zooKeeper.create("/zkconfig", bytes, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
//注册一个监听
Watcher watcher = new Watcher() {
@SneakyThrows
@Override
public void process(WatchedEvent event) {
//监听zkconfig,当数据发生变化
if (event.getType()== Event.EventType.NodeDataChanged
&& event.getPath()!=null && event.getPath().equals("/zkconfig")){
log.info(" PATH:{} 发生了数据变化" ,event.getPath());
//这里非常关键,因为zk的监听是一次性的,当监听完事件就不在继续监听了,所以这里
//需要再次监听
byte[] data = zooKeeper.getData("/zkconfig", this, null);
//拿到zk配置文件里的数据
MyConfig newConfig = objectMapper.readValue(new String(data), MyConfig.class);
log.info("数据发生变化: {}",newConfig);
}
}
};
//获取默认的数据
byte[] data = zooKeeper.getData("/zkconfig", watcher, null);
MyConfig originalMyConfig = objectMapper.readValue(new String(data), MyConfig.class);
log.info("原始数据: {}", originalMyConfig);
TimeUnit.SECONDS.sleep(3600);
}
}
应用启动,发现客户端已经有了初始化的数据

现在进行修改:

idea控制台打印日志:数据发生变化: MyConfig(key=name, name=lcd)
这就很好的实现了配置文件的动态监听
二:原生Api基本操作
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.5.8</version>
</dependency>
@Slf4j
public class BaseOperations extends StandaloneBase{
private String zk_first_node = "/zk-first-node";
//新建节点,默认权限,临时节点
@Test
public void myTestCreate() throws KeeperException, InterruptedException {
ZooKeeper zooKeeper = getZooKeeper();
String s = zooKeeper.create(zk_first_node, "first".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
log.info("Create:{}",s);
}
@Test
public void myTestGetData(){
//只要zk_first_node节点的数据发生变化就会打印日志
Watcher watcher = new Watcher() {
@Override
public void process(WatchedEvent event) {
if (event.getPath()!=null && event.getPath().equals(zk_first_node)
&& event.getType()== Event.EventType.NodeDataChanged){
log.info(" PATH: {} 发现变化",zk_first_node);
try {
byte[] data = getZooKeeper().getData(zk_first_node, this, null);
log.info(" data: {}",new String(data));
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
try {
byte[] data = getZooKeeper().getData(zk_first_node, watcher, null);
log.info(" data: {}",new String(data));
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//set的时候带上版本号,可以用作乐观锁,和cas,mysql的乐观锁一样
//也就是set的时候,dataversion会在当前版本自动+1,前提是当前版本=传递进来的版本号,
//如果高并发的时候传递进来多个相同的版本号,某一时候只能有一个修改成功
@Test
public void myTestSetData() throws KeeperException, InterruptedException {
ZooKeeper zooKeeper = getZooKeeper();
Stat stat = new Stat();
byte[] data = zooKeeper.getData(zk_first_node, false, stat);
// int version = stat.getVersion();
zooKeeper.setData(zk_first_node, "third".getBytes(), 1);
}
@Test
public void myTestDelete() throws KeeperException, InterruptedException {
// -1 代表匹配所有版本,直接删除
// 任意大于 -1 的代表可以指定数据版本删除
getZooKeeper().delete("/config",-1);
}
//异步回调,也就是耗时的操作放在异步线程中
@Test
public void myAsyncTest(){
getZooKeeper().getData("/test", false, (rc, path, ctx, data, stat) -> {
Thread thread = Thread.currentThread();
log.info(" Thread Name: {}, rc:{}, path:{}, ctx:{}, data:{}, stat:{}",thread.getName(),rc, path, ctx, data, stat);
},"test");
log.info(" over .");
}
}
curator操作zk
// 递归创建子节点,原先老的api创建子节点需要先创建父节点,然后在父节点上创建
//子节点,这里用curator可以递归创建
@Test
public void testCreateWithParent() throws Exception {
CuratorFramework curatorFramework = getCuratorFramework();
String pathWithParent = "/second-parent/sub";
//这个操作是同步执行的
String path = curatorFramework.create().creatingParentsIfNeeded().forPath(pathWithParent);
log.info("curator create node :{} successfully.", path);
}

// protection模式,防止由于异常原因,导致僵尸节点
// 当客户端发送请求给服务器创建节点,服务器接收到节点也创建好了节点,数据落盘到内存中
//但是此时服务器宕机了,客户端没有收到响应,再次发送请求,也就是执行了多次,
//底层是如何保证幂等性的呢?通过在节点上新增一个uuid
@Test
public void testCreate() throws Exception {
CuratorFramework curatorFramework = getCuratorFramework();
String forPath = curatorFramework
.create()
.withProtection()
.withMode(CreateMode.EPHEMERAL_SEQUENTIAL).
forPath("/curator-protect-node", "protect-node".getBytes());
log.info("curator create node :{} successfully.", forPath);
}

发现在curator-node前面加上了一个uuid
@Test
public void testGetData() throws Exception {
CuratorFramework curatorFramework = getCuratorFramework();
byte[] bytes = curatorFramework.getData().forPath("/_c_63335c61-80b5-4bc1-9b10-02b180d125a9-curator-node0000000002");
log.info("get data from node :{} successfully.", new String(bytes));
}
idea控制台结果:

//get操作
@Test
public void testGetData() throws Exception {
CuratorFramework curatorFramework = getCuratorFramework();
byte[] bytes = curatorFramework.getData().forPath("/_c_63335c61-80b5-4bc1-9b10-02b180d125a9-curator-node0000000002");
log.info("get data from node :{} successfully.", new String(bytes));
}
//set操作
@Test
public void testSetData() throws Exception {
CuratorFramework curatorFramework = getCuratorFramework();
curatorFramework.setData().forPath("/_c_b1ba6d26-d335-476f-85ab-ae65450a3f29-curator-node10000000005", "changed!".getBytes());
byte[] bytes = curatorFramework.getData().forPath("/_c_b1ba6d26-d335-476f-85ab-ae65450a3f29-curator-node10000000005");
log.info("get data from node /curator-node :{} successfully.", new String(bytes));
}
//删除操作
@Test
public void testDelete() throws Exception {
CuratorFramework curatorFramework = getCuratorFramework();
String pathWithParent = "/second-parent";
curatorFramework.delete().guaranteed().deletingChildrenIfNeeded().forPath(pathWithParent);
}
//遍历目录下的所有子节点
//create /discovery
//create /discovery/example
//create -s /discovery/example/x create -s /discovery/example/y create -s /discovery/example/z
@Test
public void testListChildren() throws Exception {
CuratorFramework curatorFramework = getCuratorFramework();
String pathWithParent = "/discovery/example";
List<String> strings = curatorFramework.getChildren().forPath(pathWithParent);
strings.forEach(System.out::println);
}
本文介绍如何使用ZooKeeper作为配置中心,并实现配置文件的动态监听。通过具体代码示例展示ZooKeeper的基本操作,包括节点的创建、读取、更新及删除,并对比原生API与Curator框架的操作差异。

1095

被折叠的 条评论
为什么被折叠?



