Zookeeper distributed registry configuration center (code implementation)

Time:2021-7-22

preface

Recently, I’ve been learning about zookeeper distributed, and found that there are many difficult points in its code implementation, especially the operation of responsive programming. Here I record the complete code writing process (the idea of each step). This is the first code process of zookeeper distributed registration and configuration center, There will be a second article on the simple implementation of zookeeper distributed lock.
Chapter 2: zookeeper distributed lock implementation:
https://segmentfault.com/a/11…

Distributed registry configuration center

Zookeeper has the function of publish and subscribe because of its watcher mechanism. The publish and subscribe model, the so-called configuration center, just as its name implies, is that the publisher publishes the data to the ZK node, so that the subscriber can obtain the data dynamically, and realize the centralized management and dynamic update of configuration information. When the application starts, it will take the initiative to obtain a configuration, and at the same time, it will register a watcher on the node. In this way, every time the configuration is updated in the future, it will notify the subscribing client in real time, so as to obtain the latest configuration information.

Code implementation process

The first choice is to explain the experimental environment. The version of zookeeper is 3.5.8, and the code tool is idea. A maven project is created, and only the following dependencies are added.

<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.5.8</version>
</dependency>

As the client needs to establish a connection with zookeeper, obtain data, add monitoring, and so on, a utils tool class is encapsulated here for our use.

Then for zookeeper, the address of the client can be followed by a path as the working directory under the root directory. This directory is the root directory of all operations. Here / test

At the same time, because zookeeper implements publish and Subscribe Based on the watch mechanism, all our watchers are implemented in a user-defined way. The first is the default watcher when the connection is successful.

The default watcher code is as follows:
package org.qzx.config;

import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;

/**
 * @Auther: qzx
 *@ date: 2020 / 10 / 29 - 10 - 29 - 1:05 PM
 * @Description: org.qzx.config
 * @version: 1.0
 */
public class DefaultWatcher implements Watcher {
    @Override
    public void process(WatchedEvent event) {
        System.out.println(event.toString());
    }
}
Utils tool class:
package org.qzx.config;

import org.apache.zookeeper.ZooKeeper;

/**
 * @Auther: qzx
 *@ date: 2020 / 10 / 29 - 10 - 29 - 1:02 PM
 * @Description: org.qzx.config
 * @version: 1.0
 */
public class Utils {
    //Zookeeper object
    private static ZooKeeper zooKeeper;
    //Connection address
    private static String address = "10.211.55.5:2181,10.211.55.8:2181,10.211.55.9:2181,10.211.55.10:2181/test";
    private static DefaultWatcher defaultWatcher = new DefaultWatcher();

    public static ZooKeeper getZooKeeper() throws Exception{
        zooKeeper = new ZooKeeper(address,3000,defaultWatcher);
        return zooKeeper;
    }
}

Since zookeeper uses asynchronous call, it is necessary to lock the main thread with a lock. After the connection is successful, it will be automatically unlocked and the main thread will go on. Here, countdownlatch is used to implement the lock, which is created in the main thread and passed to the fallback function of dafault watcher.

The default watcher code is modified as follows:
package org.qzx.config;

import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;

import java.util.concurrent.CountDownLatch;

/**
 * @Auther: qzx
 *@ date: 2020 / 10 / 29 - 10 - 29 - 1:05 PM
 * @Description: org.qzx.config
 * @version: 1.0
 */
public class DefaultWatcher implements Watcher {
    private CountDownLatch latch;

    public void setLatch(CountDownLatch latch) {
        this.latch = latch;
    }

    @Override
    public void process(WatchedEvent event) {
        switch (event.getState()) {
            case Unknown:
                break;
            case Disconnected:
                break;
            case NoSyncConnected:
                break;
            case SyncConnected:
                latch.countDown();
                break;
            case AuthFailed:
                break;
            case ConnectedReadOnly:
                break;
            case SaslAuthenticated:
                break;
            case Expired:
                break;
            case Closed:
                break;
        }
        System.out.println(event.toString());
    }
}
The modification code of utils is as follows:
package org.qzx.config;

import org.apache.zookeeper.ZooKeeper;

import java.util.concurrent.CountDownLatch;

/**
 * @Auther: qzx
 *@ date: 2020 / 10 / 29 - 10 - 29 - 1:02 PM
 * @Description: org.qzx.config
 * @version: 1.0
 */
