Use the package officially provided by Zookeeper for zk server connection and circular monitoring

1. Code examples and explain running phenomena

1.1. Introduce the corresponding jar package

<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.7.0</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.8.3</version>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.12</version>
</dependency>

1.2 main category

The main function of this java class is to connect zk server and create a node, add a listener, and then modify the data, get the response from the server, and get the data again to add a listener.
1. Initialize the connection zk server.
2. Create a zk node.
3. Initialize a listener and add listeners to the node just created.
4. Implement the process method executed when the listener is triggered, which is to add listening to the specified node again.

public class ConfigCenter {
    private final static String CONNECT_STR = "192.168.231.131:2181";

    private final static Integer SESSION_TIMEOUT = 30 * 1000 ;

    private static ZooKeeper zooKeeper = null;

    /**
     * Because there are two daemon threads when zk is started, and the main function is a business thread. When there is no business thread running, the daemon thread will launch itself
     * Therefore, define a CountDownLatch thread, let the main thread not end first, and let it continue to execute after zk startup is successful
     */
    private static CountDownLatch countDownLatch = new CountDownLatch(1);

    public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
        // Connect a zk client and create a listener Watcher to listen
        zooKeeper = new ZooKeeper(CONNECT_STR, SESSION_TIMEOUT, new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {
                // Determine whether zk's client has been started
                if(watchedEvent.getType() == Event.EventType.None &&
                watchedEvent.getState() == Event.KeeperState.SyncConnected){
                    System.out.println("Connection established");
                    // This will unlock the thread and wait to continue down
                    countDownLatch.countDown();
                }
            }
        });
        // Because this zk client uses a daemon thread to prevent the main thread from being too fast, but the daemon thread fails
        // Here, the main thread waits until countDownLatch.countDown()
        countDownLatch.await();


        // After connecting, you can add, delete, query and modify.
        MyConfig myConfig = new MyConfig();
        myConfig.setKey("anyKey");
        myConfig.setName("anyName");

        // ObjectMapper is a tool for serializing strings
        final ObjectMapper objectMapper = new ObjectMapper();
        // Write the string into a corresponding binary file
        byte[] bytes = objectMapper.writeValueAsBytes(myConfig);

        // ZooDefs.Ids.OPEN_ACL_UNSAFE set all permissions
        // CreateMode.PERSISTENT creates a persistent node
        String str = zooKeeper.create("/myConfig",bytes, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);

        // Here, create an instance of a listener. Next, add a listener to the specified node
        // After listening is added, the following process method will be executed if there is a specific movement in the specified node
        Watcher watcher = new Watcher() {
            @Override
            @SneakyThrows
            public void process(WatchedEvent watchedEvent) {
                if(watchedEvent.getType() == Event.EventType.NodeDataChanged &&
                    watchedEvent.getPath() != null && watchedEvent.getPath().equals("/myConfig")){
                    System.out.println("PATH:" + watchedEvent.getPath() + "Has changed.");

                    // Add listening to the specified node again and obtain data
                    byte[] data = zooKeeper.getData("/myConfig", this, null);
                    // Deserialize the data because the data obtained from the server is a byte stream
                    MyConfig newConfig = objectMapper.readValue(new String(data), MyConfig.class);
                    // Print data
                    System.out.println("Data has changed, new data:" + newConfig);

                }
            }
        };

        // Add listening and obtain data for the specified node for the first time
        byte[] data = zooKeeper.getData("/myConfig", watcher, null);
        // Deserialize the data because the data obtained from the server is a byte stream
        MyConfig newConfig = objectMapper.readValue(new String(data), MyConfig.class);
        // Print data
        System.out.println("Raw data" + newConfig);

        // The main thread cannot end. The purpose of waiting here is to connect the two daemon threads on the server
        TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);

    }
}

1.3 entity class

This class is an entity class encapsulation to write data to the server

public class MyConfig {
    private String key;
    private String name;

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

1.4 operation phenomenon

When we execute the ConfigCenter class, the console will show that the connection has been established and print the original data. Then we go to the server to execute the modification command, as shown in the following figure:

Then the console will respond, indicating that it has received the notification from the server, as shown in the following figure:

2. Auxiliary knowledge

2.1 why do we need CountDownLatch to stop the main thread when connecting zk server?

Because we are the two daemon threads started when connecting to the server, we can click the ZooKeeper of new ZooKeeper in the ConfigCenter class, and then click this all the time. Finally, the following code appears:

2.1.1 ZooKeeper() zk client initialization method

public ZooKeeper(String connectString, int sessionTimeout, Watcher watcher, boolean canBeReadOnly, HostProvider hostProvider, ZKClientConfig clientConfig) throws IOException {
    LOG.info("Initiating client connection, connectString={} sessionTimeout={} watcher={}", new Object[]{connectString, sessionTimeout, watcher});
    this.clientConfig = clientConfig != null ? clientConfig : new ZKClientConfig();
    this.hostProvider = hostProvider;
    ConnectStringParser connectStringParser = new ConnectStringParser(connectString);
    // Create a connection here and start below
    this.cnxn = this.createConnection(connectStringParser.getChrootPath(), hostProvider, sessionTimeout, this.clientConfig, watcher, this.getClientCnxnSocket(), canBeReadOnly);
    // Start the connection, click the start() method,
    this.cnxn.start();
}

2.1.2 this.cnxn.start() starts the two core threads communicating with the server

The main function of this method is to start the two core threads communicating with the server, as shown in the following code:

public void start() {
	// Let's click sendThread and see its constructor
    this.sendThread.start();
    // Let's click on eventThread and see its constructor
    this.eventThread.start();
}

2.1.3,SendThread()

The main function of this thread is to send data from zk client to server. Go through 2.1.5

SendThread(ClientCnxnSocket clientCnxnSocket) {
    super(ClientCnxn.makeThreadName("-SendThread()"));
    ClientCnxn.this.state = States.CONNECTING;
    this.clientCnxnSocket = clientCnxnSocket;
    this.setDaemon(true);
}

2.1.4,EventThread()

The main function of this thread is to receive the data responded by the server. Go to 2.1.5

EventThread() {
    super(ClientCnxn.makeThreadName("-EventThread"));
    this.sessionState = KeeperState.Disconnected;
    this.wasKilled = false;
    this.isRunning = false;
    this.setDaemon(true);
}

2.1.5 explanation

this.setDaemon(true);

The above line of code is to set the current thread as the daemon thread
Definition of daemon thread: daemon thread - also known as "service thread", will leave automatically when there is no user thread to serve

Tags: Zookeeper Middleware

Posted on Mon, 29 Nov 2021 13:00:44 -0500 by Liquidedust