ZooKeeper implements member list management

Keywords: Big Data Zookeeper Java network Attribute

outline

One of the main difficulties of distributed applications is "partial failure". For example, through message middleware, one node in the network sends messages to another node. It is difficult for the sending node to know whether the message is successfully delivered, when it is delivered, whether the receiving node is running normally, whether the message is processed correctly, and when it is processed. ZooKeeper is used to provide distributed coordination services in distributed applications. Using ZooKeeper itself can not avoid "partial failure", because "partial failure" is the attribute of distributed applications. Instead, ZooKeeper provides a series of tools to safely handle "partial failures" in distributed applications.

ZooKeeper features:

  • Simple: The ZooKeeper core is a file system that supports some simple operations and some special abstract concepts such as "commands" and "notifications".
  • Expressive: ZooKeeper provides a wealth of tools for building various types of coordinated data structures and services, such as distributed queues, distributed locks, leader election, etc.
  • High Availability: ZooKeeper is designed as a high availability cluster to avoid single point of failure.
  • Decoupling: Application references do not need to know each other's existence, or even exist at the same time.
  • Open source libraries: ZooKeeper provides a generic implementation of the generic coordination model.
  • High efficiency

Install and run ZooKeeper

A stand-alone version of ZooKeeper is very simple to install on Linux. First, you need a runtime environment for Java 6 and above. Download the ZooKeeper package and extract it to the specified directory. Set the environment variable and add the executable file path in ZooKeeper to the PATH environment variable.
 

% export ZOOKEEPER_INSTALL=/home/tom/zookeeper.x.y.z
% export PATH=$PATH:$ZOOKEEPER_INSTALL/bin

The zoo.cfg file was found in the subdirectory conf of the installation directory, but it was not created by itself. This is the default configuration file. Add the following three lines:

tickTime=2000
dataDir=/Users/tom/zookeeper
clientPort=2181

These are the minimum configurations required for a stand-alone ZooKeeper. In short, tickTime is the smallest unit of time in ZooKeeper, and its orientation unit is milliseconds. dataDir is the local file directory for ZooKeeper persistent data. clientPort is the port number that provides services to the outside world. Execute the following command to start the ZooKeeper service:

% zkServer.sh start

Check whether ZooKeeper is working properly:

% echo ruok | nc localhost 2181
imok

Common ZooKeeper commands consist of four characters, as shown in the following table:

type command describe
Service state ruok Service correctly outputs imok
  conf Output configuration file content
  envi Output environment information, such as java version, ZooKeeper version, etc.
  srvr Output statistics, such as delay statistics, znode number, node operation mode, etc.
  stat Output statistics and client connections, etc.
  srst Reset statistics
  isro Display service mode: read-only mode, read-write mode, etc.
Client Connection dump Displays all sessions, temporary znode, etc., and must be connected to the primary node.
  cons Display client connection statistics
  crst Reset client connection statistics
Monitor wchs Show general information about service monitoring
  wchc List all monitoring of connections that may affect performance
  wchp Listing all monitors through znode paths may affect performance
Monitor mntr System performance monitoring, live as the data source of other monitoring systems.

Examples of group members

Assume that services are provided to the outside world through a set of instances. We want the client to be able to locate an instance in a set of services and use it. There are two main problems. One is the problem of member column management. The member list can not be managed on a single node in the network. Otherwise, there will be a single point of failure. The other is the increase or decrease of members in the member list. The service instance itself can be self-registered in the member list, but it can't delete itself, such as in the case of its own crash. We need an active mechanism to update the membership list automatically when the instance status in the membership list changes. ZooKeeper provides this kind of functionality.

The concept of znode in ZooKeeper.

ZooKeeper can be seen as a highly available file system. It also has concepts similar to directory nodes and file nodes in the file system. In ZooKeeper, it is called znode. Znode can contain data as a file, or other znodes as a directory. Znode can form a hierarchical namespace, so the most natural way to manage the list of members is to create a znode with the group name, and the child znode below represents the list members. In this case, you only need the name of znode, and you don't need to store data in it.

Create group

Create the znode shown in Figure 1 below through the Java API provided by ZooKeeper.

Figure 1

The code is as follows:

public class CreateGroup implements Watcher {
    private static final int SESSION_TIMEOUT = 5000;
    
    private ZooKeeper zk;
    private CountDownLatch connectedSignal = new CountDownLatch(1);

    public void connect(String hosts) throws IOExeption, InterruptedException {
        zk = new ZooKeeper(hosts, SESSION_TIMEOUT, this);
        connectedSignal.await();
    }