public class Utils {
    //Zookeeper object
    private static ZooKeeper zooKeeper;
    //Connection address
    private static String address = "10.211.55.5:2181,10.211.55.8:2181,10.211.55.9:2181,10.211.55.10:2181/test";
    private static DefaultWatcher defaultWatcher = new DefaultWatcher();
    //Lock
    private static CountDownLatch latch = new CountDownLatch(1);

    public static ZooKeeper getZooKeeper() throws Exception{
        zooKeeper = new ZooKeeper(address,3000,defaultWatcher);
        defaultWatcher.setLatch(latch);
        latch.await();
        return zooKeeper;
    }
}

The next step is to write the configuration class testconfig. First, connect before the operation and close after the operation, corresponding to the conn and close methods respectively, and then the configuration method getconfig. It is not clear whether the zookeeper client must contain a custom working directory, so it is generally preferred to use the exists method for testing. In addition, there is a watcher and a callback function in the exists method. If it exists in the callback function, it needs to call the GetData method to get the data, and there is a watcher and callback function in the GetData method, which will cause the code to be too deep to read. Therefore, a tool class is also defined here to encapsulate all the watchers and callback functions. The name of this class is mywatcherandcallback

The general framework of mywatcherandcallback is as follows:
package org.qzx.config;

import org.apache.zookeeper.AsyncCallback;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.data.Stat;

/**
 * @Auther: qzx
 *@ date: 2020 / 10 / 29 - 10 - 29 - 1:40 PM
 * @Description: org.qzx.config
 * @version: 1.0
 */
public class MyWatcherAndCallBack implements Watcher, AsyncCallback.StatCallback, AsyncCallback.DataCallback {
    // StatCallback
    @Override
    public void processResult(int rc, String path, Object ctx, Stat stat) {

    }
    // Watcher
    @Override
    public void process(WatchedEvent event) {

    }
    // DataCallback
    @Override
    public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) {

    }
}
The corresponding testconfig class code is as follows:
package org.qzx.config;

import org.apache.zookeeper.ZooKeeper;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.util.zip.ZipOutputStream;


/**
 * @Auther: qzx
 *@ date: 2020 / 10 / 29 - 10 - 29 - 1:29 PM
 * @Description: org.qzx.config
 * @version: 1.0
 */
public class TestConfig {
    private ZooKeeper zooKeeper;
    private MyWatcherAndCallBack watcherAndCallBack = new MyWatcherAndCallBack();

    @Before
    public void conn() throws Exception {
        zooKeeper = Utils.getZooKeeper();
    }

    @After
    public void close() throws InterruptedException {
        zooKeeper.close();
    }

    @Test
    public void getConf(){
        //Here / appconf is actually / test / appconf in zookeeper,
        zooKeeper.exists("/AppConf",watcherAndCallBack,watcherAndCallBack,"123");
    }
}

At this time, we have to consider what we need to do when we successfully judge the existence of appconf in the working directory. In fact, it is very simple, that is, to obtain the data of the current node.

The modified code of mywatcherandcallback is as follows:
package org.qzx.config;

import org.apache.zookeeper.AsyncCallback;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;

/**
 * @Auther: qzx
 *@ date: 2020 / 10 / 29 - 10 - 29 - 1:40 PM
 * @Description: org.qzx.config
 * @version: 1.0
 */
public class MyWatcherAndCallBack implements Watcher, AsyncCallback.StatCallback, AsyncCallback.DataCallback {
    private ZooKeeper zooKeeper;

    public void setZooKeeper(ZooKeeper zooKeeper) {
        this.zooKeeper = zooKeeper;
    }

    // StatCallback
    @Override
    public void processResult(int rc, String path, Object ctx, Stat stat) {
        if(stat!=null){
            //Node exists to get data
            zooKeeper.getData("/AppConf",this,this,"aaa");
        }
    }
    // Watcher
    @Override
    public void process(WatchedEvent event) {

    }
    // DataCallback
    @Override
    public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) {

    }
}
The modified code of testconfig is as follows:
package org.qzx.config;

import org.apache.zookeeper.ZooKeeper;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.util.zip.ZipOutputStream;


/**
 * @Auther: qzx
 *@ date: 2020 / 10 / 29 - 10 - 29 - 1:29 PM
 * @Description: org.qzx.config
 * @version: 1.0
 */
