The most detailed tutorial in history is about the use of Zookeeper client, ZkClient and cursor~

Keywords: Zookeeper Session Java Apache

If you think this article is helpful to you, welcome old fellow to praise you. Your support is the biggest driving force for my creation.

This series mainly summarizes the basic use of Zookeeper. The author is going to write four articles:

Blog content Resource links
Setting up Zookeeper running environment under Linux https://blog.csdn.net/smilehappiness/article/details/105933433
Introduction to Zookeeper, one article is enough https://blog.csdn.net/smilehappiness/article/details/105933292
The most detailed tutorial in history is about the use of Zookeeper client, ZkClient and cursor~ https://blog.csdn.net/smilehappiness/article/details/105938058
Zookeeper Usage Summary (Advanced) https://blog.csdn.net/smilehappiness/article/details/105938119

Article catalog

1 foreword

Above Introduction to Zookeeper, one article is enough , introduced the basic introduction of zookeeper, can quickly understand zk, also introduced the use of command-line client, can operate zookeeper to create, delete, query, modify the znode node, in the actual project, will definitely not play like this, will definitely use some clients to operate the znode node.

This article is also an important article in this series. It mainly introduces the basic use of several clients for Zookeeper operation, hoping to help the old fellow engineers. The next article will introduce some application scenarios of Zookeeper. Please look forward to it.

2 Zookeeper server client classification

Currently, Zookeeper server has three Java clients: Zookeeper, Zkclient and cursor

  • Zookeeper: zookeeper is the official native java client
  • Zkclient: an open-source third-party Java client extended on the basis of the native zookeeper client
  • Curator: Netflix's open-source third-party Java client based on the native zookeeper client

Next, we will introduce the use of these three kinds of clients step by step.

3. Use of java client Zookeeper (official)

Use the official client tool Zookeeper to operate the Zookeeper node.
Official documents: https://zookeeper.apache.org/doc/r3.6.1/apidocs/zookeeper-server/index.html

The client is officially brought by zookeeper. Programming is not so convenient:

  • When the session timeout is abnormal, automatic reconnection is not supported, manual reconnection is required, and programming is tedious
  • watcher is one-time, and it will be invalid after registration
  • The node data is binary, and the object data needs to be converted to binary for saving
  • Recursive node creation is not supported. You need to create a parent node before creating a child node
  • Recursive node deletion is not supported. You need to delete the child node first and then the parent node
  • The establishment of the client-side and server-side sessions of the native zookeeper is an asynchronous process, that is to say, in the program, our program method returns immediately after processing the client-side initialization (the program executes the code downward, so in most cases, we do not really build a usable session, and only when the declaration cycle of the session is "CONNECTED" can we really establish it , so we need to use CountDownLatch, a tool class in multithreading, to control. After connecting to the zk client, we can continue to operate the zNode node.)

3.1 Zookeeper (official) main API

Zookeeper (official) main APIs are as follows:

method explain
create(String path, byte[] data, List acl, CreateMode createMode) Create node and assign data
getData(String path, boolean watch, Stat stat) Get node data
getChildren(String path, boolean watch) Get the data of child nodes under the current node
setData(String path, byte[] data, int version) Modify the data of a node
delete(String path, int version) Delete a node (Note: child nodes are not required)
exists(String path, boolean watch) Judge whether a node exists

3.2 add jar package dependency

To use the Java client zookeeper, first add the official client jar package dependency of zookeeper:

<!--zookeeper The official client of jar Package dependency-->
<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.5.5</version>
</dependency>

3.3 use of client Zookeeper

3.3.1 create a zookeeper connection