    @Override
    public void process(WatchedEvent event) {
        if (event.getState() == KeeperState.SyncConnected) {
            connectedSignal.countDown();
        }
    }

    public void create(String groupName) throws KeeperException,
        InterruptedException {
        String path = "/" + groupName;
        String createdPath = zk.create(path, null/*data*/, Ids.OPEN_ACL_UNSAFE,
            CreateMode.PERSISTENT);
        System.out.println("Created " + createdPath);
    }

    public void close() throws InterruptedException {
        zk.close();
    }

    public static void main(String[] args) throws Exception {
        CreateGroup createGroup = new CreateGroup();
        createGroup.connect(args[0]);
        createGroup.create(args[1]);
        createGroup.close();
    }
}

Code parsing:
CreateGroup instance is first created in main function, then connect member function is called. The connect member function creates an instance of ZooKeeper, representing the connection to the ZooKeeper server. It requires three parameters. The first parameter is the server address and the default port number is 2181. The second parameter is the timeout value, which is five seconds. Every three parameters are monitors, and here is the newly created CreateGroup instance.
When a ZooKeeper instance is created, a thread is started to connect to the server and the call returns immediately. So you need to manually add a waiter and wait until the connection is complete before returning. This is implemented using Java's CountDownLatch class. The initial value of the example connectedSignal is 1. When the connect function is called, the caller priest connectedSignal.await() waits for its value to become 0. When the newly started thread connects to the server and completes, the process function calls back. When the process function receives keeperState.SyncConnected, it indicates that the connection has been established. Calling connectedSignal.countDown() reduces its initial value by 1 to 0, and then connectedSignal.await() returns, and the connection function returns.

Next, call the createGroup.create() function. The zk.create function creates znode with four parameters: path, znode content, access control, and type. There are two main types. One is temporary. When the connection fails, ZooKeeper service automatically deletes the znode. The other is persistent. When the connection fails, it does not delete automatically unless the deletion is displayed.

Join group

As a separate application, each member needs to add itself to the group membership list at run time, and when the application exits, it is automatically deleted from the group membership list, so we create a temporary znode. The code is as follows. ConnectionWatcher indicates how to create ZooKeeper instances and how to connect services, omitting:

public class JoinGroup extends ConnectionWatcher {
    public void join(String groupName, String memberName) throws KeeperException,
        InterruptedException {
        String path = "/" + groupName + "/" + memberName;
        String createPath = zk.create(path, null/*data*/, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
        System.out.println("Created " + createPath);
    }

    public static void main(String[] args) throws Exception {
        JoinGroup joinGroup = new JoinGroup();
        joinGroup.connected(args[0]);
        joinGroup.join(args[1], args[2]);
        Thread.sleep(Long.MAX_VALUE);
    }
}

List group members

The code is as follows, omitting the logical ConnectionWatcher class that implements connection establishment:
 

public class ListGroup extends ConnectionWatcher {
    public void list(String groupName) throws KeeperException,
        InterruptedException {
        String path = "/" + groupName;

        try {
            List<String> children = zk.getChildren(path, false);
            if (children.isEmpty()) {
                System.out.printf("No members in group %s\n", groupName);
                System.exit(1);
            }
            for(String child: children) {
                System.out.println(child);
            }
        } catch (KeeperException.NoNodeException e) {
            System.out.print("Group %s does not exist\n", groupName);
            System.exit(1);
        }
    }

    public static void main(String[] args) throws Exception {
        ListGroup listGroup = new ListGroup();
        listGroup.connect(args[0]);
        listGroup.list(args[1]);
        listGroup.close();
    }
}

delete group

The code is as follows:
 

public class DeleteGroup extends ConnectionWatcher {
    public void delete(String groupName) throws KeeperException,
        InterruptedException {
        String path = "/" + groupName;

        try {
            List<String> children = zk.getChildren(path, false);
            for (String child: children) {
                zk.delete(path + "/" + child, -1);
            }
            zk.delete(path, -1);
        } catch (KeeperException.NoNodeException e) {
          System.out.printf("Group %s does not exist\n", groupName);
          System.exit(1);
        }
    }
    
    public static void main(String[] args) throws Exception {
        DeleteGroup deleteGroup = new DeleteGroup();
        deleteGroup.connect(args[0]);
        deleteGroup.delete(args[1]);
        deleteGroup.close();
    }
}

 

Posted by micah1701 on Thu, 09 May 2019 21:24:40 -0700