public class TestConfig {
    private ZooKeeper zooKeeper;
    private final MyWatcherAndCallBack watcherAndCallBack = new MyWatcherAndCallBack();

    @Before
    public void conn() throws Exception {
        zooKeeper = Utils.getZooKeeper();
    }

    @After
    public void close() throws InterruptedException {
        zooKeeper.close();
    }

    @Test
    public void getConf(){
        watcherAndCallBack.setZooKeeper(zooKeeper);
        //Here / appconf is actually / test / appconf in zookeeper,
        zooKeeper.exists("/AppConf",watcherAndCallBack,watcherAndCallBack,"123");
    }
}

Now, let’s consider another problem. When we retrieve data, zookeeper actually uses the asynchronous call model. Instead of waiting for the data to be retrieved, we continue to perform the task of the main thread directly. How can the main thread know when the data is retrieved? So here we have to prepare an object to receive data,This class is called myconf, and the corresponding code is as follows

package org.qzx.config;

/**
 * @Auther: qzx
 *@ date: 2020 / 10 / 29 - 10 - 29 - 2:00 pm
 * @Description: org.qzx.config
 * @version: 1.0
 */
public class MyConf {
    private String confData;

    public String getConfData() {
        return confData;
    }

    public void setConfData(String confData) {
        this.confData = confData;
    }
}

Because the main thread needs to accept data, the object must be aggregated in the testconfig class, and the data must be set for myconf in the callback function of GetData, so the object must also be aggregated in mywatcherandcallback.

The modification code of testconfig class is as follows:
package org.qzx.config;

import org.apache.zookeeper.ZooKeeper;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.util.zip.ZipOutputStream;


/**
 * @Auther: qzx
 *@ date: 2020 / 10 / 29 - 10 - 29 - 1:29 PM
 * @Description: org.qzx.config
 * @version: 1.0
 */
public class TestConfig {
    private ZooKeeper zooKeeper;
    private final MyWatcherAndCallBack watcherAndCallBack = new MyWatcherAndCallBack();
    //Accept data
    MyConf myConf = new MyConf();

    @Before
    public void conn() throws Exception {
        zooKeeper = Utils.getZooKeeper();
    }

    @After
    public void close() throws InterruptedException {
        zooKeeper.close();
    }

    @Test
    public void getConf(){
        watcherAndCallBack.setZooKeeper(zooKeeper);
        watcherAndCallBack.setMyConf(myConf);
        //Here / appconf is actually / test / appconf in zookeeper,
        zooKeeper.exists("/AppConf",watcherAndCallBack,watcherAndCallBack,"123");

    }
}
The mywatcherandcallback code is modified as follows:
package org.qzx.config;

import org.apache.zookeeper.AsyncCallback;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;

/**
 * @Auther: qzx
 *@ date: 2020 / 10 / 29 - 10 - 29 - 1:40 PM
 * @Description: org.qzx.config
 * @version: 1.0
 */
public class MyWatcherAndCallBack implements Watcher, AsyncCallback.StatCallback, AsyncCallback.DataCallback {
    private ZooKeeper zooKeeper;
    private MyConf myConf;

    public void setMyConf(MyConf myConf) {
        this.myConf = myConf;
    }

    public void setZooKeeper(ZooKeeper zooKeeper) {
        this.zooKeeper = zooKeeper;
    }

    // StatCallback
    @Override
    public void processResult(int rc, String path, Object ctx, Stat stat) {
        if(stat!=null){
            //Node exists to get data
            zooKeeper.getData("/AppConf",this,this,"aaa");
        }
    }
    // Watcher
    @Override
    public void process(WatchedEvent event) {

    }
    // DataCallback
    @Override
    public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) {
        if(stat!=null){
            myConf.setConfData(new String(data));
        }
    }
}

In this way, after the data is retrieved, the data can be seen in testconfig. There is a problem here. When exists is executed, it will not wait for data acquisition, but will be executed all the time. However, when judging, if there is this node and data acquisition should be an atomic operation, here we will encapsulate these two steps into one operation. We can add a method to the mywatcherandcallback class to wait for the operation to be executed, so as to obtain the data result. This method is called await (). Here, we move the exists method to the await method, and use countdownlatch to block the operation until the data is obtained successfully. Here, a countdownlatch is used to encapsulate the judgment of the existence of nodes and the acquisition of data. If the exists method is locked in testconfig, the lock must be passed to mywatcherandcallback to be unlocked at the end of the GetData callback. This implementation is obviously not better than moving it to await method semantically.

