What is the Watcher mechanism in Zookeeper?

Article catalog

What is the watcher mechanism

Zookeeper's watcher mechanism is a very core mechanism. The core functions provided by zookeeper, such as publish / subscribe, monitoring node changes (such as node deletion, content changes, or sub node state changes), are based on the watcher mechanism

In fact, the implementation of the watcher mechanism is an observer mode, but this mode is distributed rather than stand-alone.

The watcher mechanism involves state monitoring, which actually includes two states: the connection state (notification state) between the client and the server, and the state (event type) of the node

Notification state (KeeperState)

The keeperstate describes some notification types when the connection state between the client and the server changes. In the Java client of ZK, there is an enumeration to specifically store these states: org.apache.zookeeper.Watcher.Event.KeeperState

Some of the main attributes are as follows:

Enumeration property name describe
DisConnected It's easy to understand that CS is not connected
SyncConnected Normal connection status
Expired The session timed out. When the client and the server are connected, the server will allocate a possession time for them. If the contract has not been renewed when it expires, the Expired state will occur
NoSyncConnected Property timeout
AuthFailed Authentication failed, server refused to connect
ConnectedReadOnly This is the pattern that zookeeper 3.3 started to provide. If the client is set to allow ReadOnly, then when more than half of the machines in the cluster are abnormal, according to the past practice, the whole service can not be provided directly to the outside world; if ReadOnly is set, even if more than half of the exceptions occur, the client can also do read-only

Event type

In fact, this is more used because it describes the state change of the Znode node to complete some publish / subscribe functions. Similarly, in the Java client, there is an enumeration corresponding to it: org.apache.zookeeper.Watcher.Event.EventType

Enumeration property name describe
NodeCreated Nodes watched by Watcher are created
NodeDeleted Node watched by Watcher is deleted
NodeDataChanged Node value monitored by Watcher is modified
NodeChildrenChanged Status change of child nodes of nodes monitored by Watcher

Client implementation of EventType registration and notification

In fact, they are essentially three hashmaps, which can be seen clearly directly from the code

//The key s of these maps are node paths, similar to / a,/b, and value is all the watch es corresponding to this node

//Node content monitoring
private final Map<String, Set<Watcher>> dataWatches = new HashMap<String, Set<Watcher>>();

//Monitor on behalf of node status change (create or destroy)
private final Map<String, Set<Watcher>> existWatches = new HashMap<String, Set<Watcher>>();

//Sub node status monitoring of a node
private final Map<String, Set<Watcher>> childWatches = new HashMap<String, Set<Watcher>>();

Server implementation of EventType registration and notification

In the same way, there are two map s that are related to it and directly pasted with code

/*
* Here, the key represents the node path, and the value represents the set of client connections
* Function: when a node changes, all watcher s listening to the node will be directly obtained, and then one by one notifications will be given
*/
private final HashMap<String, HashSet<Watcher>> watchTable =
    new HashMap<String, HashSet<Watcher>>();

/**
* key Represents a client watcher, and value represents all node paths it listens to
* Why do you want to do this? Very simply, when a client is disconnected, some listening related to it should also be removed, that is, the watcher should be removed from the set of the corresponding path in the first map
* So this map can quickly locate all the path corresponding to this watcher
*/
private final HashMap<Watcher, HashSet<String>> watch2Paths =
    new HashMap<Watcher, HashSet<String>>();

In addition, it should be noted that the watcher in the server is not the Watcher in the code, but an abstract ServerCnxn class that implements the Watcher interface,

EventType registration and notification process

When the client calls the corresponding api (such as getData) to initiate the registration event, the client will encapsulate the request into a Packet object, and then join the outGoingQueue queue queue to wait for sending. Note that at this time, the corresponding worker has not been maintained in the HashMap. This step needs to be performed after the service is installed and called back

When the server receives the object, it will register the watcher, that is, update the two hashmaps, and then respond to the client.

After receiving the response, the client will register the corresponding watcher to its own HashMap. This completes the registration

The notification process is also very simple. The server modifies the corresponding nodes, then removes all the watchers from its own watchTable, and notifies them one by one. The process function of the client's watcher will be called to complete a notification mechanism

It should be noted that the event notification in zookeeper is one-time, that is, when the server makes a notification, it will delete the watcher (mainly for performance consideration)

Simple code example

import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;

/**
 * Zookeeper Wathcher 
 * This class is a Watcher class org.apache.zookeeper.Watcher class)
 * @author(alienware)
 * @since 2015-6-14
 */
public class ZooKeeperWatcher implements Watcher {
    /** Define session expiration time */
    private static final int SESSION_TIMEOUT = 5000;
    /** zookeeper server address */
    private static final String CONNECTION_ADDR = "ip1:port1,ip2:port2,ip3:port3";
    /** zk Parent path settings */
    private static final String PARENT_PATH = "/a";
    /** zk Subpath settings */
    private static final String CHILDREN_PATH = "/b/c";
    /** zk variable */
    private ZooKeeper zk = null;

    /**
     * Create ZK connection
     * @param connectAddr ZK Server address list
     * @param sessionTimeout Session Timeout
     */
    public void createConnection(String connectAddr, int sessionTimeout) {
        this.releaseConnection();
        try {
            //this means to pass the current object to it (that is, the new ZooKeeperWatcher() instance object instantiated in the main function)
            zk = new ZooKeeper(connectAddr, sessionTimeout, this);
            System.out.println(LOG_PREFIX_OF_MAIN + "Start connection ZK The server");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Close ZK connection
     */
    public void releaseConnection() {
        if (this.zk != null) {
            try {
                this.zk.close();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    
    /**
     * Processing after receiving a Watcher notification from the Server.
     */
    @Override
    public void process(WatchedEvent event) {
        
        System.out.println("get into process method");
        
        if (event == null) {
            return;
        }
        
        // Get connection status
        KeeperState keeperState = event.getState();
        // Event type
        EventType eventType = event.getType();
        // Affected path
        String path = event.getPath();
        System.out.println("Connection status:\t" + keeperState.toString());
        System.out.println("Event type:\t" + eventType.toString());

        if (KeeperState.SyncConnected == keeperState) {
            // Successfully connected to ZK server
            if (EventType.None == eventType) {
                System.out.println( "Successfully connected to ZK The server");
                connectedSemaphore.countDown();
            } 
            //Create node
            else if (EventType.NodeCreated == eventType) {
                System.out.println("Node creation");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } 
            //Update node
            else if (EventType.NodeDataChanged == eventType) {
                System.out.println("Node data update");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } 
            //Update child nodes
            else if (EventType.NodeChildrenChanged == eventType) {
                System.out.println("Child node change");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } 
            //Delete node
            else if (EventType.NodeDeleted == eventType) {
                System.out.println("node " + path + " Deleted");
            }
            else ;
        } 
        else if (KeeperState.Disconnected == keeperState) {
            System.out.println("And ZK Server Disconnected");
        } 
        else if (KeeperState.AuthFailed == keeperState) {
            System.out.println("Permission check failed");
        } 
        else if (KeeperState.Expired == keeperState) {
            System.out.println("Session failure");
        }

    }

}

Tags: Zookeeper Apache Java Session

Posted on Thu, 18 Jun 2020 07:06:48 -0400 by paudelvikash