The code example is as follows:

	/**
     * <p>
     * Initialize zookeeper and create zookeeper client object
     * <p/>
     *
     * @param connectAddress
     * @param sessionTimeout
     * @return void
     * @Date 2020/6/20 13:22
     */
    private static void initConnect(String connectAddress, int sessionTimeout) {
        try {
            //Create a zookeeper client object
            //zookeeper = new ZooKeeper(connectAddress, sessionTimeout, null);
            //In the above way, because the zookeeper connection is asynchronous, if the new ZooKeeper(connectStr, sessionTimeout, null) is used immediately after completion, an error may be reported.

            //Solution: add a watcher listening event. If SyncConnected is selected, other operations can be performed. (this is controlled by CountDownLatch countdown)
            zookeeper = new ZooKeeper(connectAddress, sessionTimeout, watchedEvent -> {
                //Get the status of the listening event
                Watcher.Event.KeeperState state = watchedEvent.getState();

                //Get listening event type
                Watcher.Event.EventType type = watchedEvent.getType();

                //If a connection has been established
                if (Watcher.Event.KeeperState.SyncConnected == state) {
                    if (Watcher.Event.EventType.None == type) {
                        System.out.println("zookeeper Connection successful......");
                        countDownLatch.countDown();
                    }
                }

                if (Watcher.Event.EventType.NodeCreated == type) {
                    System.out.println("zookeeper There are new nodes[" + watchedEvent.getPath() + "]establish!");
                }
                if (Watcher.Event.EventType.NodeDataChanged == type) {
                    System.out.println("zookeeper With nodes[" + watchedEvent.getPath() + "]Data changes!");
                }
                if (Watcher.Event.EventType.NodeDeleted == type) {
                    System.out.println("zookeeper With nodes[" + watchedEvent.getPath() + "]Deleted!");
                }
                if (Watcher.Event.EventType.NodeChildrenChanged == type) {
                    System.out.println("zookeeper Has child nodes[" + watchedEvent.getPath() + "]change!");
                }
            });

            //The countdown counter is not completed. You cannot execute the following code because you need to wait for zookeeper to connect before you can operate node. Otherwise, an error may be reported
            countDownLatch.await();

            System.out.println("init connect success: " + zookeeper);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

3.3.2 creating a zNode node

The code example is as follows:

	/**
     * <p>
     * According to the specified path, create the zNode node and assign data
     * <p/>
     *
     * @param nodePath
     * @param data
     * @return void
     * @Date 2020/6/20 13:58
     */
    private static void createNode(String nodePath, String data) throws KeeperException, InterruptedException {
        if (StringUtils.isEmpty(nodePath)) {
            System.out.println("Node[" + nodePath + "]Cannot be empty");
            return;
        }

        //Judge whether the node exists or not, otherwise an error will be reported: [nodeexistsexception: keeperrorcode = nodeexists for / root]
        Stat exists = zookeeper.exists(nodePath, true);
        if (null != exists) {
            System.out.println("Node[" + nodePath + "]Existing, cannot add");
            return;
        }

        String result = zookeeper.create(nodePath, data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        System.out.println("create:" + "[" + nodePath + "-->" + data + "],result:" + result);
    }

    /**
     * <p>
     * According to the specified path, recursively create the zNode node and assign data
     * <p/>
     *
     * @param nodePath
     * @param data
     * @return void
     * @Date 2020/6/20 14:28
     */
    private static void createNodeRecursion(String nodePath, String data) throws KeeperException, InterruptedException {
        if (StringUtils.isEmpty(nodePath)) {
            System.out.println("Node[" + nodePath + "]Cannot be empty");
            return;
        }

        String paths[] = nodePath.substring(1).split("/");
        for (int i = 0; i < paths.length; i++) {
            String childPath = "";
            for (int j = 0; j <= i; j++) {
                childPath += "/" + paths[j];
            }
            createNode(childPath, data);
        }
    }

3.3.3 querying the zNode node

The code example is as follows:

	/**
     * <p>
     * Query node
     * <p/>
     *
     * @param nodePath
     * @return void
     * @Date 2020/6/20 15:12
     */
    private static void queryNode(String nodePath) throws KeeperException, InterruptedException {
        System.out.println("--------------------Gorgeous dividing line-------------------------");

        byte[] bytes = zookeeper.getData(nodePath, false, null);
        System.out.println(new String(bytes));

        Stat stat = new Stat();
        byte[] data = zookeeper.getData(nodePath, true, stat);
        System.out.println("queryNode:" + "[" + nodePath + "],result: " + new String(data) + ",stat: " + stat);
    }

3.3.4 update zNode node

The code example is as follows:

 	/**
     * <p>
     * Update data for the specified node
     * <p/>
     *
     * @param nodePath
     * @param data
     * @return void
     * @Date 2020/6/20 16:01
     */
    private static void updateNodeData(String nodePath, String data) throws KeeperException, InterruptedException {
        //version = -1 means no version specified
        Stat stat = zookeeper.setData(nodePath, data.getBytes(), -1);
        System.out.println("setData:" + "[" + nodePath + "],stat:" + stat);
    }

3.3.5 delete zNode

The code example is as follows:

	/**
     * <p>
     * Delete a node according to a node
     * <p/>
     *
     * @param nodePath
     * @return void
     * @Date 2020/6/20 15:28
     */
    private static void deleteNode(String nodePath) throws KeeperException, InterruptedException {
        System.out.println("--------------------Gorgeous dividing line-------------------------");

        Stat exists = zookeeper.exists(nodePath, true);
        if (null == exists) {
            System.out.println(nodePath + "No, please check and carry out relevant operation!");
            return;
        }

        zookeeper.delete(nodePath, -1);//Version: - 1 means no version is specified when deleting a node
        System.out.println("delete node:" + "[" + nodePath + "]");
    }

    /**
     * <p>
     * According to a path, delete the node recursively (this method will delete the parent node)
     * <p/>
     *
     * @param nodePath
     * @return void
     * @Date 2020/6/20 15:29
     */
    private static void deleteRecursion(String nodePath) throws KeeperException, InterruptedException {
        System.out.println("--------------------Gorgeous dividing line-------------------------");

        Stat exists = zookeeper.exists(nodePath, true);
        if (null == exists) {
            System.out.println(nodePath + "No, please check and carry out relevant operation!");
            return;
        }

        //Get the data of child nodes under the current nodePath
        List<String> list = zookeeper.getChildren(nodePath, true);
        if (CollectionUtils.isEmpty(list)) {
            deleteNode(nodePath);

            String parentPath = nodePath.substring(0, nodePath.lastIndexOf("/"));
            System.out.println("parentPath=" + parentPath);
            //If the current node has a parent node, delete the parent node and all child nodes under the parent node
            if (StringUtils.isNotBlank(parentPath)) {
                deleteRecursion(parentPath);
            }
        } else {
            for (String child : list) {
                deleteRecursion(nodePath + "/" + child);
            }
        }
    }

3.3.6 use summary of zookeeper basic api

For basic addition, deletion, modification and query of zk node, the complete code example is as follows:

/**
 * <p>
 * Native zookeeper client (official)
 * 1.The connection is asynchronous. When using, you need to pay attention to adding a watcher. If the listening event is SyncConnected, you can do other operations. (you can use CountDownLatch or other fence controls -- Concurrent Programming)
 * 2.The listening event is one-time. If the operation needs to be registered more than once
 * <p>
 * api: https://zookeeper.apache.org/doc/r3.6.1/apidocs/zookeeper-server/index.html
 * <p/>
 *
 * @author smilehappiness
 * @Date 2020/6/20 16:09
 */
public class ZookeeperClient01 {

    /**
     * Client connection address
     */
    private static final String ZK_ADDRESS = "ip:2181";
    /**
     * Client root node
     */
    private static final String ROOT_NODE = "/root";
    /**
     * Customer terminal node
     */
    private static final String ROOT_NODE_CHILDREN = "/root/user";
    /**
     * Countdown counter, one from the bottom
     */
    private static CountDownLatch countDownLatch = new CountDownLatch(1);
    /**
     * ZooKeeper object
     */
    private static ZooKeeper zookeeper = null;

    /**
     * <p>
     * zookeeper Client use
     * <p>
     * Note: do not use debug for breakpoint testing, otherwise errors may be reported (for example: org.apache.zookeeper.KeeperException$ConnectionLossException: KeeperErrorCode = ConnectionLoss for /root)
     * <p/>
     *
     * @param args
     * @return void
     * @Date 2020/6/20 15:42
     */
    public static void main(String[] args) throws Exception {

        //1. Initialize zookeeper and create zookeeper client object
        initConnect(ZK_ADDRESS, 5000);

        //2. Create node
        createNode(ROOT_NODE, "root data1");
        createNode(ROOT_NODE + "/home", "home data1");

        //Recursively create nodes (recursively assign the same value to each node, which is not used in many scenarios)
        createNodeRecursion(ROOT_NODE_CHILDREN, "recursion data1");

        //3. Query node
        queryNode(ROOT_NODE);

        //4. Modify node
        updateNodeData(ROOT_NODE, "nice");

        //5. Single node deletion (Note: if there is a child node under the node, you cannot delete -- NotEmptyException: KeeperErrorCode = Directory not empty for /root)
        //There is no way to directly delete the current node and child nodes in the api of the zookeeper client
        //deleteNode(ROOT_NODE);
        //Recursively delete nodes
        deleteRecursion(ROOT_NODE_CHILDREN);
    }

    /**
     * <p>
     * Initialize zookeeper and create zookeeper client object
     * <p/>
     *
     * @param connectAddress
     * @param sessionTimeout
     * @return void
     * @Date 2020/6/20 13:22
     */
    private static void initConnect(String connectAddress, int sessionTimeout) {
        try {
            //Create a zookeeper client object
            //zookeeper = new ZooKeeper(connectAddress, sessionTimeout, null);
            //In the above way, because the zookeeper connection is asynchronous, if the new ZooKeeper(connectStr, sessionTimeout, null) is used immediately after completion, an error may be reported.

            //Solution: add a watcher listening event. If SyncConnected is selected, other operations can be performed. (this is controlled by CountDownLatch countdown)
            zookeeper = new ZooKeeper(connectAddress, sessionTimeout, watchedEvent -> {
                //Get the status of the listening event
                Watcher.Event.KeeperState state = watchedEvent.getState();

                //Get listening event type
                Watcher.Event.EventType type = watchedEvent.getType();

                //If a connection has been established
                if (Watcher.Event.KeeperState.SyncConnected == state) {
                    if (Watcher.Event.EventType.None == type) {
                        System.out.println("zookeeper Connection successful......");
                        countDownLatch.countDown();
                    }
                }

                if (Watcher.Event.EventType.NodeCreated == type) {
                    System.out.println("zookeeper There are new nodes[" + watchedEvent.getPath() + "]establish!");
                }
                if (Watcher.Event.EventType.NodeDataChanged == type) {
                    System.out.println("zookeeper With nodes[" + watchedEvent.getPath() + "]Data changes!");
                }
                if (Watcher.Event.EventType.NodeDeleted == type) {
                    System.out.println("zookeeper With nodes[" + watchedEvent.getPath() + "]Deleted!");
                }
                if (Watcher.Event.EventType.NodeChildrenChanged == type) {
                    System.out.println("zookeeper Has child nodes[" + watchedEvent.getPath() + "]change!");
                }
            });

            //The countdown counter is not completed. You cannot execute the following code because you need to wait for zookeeper to connect before you can operate node. Otherwise, an error may be reported
            countDownLatch.await();

            System.out.println("init connect success: " + zookeeper);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * <p>
     * According to the specified path, create the zNode node and assign data
     * <p/>
     *
     * @param nodePath
     * @param data
     * @return void
     * @Date 2020/6/20 13:58
     */
    private static void createNode(String nodePath, String data) throws KeeperException, InterruptedException {
        if (StringUtils.isEmpty(nodePath)) {
            System.out.println("Node[" + nodePath + "]Cannot be empty");
            return;
        }

        //Judge whether the node exists or not, otherwise an error will be reported: [nodeexistsexception: keeperrorcode = nodeexists for / root]
        Stat exists = zookeeper.exists(nodePath, true);
        if (null != exists) {
            System.out.println("Node[" + nodePath + "]Existing, cannot add");
            return;
        }

        String result = zookeeper.create(nodePath, data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        System.out.println("create:" + "[" + nodePath + "-->" + data + "],result:" + result);
    }

    /**
     * <p>
     * According to the specified path, recursively create the zNode node and assign data
     * <p/>
     *
     * @param nodePath
     * @param data
     * @return void
     * @Date 2020/6/20 14:28
     */
    private static void createNodeRecursion(String nodePath, String data) throws KeeperException, InterruptedException {
        if (StringUtils.isEmpty(nodePath)) {
            System.out.println("Node[" + nodePath + "]Cannot be empty");
            return;
        }

        String paths[] = nodePath.substring(1).split("/");
        for (int i = 0; i < paths.length; i++) {
            String childPath = "";
            for (int j = 0; j <= i; j++) {
                childPath += "/" + paths[j];
            }
            createNode(childPath, data);
        }
    }

    /**
     * <p>
     * Query node
     * <p/>
     *
     * @param nodePath
     * @return void
     * @Date 2020/6/20 15:12
     */
    private static void queryNode(String nodePath) throws KeeperException, InterruptedException {
        System.out.println("--------------------Gorgeous dividing line-------------------------");

        byte[] bytes = zookeeper.getData(nodePath, false, null);
        System.out.println(new String(bytes));

        Stat stat = new Stat();
        byte[] data = zookeeper.getData(nodePath, true, stat);
        System.out.println("queryNode:" + "[" + nodePath + "],result: " + new String(data) + ",stat: " + stat);
    }

    /**
     * <p>
     * Update data for the specified node
     * <p/>
     *
     * @param nodePath
     * @param data
     * @return void
     * @Date 2020/6/20 16:01
     */
    private static void updateNodeData(String nodePath, String data) throws KeeperException, InterruptedException {
        //version = -1 means no version specified
        Stat stat = zookeeper.setData(nodePath, data.getBytes(), -1);
        System.out.println("setData:" + "[" + nodePath + "],stat:" + stat);
    }

    /**
     * <p>
     * Delete a node according to a node
     * <p/>
     *
     * @param nodePath
     * @return void
     * @Date 2020/6/20 15:28
     */
    private static void deleteNode(String nodePath) throws KeeperException, InterruptedException {
        System.out.println("--------------------Gorgeous dividing line-------------------------");

        Stat exists = zookeeper.exists(nodePath, true);
        if (null == exists) {
            System.out.println(nodePath + "No, please check and carry out relevant operation!");
            return;
        }

        zookeeper.delete(nodePath, -1);//Version: - 1 means no version is specified when deleting a node
        System.out.println("delete node:" + "[" + nodePath + "]");
    }

    /**
     * <p>
     * According to a path, delete the node recursively (this method will delete the parent node)
     * <p/>
     *
     * @param nodePath
     * @return void
     * @Date 2020/6/20 15:29
     */
    private static void deleteRecursion(String nodePath) throws KeeperException, InterruptedException {
        System.out.println("--------------------Gorgeous dividing line-------------------------");

        Stat exists = zookeeper.exists(nodePath, true);
        if (null == exists) {
            System.out.println(nodePath + "No, please check and carry out relevant operation!");
            return;
        }

        //Get the data of child nodes under the current nodePath
        List<String> list = zookeeper.getChildren(nodePath, true);
        if (CollectionUtils.isEmpty(list)) {
            deleteNode(nodePath);

            String parentPath = nodePath.substring(0, nodePath.lastIndexOf("/"));
            System.out.println("parentPath=" + parentPath);
            //If the current node has a parent node, delete the parent node and all child nodes under the parent node
            if (StringUtils.isNotBlank(parentPath)) {
                deleteRecursion(parentPath);
            }
        } else {
            for (String child : list) {
                deleteRecursion(nodePath + "/" + child);
            }
        }
    }

}

4. Use of Zkclient in Java client

Zkclient is an open-source third-party Java client extended on the basis of native zookeeper. Compared with native client, it is too convenient to use.
Github source code: https://github.com/sgroschupf/zkclient

4.1 problems with the official zookeeper client

There are problems with the official zookeeper client itself:

  • When the session timeout is abnormal, automatic reconnection is not supported, manual reconnection is required, and programming is cumbersome (if the network is unstable in the production environment, this situation is more obvious)
  • ZooKeeper's watcher monitoring is one-time, and will fail after registration
  • The node data is binary, and the object data needs to be converted to binary for saving
  • Recursive node creation is not supported. You need to create a parent node before creating a child node
  • Recursive node deletion is not supported. You need to delete the child node first and then the parent node
  • There is no leadership election mechanism. In the case of a cluster, you may need to implement stand by. One service is suspended, and the other needs to be replaced
  • The client only provides an interface for storing byte arrays, while objects are usually used in projects
  • The client interface has too many exceptions to handle, and often we don't know how to handle them
  • The establishment of the client-side and server-side sessions of the native zookeeper is an asynchronous process, that is to say, in the program, our program method returns immediately after processing the client-side initialization (the program executes the code downward, so in most cases, we do not really build a usable session, and only when the declaration cycle of the session is "CONNECTED" can we really establish it , so we need to use CountDownLatch, a tool class in multithreading, to control. After connecting to the zk client, we can continue to operate the zNode node.)

4.2 why to use ZkClient client

ZkClient is packaged on the native Zookeeper API interface, which is easier to use. ZkClient also implements Session timeout reconnection, Watcher registration and other functions, which makes these operations transparent to development and improves development efficiency.

ZkClient mainly solves two major problems of native api:

  • ZkClient solves the problem of one-time registration of the watcher. The event of znode is redefined as the change of child node, the change of data and the change of connection state. ZkClient converts the WatchedEvent of the watcher to the above three cases to process. After the watcher is executed, it reads the data again and registers a new same watcher.

    Zkclient transforms Zookeeper's watcher mechanism into a more understandable form of subscription, and this relationship can be maintained rather than one-time. That is to say, the change of child nodes, data and state can be subscribed. When the watcher is used up, zkclient will automatically add the same watcher.

  • Automatically create a new ZooKeeper instance for reconnection during session loss and session expire

The zookeeper client of 101tec mainly has the following features:

  • It provides the reconnection feature of zookeeper, which can reestablish the connection when the chain is broken, no matter whether the session fails or not
  • The persistent event listener mechanism -- the ZKClient framework divides the event redefinition into three situations: stateChanged, znodeChanged, and dataChanged. Users can register the listener in these three situations (znodeChanged and dataChanged are related to the path), rather than register the Watcher.
  • Zookeeper Exception handling - there are many exceptions in zookeeper, and each Exception needs different attention. I0Itec simply encapsulates them.
  • Data serialization -- simple data serialization. (serializer / deserialzer)
  • There is a default leadership election mechanism

Note: when using 101tec zkclient, there is a method that needs to be improved. create() method is used to create a node. If the node already exists, NodeExistException is still thrown. You need to manually determine whether a node exists. This step can be optimized.

4.3 add ZkClient client dependency

To use Zkclient, you need to rely on jar package. Use the following method to add maven dependency
Add the following dependencies:

<!--zkclient Client's jar Package dependency-->
<dependency>
   <groupId>com.101tec</groupId>
   <artifactId>zkclient</artifactId>
   <version>0.11</version>
</dependency>

4.4 use of common APIs of ZkClient

4.4.1 create ZkClient connection object

There are several ways:

public ZkClient(String serverstring)
public ZkClient(String zkServers, int connectionTimeout)
public ZkClient(String zkServers, int sessionTimeout, int connectionTimeout)
public ZkClient(String zkServers, int sessionTimeout, int connectionTimeout, ZkSerializer zkSerializer)
public ZkClient(IZkConnection connection)
public ZkClient(IZkConnection connection, int connectionTimeout)
public ZkClient(IZkConnection zkConnection, int connectionTimeout, ZkSerializer zkSerializer)

Method parameter description:

  • zkServers: the list of Zookeeper's servers (such as ip:2181), is a host:port String, if more than one, divided by
  • sessionTimeout: session timeout in ms
  • connectionTimeout: connection timeout, in ms. If the connection with Zookeeper has not been established successfully within this time, the connection will be abandoned and an exception will be thrown
  • connection: the implementation class of the IZkConnection interface. IZkConnection is the most direct wrapper for the Zookeeper native interface. There are two main types of IZkConnection: ZkConnection and InMemoryConnection. The most commonly used one is ZkConnection
  • zkSerializer: Custom serializer. Zookeeper supports byte [] type data, so developers need to define their own serializers to allow users to pass in a custom serialization implementation. If not, Java's own serialization method is used for serialization and deserialization by default. The default serialization data format is not very friendly, so it is recommended to use a custom serializer.

4.4.2 creating a zNode node

4.4.2.1 create a zNode node

There are several ways:

public String create(final String path, Object data, final CreateMode mode) throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException

public void createEphemeral(final String path) throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException

public void createEphemeral(final String path, final Object data) throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException

public String createEphemeralSequential(final String path, final Object data) throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException

public void createPersistent(String path) throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException

public void createPersistent(String path, boolean createParents) throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException

public void createPersistent(String path, Object data) throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException

public String createPersistentSequential(String path, Object data) throws ZkInterruptedException, IllegalArgumentException, ZkException, RuntimeException

In the above methods, the first method, create(), can create a node just like the native method. createPersistent(), which encapsulates the native api, makes node creation easier. createPersistentSequential() is also a encapsulation of the native api, allowing the creation of a persistent node with a serial number.

Method parameter description:

  • Path: created node path
  • data: when creating a node, the assigned content
  • createParents: whether to create a parent node if the parent node does not exist

4.4.2.2 create node status monitoring

public void subscribeStateChanges(final IZkStateListener listener)

public interface IZkStateListener {
    // Callback when session state changes
    public void handleStateChanged(KeeperState state) throws Exception;
    // Callback when a session expires and a new session is recreated
    public void handleNewSession() throws Exception;
}

Register a Watcher to listen for the state change of the current client session. If the client session state changes, the method in IZkStateListener will be called back.

4.4.3 get node list

4.4.3.1 obtaining node content

The main api methods are as follows:

// Judge whether the node exists. Generally, if there is no node, create the node. Otherwise, an error will be reported
public boolean exists(final String path)
public <T extends Object> T readData(String path)
public <T extends Object> T readData(String path, boolean returnNullIfPathNotExists)
public <T extends Object> T readData(String path, Stat stat)
// Cannot call if not under the same package
protected <T extends Object> T readData(final String path, final Stat stat, final boolean watch)
public void subscribeDataChanges(String path, IZkDataListener listener)

Parameter Description:

  • path: Specifies the node to read
  • returnNullIfPathNotExists: literally, if the node does not exist, return null instead of throwing an exception
  • Stat: Specifies the node status information of the data node, which will be replaced by the new stat responded by the server during the interaction

Listening method subscribeDataChanges() Description:

public interface IZkDataListener {
    public void handleDataChange(String dataPath, Object data) throws Exception;
    public void handleDataDeleted(String dataPath) throws Exception;
}

Parameter Description:
dataPath: full path name of the changed node
data: new content of node

The last method, subscribeDataChanges(), is equivalent to registering a Watcher listener, which mainly notifies the client when the following events occur:

  • When the node content changes, the handleDataChange() method will be called back
  • When the node is deleted, the handleDataDeleted() method will be called back

4.4.3.2 get the list of child nodes

The common api methods are as follows:

// Get children of current node
public List<String> getChildren(String path)
// Get the number of children of the current node
public int countChildren(String path)
// Cannot be called without the same package, watch: whether to use the default Watcher
protected List<String> getChildren(final String path, final boolean watch)
// Subscribing to changes in children of the current node
public List<String> subscribeChildChanges(String path, IZkChildListener listener)

Listening method subscribeChildChanges() Description:
The last method, subscribeChildChanges(), is equivalent to registering a Watcher listener. The difference between this method and registering a Watcher is that when IZkChildListener is registered, the node can either exist or listen, and one registration is always valid.

public interface IZkChildListener {
    public void handleChildChange(String parentPath, List<String> currentChilds) throws Exception;
}

IZkChildListener mainly notifies the client when the following events occur:

  • When the path node is added, parentPath is the node name and currentfolders is the empty collection
  • When the path node is deleted, parentPath is the node name and currentchildren is null
  • When a path node has children created, parentPath is the name of the node, and currentfolders is the name (relative path) list of the current child node
  • When a path node has child nodes that are deleted, parentPath is the name of the node, and currentfolders is the name (relative path) list of the current child nodes. It may be an empty collection (when all child nodes are deleted)

4.4.4 setting and modifying node content

There are mainly the following methods to modify node contents:

// Set content for nodes
public void writeData(String path, Object data)
// Set content for a version of the node
public void writeData(final String path, Object data, final int expectedVersion)
//This method is more flexible to set content. You can use dataupdater < T > to set content
public <T> void updateDataSerialized(String path, DataUpdater<T> updater)

4.4.5 delete the zNode node

There are two methods:

public boolean delete(final String path)
//Delete node according to node path, version number
public boolean delete(final String path, final int version)
//Recursively delete the current node and its children
public boolean deleteRecursive(String path)

The deleteRecursive() method supports deleting the child nodes of the current node, that is, recursive deletion

4.4.6 summary of zkclient use

The above introduces the basic api of ZkClient, and the following is the complete code of the test.

The complete code example is as follows:

/**
 * <p>
 * Zookeeper Basic use of client ZkClient
 * <p/>
 *
 * @author smilehappiness
 * @Date 2020/6/20 16:56
 */
public class ZookeeperClient02 {

    /**
     * Client connection address
     */
    private static final String ZK_ADDRESS = "ip:2181";
    /**
     * Client root node
     */
    private static final String ROOT_NODE = "/root";
    /**
     * Customer terminal node
     */
    private static final String ROOT_NODE_CHILDREN = "/root/children/node";

    public static void main(String[] args) {
        //1. Establish a zookeeper connection
        //ZkClient zkClient = new ZkClient(ZK_ADDRESS, 5000, 15000);
        //zkClient supports defining its own serialization format (the data format saved by zkClient is not good-looking, and you can customize the format)
        ZkClient zkClient = new ZkClient(ZK_ADDRESS, 5000, 15000, new MyZkSerializer());

        //2. Create a persistent node
        //zkClient.createPersistent(ROOT_NODE);
        //Judge whether the node exists
        if (!zkClient.exists(ROOT_NODE)) {
            //Create persistent nodes and assign values
            zkClient.createPersistent(ROOT_NODE, "hello");

            //Create persistent and orderly nodes (e.g. sequential 0000000000, which can be used for primary key generator in distributed environment) and assign values
            zkClient.createPersistentSequential(ROOT_NODE + "/sequential", "sequential data");

            //zkClient also supports calling native methods to create nodes
            String node = zkClient.create(ROOT_NODE + "/home", "zkclient data", CreateMode.PERSISTENT);
            System.out.println(node);
        }

        //Create a persistent node. If the parent node does not exist, create the parent node
        zkClient.createPersistent(ROOT_NODE_CHILDREN, true);

        //3. Query node
        //Read node data
        Object object = zkClient.readData(ROOT_NODE);
        System.out.println(object);

        //Get children of current node
        List<String> children = zkClient.getChildren(ROOT_NODE);
        children.forEach(item -> {
            System.out.println(item);
        });
        //Get the number of children of the current node
        Integer number = zkClient.countChildren(ROOT_NODE);
        System.out.println(number);

        //4. Modify node
        zkClient.writeData(ROOT_NODE, "update hello2");

        //DataUpdater has only one interface, which is a functional interface, so you can use lambda expression shorthand
        DataUpdater<String> dataUpdater = new DataUpdater<String>() {
            @Override
            public String update(String s) {
                return "update hello3";
            }
        };
        //The above lines of code can be abbreviated using lambda expressions
        //DataUpdater<String> dataUpdater2 = s -> "update hello3";

        zkClient.updateDataSerialized(ROOT_NODE, dataUpdater);

        //5. Delete node
        //To delete a node, this method cannot delete a node with child nodes (if there are child nodes, an error will be reported: NotEmptyException: KeeperErrorCode = Directory not empty for /root)
        //zkClient.delete(ROOT_NODE);
        //Delete node according to node path, version number
        zkClient.delete(ROOT_NODE + "/home", -1);
        //Recursively delete the current node and its children
        zkClient.deleteRecursive(ROOT_NODE);
    }

}

4.5 summary of event monitoring mechanism of zkclient

In the native Zk API, it provides the mechanism Listener node of the watcher, and the zkClient converts it into the concept of Listener, which is to subscribe to the events of the server, so we can handle the events as long as we implement the corresponding methods of the IZkChildListener interface.

Subscribe / unsubscribe node change event:

public List<String> subscribeChildChanges(String path, IZkChildListener listener)
public void unsubscribeChildChanges(String path, IZkChildListener childListener)

Subscribe / unsubscribe data change event:

public void subscribeDataChanges(String path, IZkDataListener listener)
public void unsubscribeDataChanges(String path, IZkDataListener dataListener)

Subscription / unsubscribe connection status change event:

public void subscribeStateChanges(final IZkStateListener listener)
public void unsubscribeStateChanges(IZkStateListener stateListener)

Cancel all subscription events:

public void unsubscribeAll()

The above monitoring content is excerpted from: Event monitoring mechanism of zkClient

The ZkClient is also good, that is, the update is slow, and I don't think it's maintained very much. Now the main stream is the cursor client. Next, I'll introduce the use of the cursor client.

5 use of Java client-side cursor

Netflix cursor (already donated to Apache maintenance): https://github.com/Netflix/curator
Apache creator website: http://curator.apache.org

Curator is a Zookeeper Java client developed by Netflix on the basis of native zookeeper. At present, curator is donated to Apache. Now it is an open source project under Apache. Compared with the native client provided by zookeeper, the curator is highly abstract and encapsulated, which simplifies the development and operation of zookeeper client.

Compared with ZkClient, cursor has more powerful functions. It not only solves the legacy problems of the native zk Api, but also provides many common tool classes, as well as many solutions, such as distributed locks. The use of the cursor API is more concise and convenient. It provides streaming operation and can call methods in the way of point. Point. Point. So the cursor is the mainstream zk client at present.

By checking the official documents, it can be found that the cursor mainly solves three types of problems:

  • Encapsulate the connection processing between ZooKeeper client and ZooKeeper server
  • Provides a set of Fluent style operation API (point. Point. Point.)
  • Provide abstract encapsulation of various application scenarios (recipe s) of ZooKeeper, such as distributed lock service, cluster leader election, shared counter, cache mechanism, distributed queue, etc

5.1 add the cursor client dependency

To use the cursor, you need to rely on the jar package. Use the following method to add maven dependency
Add the following dependencies:

<!-- curator-framework -->
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-framework</artifactId>
    <version>4.2.0</version>
</dependency>
<!-- curator-recipes -->
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-recipes</artifactId>
    <version>4.2.0</version>
</dependency>

5.2 use of common APIs of the three-party client-side Coordinator

5.2.1 creating a cursor connection object

There are mainly the following ways:

public static CuratorFramework newClient(String connectString, RetryPolicy retryPolicy)
public static CuratorFramework newClient(String connectString, int sessionTimeoutMs, int connectionTimeoutMs, RetryPolicy retryPolicy)

Method parameter description:
connectString: refers to the zookeeperAddress ip to be connected
retryPolicy: refers to which retry policy is used when connecting zk
Sessiontimeouts: refers to the session timeout
Connectiontimeouts: refers to the connection timeout

Code example:

	/**
     * <p>
     * Create a cursor connection object
     * <p/>
     *
     * @param
     * @return
     * @Date 2020/6/21 12:29
     */
    public static void connectCuratorClient() {
        //In the old version, create zookeeper to connect to the client
        client = CuratorFrameworkFactory.builder().
                connectString(ZK_ADDRESS).
                sessionTimeoutMs(5000).
                connectionTimeoutMs(10000).
                retryPolicy(retry).
                build();

        //Create zookeeper connection, new version
        //client = CuratorFrameworkFactory.newClient(ZK_ADDRESS, retry);

        //Start the client. Most mutator methods will not work until the client is started
        client.start();

        System.out.println("zookeeper Initialize connection successful:" + client);
    }

In the cursor, you first need to create a zookeeerclient connection instance through the cursor framework factory, and then you can continue with various basic operations. It should be noted that for connection retry policy, the cursor client provides the following by default:

RetryNTimes: N retries
Exponential backoff retry: retry a certain number of times, and the time of each retry increases in turn
RetryOneTime: try again
RetryUntilElapsed: retry for a certain time

5.2.2 creating a zNode node

There are mainly the following methods:

	 public T forPath(String path) 
	 //Create node and assign content
	 public T forPath(String path, byte[] data)
	 //Judge whether the node exists. If the node exists, an error will still be reported during creation
	 public ExistsBuilder checkExists()

Code example:

 	/**
     * <p>
     * Create nodes and support the assignment of data content
     * <p/>
     *
     * @param nodePath
     * @param data
     * @return void
     * @Date 2020/6/21 12:39
     */
    private static void createNode(String nodePath, String data) throws Exception {
        if (StringUtils.isEmpty(nodePath)) {
            System.out.println("Node[" + nodePath + "]Cannot be empty");
            return;
        }

        //1. Judge whether the node exists or not, otherwise an error will be reported: [nodeexistsexception: keeperrorcode = nodeexists for / root]
        Stat exists = client.checkExists().forPath(nodePath);
        if (null != exists) {
            System.out.println("Node[" + nodePath + "]Existing, cannot add");
            return;
        } else {
            System.out.println(StringUtils.join("Node[", nodePath, "]No, you can add a new node!"));
        }

        //2. To create a node, the cursor client development provides a Fluent style API, which is a streaming coding method, and can constantly place, point and call API methods
        //Create persistent nodes (persistent by default)
        client.create().forPath(nodePath);

        //3. Manually specify the type of node
        client.create()
                .withMode(CreateMode.PERSISTENT)
                .forPath(nodePath);

        //4. If the parent does not exist, create the parent of the current node
        String node = client.create()
                .creatingParentsIfNeeded()
                .forPath(nodePath);
        System.out.println(node);

        //Create a node and assign content to the current node
        if (StringUtils.isNotBlank(data)) {
            //5. Create a permanent node and assign content to the current node
            client.create()
                    .forPath(nodePath, data.getBytes());

            //6. Create a permanent ordered node
            client.create().withMode(CreateMode.PERSISTENT_SEQUENTIAL).forPath(nodePath, data.getBytes());

            //7. Create temporary node
            client.create()
                    .withMode(CreateMode.EPHEMERAL)
                    .forPath(nodePath, data.getBytes());
        }

        //8. Create a temporary ordered node
        client.create()
                .withMode(CreateMode.EPHEMERAL_SEQUENTIAL)
                .forPath(nodePath, data.getBytes());
    }

5.2.3 get node list

5.2.3.1 obtaining node content

The main api methods are as follows:

	//Get a node data
	client.getData().forPath(nodePath)
	//Read the data of zookeeper and put it into Stat
	client.getData().storingStatIn(stat1).forPath(nodePath)

Parameter Description:

  • path: Specifies the node to read
  • stat: Specifies the node status information of the data node

The code example is as follows:

	/**
     * <p>
     * Get node data
     * <p/>
     *
     * @param nodePath
     * @return void
     * @Date 2020/6/21 13:13
     */
    private static void getNode(String nodePath) throws Exception {
        //Get a node data
        byte[] bytes = client.getData().forPath(nodePath);
        System.out.println(StringUtils.join("Node:[", nodePath, "],Data:", new String(bytes)));

        //Read the data of zookeeper and put it into Stat
        Stat stat1 = new Stat();
        byte[] bytes2 = client.getData().storingStatIn(stat1).forPath(nodePath);
        System.out.println(StringUtils.join("Node:[", nodePath, "],Data:", new String(bytes2)));
        System.out.println(stat1);
    }

5.2.3.2 get the list of child nodes

The common api methods are as follows:

	//Get all children of a node
	client.getChildren().forPath(nodePath)

The code example is as follows:

	/**
     * <p>
     * Get node data
     * <p/>
     *
     * @param nodePath
     * @return void
     * @Date 2020/6/21 13:20
     */
    private static void getNode(String nodePath) throws Exception {
        //Get all children of a node
        List<String> stringList = client.getChildren().forPath(nodePath);
        if (CollectionUtils.isEmpty(stringList)) {
            return;
        }
        //Ergodic node
        stringList.forEach(System.out::println);
    }

5.2.4 setting and modifying node content

There are mainly the following methods to modify node contents:

	//Update node
	client.setData().forPath(nodePath, data.getBytes())
	//Specify version number, update node
	client.setData().withVersion(-1).forPath(nodePath, data.getBytes())
	//Set a node data asynchronously
	client.setData().inBackground().forPath(nodePath, data.getBytes())

The code example is as follows:

	/**
     * <p>
     * Set (modify) node data
     * <p/>
     *
     * @param nodePath
     * @param data
     * @return void
     * @Date 2020/6/21 13:46
     */
    private static void updateNode(String nodePath, String data) throws Exception {
        //Update node
        Stat stat = client.setData().forPath(nodePath, data.getBytes());
        //Specify the version number, update node, and if you specify the data version when updating, you need to be consistent with the current data version in zookeeper, - 1 means to match any version
        //Stat stat = client.setData().withVersion(-1).forPath(nodePath, data.getBytes());
        System.out.println(stat);

        //Set a node data asynchronously
        Stat stat1 = client.setData().inBackground().forPath(nodePath, data.getBytes());
        System.out.println(stat1.toString());
    }

5.2.5 delete the zNode node

There are several methods:

	//Delete node
	client.delete().forPath(nodePath);
	
	//Delete the node. In case of network failure, zookeeper can guarantee to delete the node
	client.delete().guaranteed().forPath(nodePath);
	
	//Cascade delete nodes (if the current node has children, the children can also be deleted together)
	client.delete().deletingChildrenIfNeeded().forPath(nodePath);

5.2.6 summary of using the cursor

The cursor client is very convenient to use, and there are many builder objects, such as CreateBuilder, DeleteBuilder, ExistsBuilder, GetDataBuilder, SetDataBuilder and so on.

The above describes the use of the basic Api of the cursor. If you want to know more about the use of Api, you can refer to the official website for learning and use. The following is the complete code of the above cursor client test.

The complete code example is as follows:

/**
 * <p>
 * Curator Basic use of client
 * <p/>
 *      
 * @author smilehappiness
 * @Date 2020/6/21 9:41
 */
public class ZookeeperCuratorClient {

    /**
     * Client connection address
     */
    private static final String ZK_ADDRESS = "ip:2181";
    /**
     * Client root node
     */
    private static final String ROOT_NODE = "/root";
    /**
     * Customer terminal node
     */
    private static final String ROOT_NODE_CHILDREN = "/root/children";
    /**
     * Session timeout
     */
    private final int SESSION_TIMEOUT = 20 * 1000;
    /**
     * Connection timeout
     */
    private final int CONNECTION_TIMEOUT = 5 * 1000;
    /**
     * Create a zookeeper connection instance
     */
    private static CuratorFramework client = null;

    /**
     * Retry policy
     * baseSleepTimeMs: Initial retry wait time in milliseconds
     * maxRetries: Maximum retries
     */
    private static final RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
    /**
     * Retry policy
     * n: Maximum retries
     * sleepMsBetweenRetries: Retry interval in milliseconds
     */
    private static final RetryPolicy retry = new RetryNTimes(3, 2000);

    static {
        // Create a cursor connection object
        connectCuratorClient();
    }

    /**
     * <p>
     * Create a cursor connection object
     * <p/>
     *
     * @param
     * @return
     * @Date 2020/6/21 12:29
     */
    public static void connectCuratorClient() {
        //In the old version, create zookeeper to connect to the client
        /*client = CuratorFrameworkFactory.builder().
                connectString(ZK_ADDRESS).
                sessionTimeoutMs(5000).
                connectionTimeoutMs(10000).
                retryPolicy(retry).
                build();*/

        //Create zookeeper connection, new version
        client = CuratorFrameworkFactory.newClient(ZK_ADDRESS, retry);

        //Start the client. Most mutator methods will not work until the client is started
        client.start();

        System.out.println("zookeeper Initialize connection successful:" + client);
    }

    public static void main(String[] args) throws Exception {
        //Create node
        //createNode(ROOT_NODE, null);
        //createNode(ROOT_NODE_CHILDREN, "child data");

        //Get node data
        getNode(ROOT_NODE);

        //Set (modify) node data
        updateNode(ROOT_NODE, "update curator data");
        //Set a node data asynchronously
        updateNode(ROOT_NODE, "update curator data with Async");

        //Delete the specified node (in the native zk, data with child nodes cannot be deleted directly)
        deleteNode(ROOT_NODE);
    }

    /**
     * <p>
     * Create nodes and support the assignment of data content
     * <p/>
     *
     * @param nodePath
     * @param data
     * @return void
     * @Date 2020/6/21 12:39
     */
    private static void createNode(String nodePath, String data) throws Exception {
        if (StringUtils.isEmpty(nodePath)) {
            System.out.println("Node[" + nodePath + "]Cannot be empty");
            return;
        }

        //1. Judge whether the node exists or not, otherwise an error will be reported: [nodeexistsexception: keeperrorcode = nodeexists for / root]
        Stat exists = client.checkExists().forPath(nodePath);
        if (null != exists) {
            System.out.println("Node[" + nodePath + "]Existing, cannot add");
            return;
        } else {
            System.out.println(StringUtils.join("Node[", nodePath, "]No, you can add a new node!"));
        }

        //2. To create a node, the cursor client development provides a Fluent style API, which is a streaming coding method, and can constantly place, point and call API methods
        //Create persistent nodes (persistent by default)
        //client.create().forPath(nodePath);

        //3. Manually specify the type of node
        /*client.create()
                .withMode(CreateMode.PERSISTENT)
                .forPath(nodePath);*/

        //4. If the parent does not exist, create the parent of the current node
        /*String node = client.create()
                .creatingParentsIfNeeded()
                .forPath(nodePath);
        System.out.println(node);*/

        //Create a node and assign content to the current node
        if (StringUtils.isNotBlank(data)) {
            //5. Create a permanent node and assign content to the current node
            /*client.create()
                    .forPath(nodePath, data.getBytes());*/

            String node = client.create()
                    .creatingParentsIfNeeded()
                    .forPath(nodePath, data.getBytes());
            System.out.println(node);

            //6. Create a permanent ordered node
            client.create().withMode(CreateMode.PERSISTENT_SEQUENTIAL).forPath(nodePath + "-sequential", data.getBytes());

            //7. Create temporary node
            client.create()
                    .withMode(CreateMode.EPHEMERAL)
                    .forPath(nodePath + "/temp", data.getBytes());

            //8. Create a temporary ordered node
            client.create()
                    .withMode(CreateMode.EPHEMERAL_SEQUENTIAL)
                    .forPath(nodePath + "/temp-sequential", data.getBytes());
        }
    }

    /**
     * <p>
     * Get node data
     * <p/>
     *
     * @param nodePath
     * @return void
     * @Date 2020/6/21 13:13
     */
    private static void getNode(String nodePath) throws Exception {
        //Get a node data
        byte[] bytes = client.getData().forPath(nodePath);
        System.out.println(StringUtils.join("Node:[", nodePath, "],Data:", new String(bytes)));

        //Read the data of zookeeper and put it into Stat
        Stat stat1 = new Stat();
        byte[] bytes2 = client.getData().storingStatIn(stat1).forPath(nodePath);
        System.out.println(StringUtils.join("Node:[", nodePath, "],Data:", new String(bytes2)));
        System.out.println(stat1);

        //Get all children of a node
        List<String> stringList = client.getChildren().forPath(nodePath);
        if (CollectionUtils.isEmpty(stringList)) {
            return;
        }
        //Ergodic node
        stringList.forEach(System.out::println);
    }

    /**
     * <p>
     * Set (modify) node data
     * <p/>
     *
     * @param nodePath
     * @param data
     * @return void
     * @Date 2020/6/21 13:46
     */
    private static void updateNode(String nodePath, String data) throws Exception {
        //Update node
        Stat stat = client.setData().forPath(nodePath, data.getBytes());
        //Specify the version number, update node, and if you specify the data version when updating, you need to be consistent with the current data version in zookeeper, - 1 means to match any version
        //Stat stat = client.setData().withVersion(-1).forPath(nodePath, data.getBytes());
        System.out.println(stat);

        //Set a node data asynchronously
        Stat stat1 = client.setData().inBackground().forPath(nodePath, data.getBytes());
        if (null != stat1) {
            System.out.println(stat1);
        }
    }

    /**
     * <p>
     * Delete the specified node
     * <p/>
     *
     * @param nodePath
     * @return void
     * @Date 2020/6/21 13:50
     */
    private static void deleteNode(String nodePath) throws Exception {
        //Delete node
        //client.delete().forPath(nodePath);

        //Delete the node. In case of network failure, zookeeper can guarantee to delete the node
        //client.delete().guaranteed().forPath(nodePath);

        //Cascade delete nodes (if the current node has children, the children can also be deleted together)
        client.delete().deletingChildrenIfNeeded().forPath(nodePath);
    }

}

6. Selection of zookeeper, ZkClient and cursor

6.1 Native Client Zookeeper

There are problems with the official zookeeper client itself:

  • When the session timeout is abnormal, automatic reconnection is not supported, manual reconnection is required, and programming is cumbersome (if the network is unstable in the production environment, this situation is more obvious)
  • ZooKeeper's watcher monitoring is one-time, and will fail after registration
  • The node data is binary, and the object data needs to be converted to binary for saving
  • Recursive node creation is not supported. You need to create a parent node before creating a child node
  • Recursive node deletion is not supported. You need to delete the child node first and then the parent node
  • There is no leadership election mechanism. In the case of a cluster, you may need to implement stand by. One service is suspended, and the other needs to be replaced
  • The client only provides an interface for storing byte arrays, while objects are usually used in projects
  • The client interface has too many exceptions to handle, and often we don't know how to handle them
  • The establishment of the client-side and server-side sessions of the native zookeeper is an asynchronous process, that is to say, in the program, our program method returns immediately after processing the client-side initialization (the program executes the code downward, so in most cases, we do not really build a usable session, and only when the declaration cycle of the session is "CONNECTED" can we really establish it , so we need to use CountDownLatch, a tool class in multithreading, to control. After connecting to the zk client, we can continue to operate the zNode node.)

6.2 ZkClient client

ZkClient is packaged on the native Zookeeper API interface, which is easier to use. ZkClient also implements Session timeout reconnection, Watcher registration and other functions, which makes these operations transparent to development and improves development efficiency.

ZkClient mainly solves two major problems of native api:

  • ZkClient solves the problem of one-time registration of the watcher. The event of znode is redefined as the change of child node, the change of data and the change of connection state. ZkClient converts the WatchedEvent of the watcher to the above three cases to process. After the watcher is executed, it reads the data again and registers a new same watcher.

    Zkclient transforms Zookeeper's watcher mechanism into a more understandable form of subscription, and this relationship can be maintained rather than one-time. That is to say, the change of child nodes, data and state can be subscribed. When the watcher is used up, zkclient will automatically add the same watcher.

  • Automatically create a new ZooKeeper instance for reconnection during session loss and session expire

The zookeeper client of 101tec mainly has the following features:

  • It provides the reconnection feature of zookeeper, which can reestablish the connection when the chain is broken, no matter whether the session fails or not
  • The persistent event listener mechanism -- the ZKClient framework divides the event redefinition into three situations: stateChanged, znodeChanged, and dataChanged. Users can register the listener in these three situations (znodeChanged and dataChanged are related to the path), rather than register the Watcher.
  • Zookeeper Exception handling - there are many exceptions in zookeeper, and each Exception needs different attention. I0Itec simply encapsulates them.
  • Data serialization -- simple data serialization. (serializer / deserialzer)
  • There is a default leadership election mechanism

The ZkClient is still good. Now the company should use more of it. However, the client is not updated very much. Now the main client is the cursor client.

6.3 cursor client (recommended)

Curator is a Zookeeper Java client developed by Netflix on the basis of native zookeeper. At present, curator is donated to Apache. Now it is an open source project under Apache. Compared with the native client provided by zookeeper, the curator is highly abstract and encapsulated, which simplifies the development and operation of zookeeper client.

By checking the official documents, it can be found that the cursor mainly solves three types of problems:

  • Encapsulate the connection processing between ZooKeeper client and ZooKeeper server
  • Provides a set of Fluent style operation API (point. Point. Point.)
  • Provide abstract encapsulation of various application scenarios (recipe s) of ZooKeeper, such as distributed lock service, cluster leader election, shared counter, cache mechanism, distributed queue, etc

Compared with ZkClient, cursor has more powerful functions. It not only solves the legacy problems of the native zk Api, but also provides many common tool classes, as well as many solutions, such as distributed locks. The use of the cursor API is more concise and convenient. It provides streaming operation and can call methods in the way of point. Point. Point. So the cursor is the mainstream zk client at present.

Resources link:
https://www.cnblogs.com/lookupthesky/p/9881806.html
https://cloud.tencent.com/developer/article/1470953
https://blog.csdn.net/u013468915/article/details/80915271
https://blog.csdn.net/z1017915048/article/details/95351212

Blogging is to remember what you forget easily. It's also a summary of your work. I hope you can do your best and make progress together!

If there is any problem, please comment and discuss together. If there is any problem with the code, please correct it!

Add a pair of wings to your dream, let it fly freely in the sky!

Posted by tzuriel on Sun, 21 Jun 2020 01:15:06 -0700