The modified code of mywatcherandcallback is as follows:
package org.qzx.config;

import org.apache.zookeeper.AsyncCallback;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;

import java.util.concurrent.CountDownLatch;

/**
 * @Auther: qzx
 *@ date: 2020 / 10 / 29 - 10 - 29 - 1:40 PM
 * @Description: org.qzx.config
 * @version: 1.0
 */
public class MyWatcherAndCallBack implements Watcher, AsyncCallback.StatCallback, AsyncCallback.DataCallback {
    private ZooKeeper zooKeeper;
    private MyConf myConf;
    private CountDownLatch latch = new CountDownLatch(1);

    public void setMyConf(MyConf myConf) {
        this.myConf = myConf;
    }

    public void setZooKeeper(ZooKeeper zooKeeper) {
        this.zooKeeper = zooKeeper;
    }

    // StatCallback
    @Override
    public void processResult(int rc, String path, Object ctx, Stat stat) {
        if(stat!=null){
            //Node exists to get data
            zooKeeper.getData("/AppConf",this,this,"aaa");
        }
    }
    // Watcher
    @Override
    public void process(WatchedEvent event) {

    }
    // DataCallback
    @Override
    public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) {
        if(stat!=null){
            myConf.setConfData(new String(data));
            latch.countDown();
        }
    }

    public void aWait() throws InterruptedException {
        //Here / appconf is actually / test / appconf in zookeeper,
        zooKeeper.exists("/AppConf",this,this,"123");
        latch.await();
    }
}
The modification code of testconfig is as follows:
package org.qzx.config;

import org.apache.zookeeper.ZooKeeper;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.util.zip.ZipOutputStream;


/**
 * @Auther: qzx
 *@ date: 2020 / 10 / 29 - 10 - 29 - 1:29 PM
 * @Description: org.qzx.config
 * @version: 1.0
 */
public class TestConfig {
    private ZooKeeper zooKeeper;
    private final MyWatcherAndCallBack watcherAndCallBack = new MyWatcherAndCallBack();
    //Accept data
    MyConf myConf = new MyConf();

    @Before
    public void conn() throws Exception {
        zooKeeper = Utils.getZooKeeper();
    }

    @After
    public void close() throws InterruptedException {
        zooKeeper.close();
    }

    @Test
    public void getConf() throws InterruptedException {
        watcherAndCallBack.setZooKeeper(zooKeeper);
        watcherAndCallBack.setMyConf(myConf);
        watcherAndCallBack.aWait();

    }
}

Now we have written the path to judge the existence of a node and successfully obtain the node data. Next, we consider the modification of the node. First, when the node does not exist, the callback of exists will not be executed. When the node is created, the watcher registered in exists will be executed. Then we only need to call the data, The second is that the data in the node is modified. We need to get the new node data again and set it to confdata. The second is that the node is deleted. We need to leave the data in confdata blank. In order to observe the change of data, print the set data circularly in testconfig.

The testconfig code is as follows:
package org.qzx.config;

import org.apache.zookeeper.ZooKeeper;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.util.concurrent.TimeUnit;
import java.util.zip.ZipOutputStream;


/**
 * @Auther: qzx
 *@ date: 2020 / 10 / 29 - 10 - 29 - 1:29 PM
 * @Description: org.qzx.config
 * @version: 1.0
 */
public class TestConfig {
    private ZooKeeper zooKeeper;
    private final MyWatcherAndCallBack watcherAndCallBack = new MyWatcherAndCallBack();
    //Accept data
    MyConf myConf = new MyConf();

    @Before
    public void conn() throws Exception {
        zooKeeper = Utils.getZooKeeper();
    }

    @After
    public void close() throws InterruptedException {
        zooKeeper.close();
    }

    @Test
    public void getConf() throws InterruptedException {
        watcherAndCallBack.setZooKeeper(zooKeeper);
        watcherAndCallBack.setMyConf(myConf);
        watcherAndCallBack.aWait();
        while (true){
            System.out.println(myConf.getConfData());
            TimeUnit.SECONDS.sleep(2);
        }
    }
}
Mywatcherandcallback code is as follows:
package org.qzx.config;

import org.apache.zookeeper.AsyncCallback;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;

import java.util.concurrent.CountDownLatch;

/**
 * @Auther: qzx
 *@ date: 2020 / 10 / 29 - 10 - 29 - 1:40 PM
 * @Description: org.qzx.config
 * @version: 1.0
 */
public class MyWatcherAndCallBack implements Watcher, AsyncCallback.StatCallback, AsyncCallback.DataCallback {
    private ZooKeeper zooKeeper;
    private MyConf myConf;
    private final CountDownLatch latch = new CountDownLatch(1);

    public void setMyConf(MyConf myConf) {
        this.myConf = myConf;
    }

    public void setZooKeeper(ZooKeeper zooKeeper) {
        this.zooKeeper = zooKeeper;
    }

    // StatCallback
    @Override
    public void processResult(int rc, String path, Object ctx, Stat stat) {
        if(stat!=null){
            //Node exists to get data
            zooKeeper.getData("/AppConf",this,this,"aaa");
        }
    }
    // Watcher
    @Override
    public void process(WatchedEvent event) {
        switch (event.getType()) {
            case None:
                break;
            case NodeCreated:
                //Node creation requires data acquisition
                zooKeeper.getData("/AppConf",this,this,"bbb");
                break;
            case NodeDeleted:
                //Node deletion needs to clear data
                myConf.setConfData("");
                break;
            case NodeDataChanged:
                //Data modification
                zooKeeper.getData("/AppConf",this,this,"bbb");
                break;
            case NodeChildrenChanged:
                break;
            case DataWatchRemoved:
                break;
            case ChildWatchRemoved:
                break;
        }
    }
    // DataCallback
    @Override
    public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) {
        if(stat!=null){
            myConf.setConfData(new String(data));
            latch.countDown();
        }
    }

    public void aWait() throws InterruptedException {
        //Here / appconf is actually / test / appconf in zookeeper,
        zooKeeper.exists("/AppConf",this,this,"123");
        latch.await();
    }
}

The next step is to test whether the program is correct. First, start four zookeepers, and then create the test working directory in the root directory
Zookeeper distributed registry configuration center (code implementation)
Then start the program, and manually create the appconf node in the zookeeper client, and set the data olddata.
Zookeeper distributed registry configuration center (code implementation)
You can see the program output olddata
Zookeeper distributed registry configuration center (code implementation)
Now modify the node data to newdata
Zookeeper distributed registry configuration center (code implementation)
Then you can see the program output newdata
Zookeeper distributed registry configuration center (code implementation)
During the test, it is found that if the node is deleted, the empty string will be continuously output. This comparison takes up IO and resources, and is modified to block and wait for the data to be not empty. At the same time, if the data is empty when outputting, print a message that the data is empty. Here, for the node deletion code in mywatcherandcallback, we need to pay attention to that we call the await method to achieve the blocking, because this will automatically unlock the node data when it exists, and then output the node data, but because countdownlatch has been reduced, So we need to reassign latch here.

The mywatcherandcallback code is modified as follows:
package org.qzx.config;

import org.apache.zookeeper.AsyncCallback;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;

import java.util.concurrent.CountDownLatch;

/**
 * @Auther: qzx
 *@ date: 2020 / 10 / 29 - 10 - 29 - 1:40 PM
 * @Description: org.qzx.config
 * @version: 1.0
 */
public class MyWatcherAndCallBack implements Watcher, AsyncCallback.StatCallback, AsyncCallback.DataCallback {
    private ZooKeeper zooKeeper;
    private MyConf myConf;
    private CountDownLatch latch = new CountDownLatch(1);

    public void setMyConf(MyConf myConf) {
        this.myConf = myConf;
    }

    public void setZooKeeper(ZooKeeper zooKeeper) {
        this.zooKeeper = zooKeeper;
    }

    // StatCallback
    @Override
    public void processResult(int rc, String path, Object ctx, Stat stat) {
        if(stat!=null){
            //Node exists to get data
            zooKeeper.getData("/AppConf",this,this,"aaa");
        }
    }
    // Watcher
    @Override
    public void process(WatchedEvent event) {
        switch (event.getType()) {
            case None:
                break;
            case NodeCreated:
                //Node creation requires data acquisition
                zooKeeper.getData("/AppConf",this,this,"bbb");
                break;
            case NodeDeleted:
                //Node deletion needs to clear data and wait for data to arrive
                myConf.setConfData("");
                latch = new CountDownLatch(1);
                break;
            case NodeDataChanged:
                //Data modification
                zooKeeper.getData("/AppConf",this,this,"bbb");
                break;
            case NodeChildrenChanged:
                break;
            case DataWatchRemoved:
                break;
            case ChildWatchRemoved:
                break;
        }
    }
    // DataCallback
    @Override
    public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) {
        if(stat!=null){
            myConf.setConfData(new String(data));
            latch.countDown();
        }
    }

    public void aWait() throws InterruptedException {
        //Here / appconf is actually / test / appconf in zookeeper,
        zooKeeper.exists("/AppConf",this,this,"123");
        latch.await();
    }
}
The code of testconfig is as follows:
package org.qzx.config;

import org.apache.zookeeper.ZooKeeper;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.util.concurrent.TimeUnit;
import java.util.zip.ZipOutputStream;


/**
 * @Auther: qzx
 *@ date: 2020 / 10 / 29 - 10 - 29 - 1:29 PM
 * @Description: org.qzx.config
 * @version: 1.0
 */
public class TestConfig {
    private ZooKeeper zooKeeper;
    private final MyWatcherAndCallBack watcherAndCallBack = new MyWatcherAndCallBack();
    //Accept data
    MyConf myConf = new MyConf();

    @Before
    public void conn() throws Exception {
        zooKeeper = Utils.getZooKeeper();
    }

    @After
    public void close() throws InterruptedException {
        zooKeeper.close();
    }

    @Test
    public void getConf() throws InterruptedException {
        watcherAndCallBack.setZooKeeper(zooKeeper);
        watcherAndCallBack.setMyConf(myConf);
        watcherAndCallBack.aWait();
        while (true){
            if(myConf.getConfData().equals("")){
                System. Out. Println ("data is empty");
                watcherAndCallBack.aWait();//  Waiting for data to arrive
            }
            System.out.println(myConf.getConfData());
            TimeUnit.SECONDS.sleep(2);
        }
    }
}

Next, retest the deletion of the node
Zookeeper distributed registry configuration center (code implementation)
After deleting the node, you will be prompted to find that the output data of the program is empty, and then it will be blocked.
Zookeeper distributed registry configuration center (code implementation)
Now recreate the node:
Zookeeper distributed registry configuration center (code implementation)
You will find that you have retrieved the data of the node.
Zookeeper distributed registry configuration center (code implementation)

At this point, the code for zookeeper configuration registration is completed.

Here is a brief summary of zookeeper’s configuration registration. Configuration registration is essentially a node shared by the unified management server, and its configuration information is all written in the 1m data. After a server modifies the node, other servers will receive the message through zookeeper’s watcher mechanism, In this way, the coordination function of distributed services is completed.

The configuration center code of zookeeper is arranged as follows

Utils class:
package org.qzx.config;

import org.apache.zookeeper.ZooKeeper;

import java.util.concurrent.CountDownLatch;

/**
 * @Auther: qzx
 *@ date: 2020 / 10 / 29 - 10 - 29 - 1:02 PM
 * @Description: org.qzx.config
 * @version: 1.0
 */
public class Utils {
    //Zookeeper object
    private static ZooKeeper zooKeeper;
    //Connection address
    private static String address = "10.211.55.5:2181,10.211.55.8:2181,10.211.55.9:2181,10.211.55.10:2181/test";
    private static DefaultWatcher defaultWatcher = new DefaultWatcher();
    //Lock
    private static CountDownLatch latch = new CountDownLatch(1);

    public static ZooKeeper getZooKeeper() throws Exception{
        zooKeeper = new ZooKeeper(address,3000,defaultWatcher);
        defaultWatcher.setLatch(latch);
        latch.await();
        return zooKeeper;
    }
}
Defaultwatcher class:
package org.qzx.config;

import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;

import java.util.concurrent.CountDownLatch;

/**
 * @Auther: qzx
 *@ date: 2020 / 10 / 29 - 10 - 29 - 1:05 PM
 * @Description: org.qzx.config
 * @version: 1.0
 */
public class DefaultWatcher implements Watcher {
    private CountDownLatch latch;

    public void setLatch(CountDownLatch latch) {
        this.latch = latch;
    }

    @Override
    public void process(WatchedEvent event) {
        switch (event.getState()) {
            case Unknown:
                break;
            case Disconnected:
                break;
            case NoSyncConnected:
                break;
            case SyncConnected:
                latch.countDown();
                break;
            case AuthFailed:
                break;
            case ConnectedReadOnly:
                break;
            case SaslAuthenticated:
                break;
            case Expired:
                break;
            case Closed:
                break;
        }
        System.out.println(event.toString());
    }
}
Mywatcherandcallback class:
package org.qzx.config;

import org.apache.zookeeper.AsyncCallback;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;

import java.util.concurrent.CountDownLatch;

/**
 * @Auther: qzx
 *@ date: 2020 / 10 / 29 - 10 - 29 - 1:40 PM
 * @Description: org.qzx.config
 * @version: 1.0
 */
public class MyWatcherAndCallBack implements Watcher, AsyncCallback.StatCallback, AsyncCallback.DataCallback {
    private ZooKeeper zooKeeper;
    private MyConf myConf;
    private CountDownLatch latch = new CountDownLatch(1);

    public void setMyConf(MyConf myConf) {
        this.myConf = myConf;
    }

    public void setZooKeeper(ZooKeeper zooKeeper) {
        this.zooKeeper = zooKeeper;
    }

    // StatCallback
    @Override
    public void processResult(int rc, String path, Object ctx, Stat stat) {
        if(stat!=null){
            //Node exists to get data
            zooKeeper.getData("/AppConf",this,this,"aaa");
        }
    }
    // Watcher
    @Override
    public void process(WatchedEvent event) {
        switch (event.getType()) {
            case None:
                break;
            case NodeCreated:
                //Node creation requires data acquisition
                zooKeeper.getData("/AppConf",this,this,"bbb");
                break;
            case NodeDeleted:
                //Node deletion needs to clear data and wait for data to arrive
                myConf.setConfData("");
                latch = new CountDownLatch(1);
                break;
            case NodeDataChanged:
                //Data modification
                zooKeeper.getData("/AppConf",this,this,"bbb");
                break;
            case NodeChildrenChanged:
                break;
            case DataWatchRemoved:
                break;
            case ChildWatchRemoved:
                break;
        }
    }
    // DataCallback
    @Override
    public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) {
        if(stat!=null){
            myConf.setConfData(new String(data));
            latch.countDown();
        }
    }

    public void aWait() throws InterruptedException {
        //Here / appconf is actually / test / appconf in zookeeper,
        zooKeeper.exists("/AppConf",this,this,"123");
        latch.await();
    }
}
Myconf class:
package org.qzx.config;

/**
 * @Auther: qzx
 *@ date: 2020 / 10 / 29 - 10 - 29 - 2:00 pm
 * @Description: org.qzx.config
 * @version: 1.0
 */
public class MyConf {
    private String confData;

    public String getConfData() {
        return confData;
    }

    public void setConfData(String confData) {
        this.confData = confData;
    }
}
Testconfig class:
package org.qzx.config;

import org.apache.zookeeper.ZooKeeper;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.util.concurrent.TimeUnit;
import java.util.zip.ZipOutputStream;


/**
 * @Auther: qzx
 *@ date: 2020 / 10 / 29 - 10 - 29 - 1:29 PM
 * @Description: org.qzx.config
 * @version: 1.0
 */
public class TestConfig {
    private ZooKeeper zooKeeper;
    private final MyWatcherAndCallBack watcherAndCallBack = new MyWatcherAndCallBack();
    //Accept data
    MyConf myConf = new MyConf();

    @Before
    public void conn() throws Exception {
        zooKeeper = Utils.getZooKeeper();
    }

    @After
    public void close() throws InterruptedException {
        zooKeeper.close();
    }

    @Test
    public void getConf() throws InterruptedException {
        watcherAndCallBack.setZooKeeper(zooKeeper);
        watcherAndCallBack.setMyConf(myConf);
        watcherAndCallBack.aWait();
        while (true){
            if(myConf.getConfData().equals("")){
                System. Out. Println ("data is empty");
                watcherAndCallBack.aWait();//  Waiting for data to arrive
            }
            System.out.println(myConf.getConfData());
            TimeUnit.SECONDS.sleep(2);
        }
    }
}

Write at the end

Some people may think that this article is redundant (especially the code part) and too simple, but I just want to record the thinking process and complete code writing process of responsive programming completely, which can be used for my review and provide a small demo of zookeeper responsive programming for Xiaobai.