Source code analysis of Zookeeper: Watcher Mechanism

Source: Internet
Author: User

Source code analysis of Zookeeper: Watcher Mechanism

1. Set Watcher
To use Watcher, you must first implement the Watcher interface and pass the implementation Class Object to the specified method, such as getChildren and exist. Zookeeper allows you to specify a default Watcher object when constructing a Zookeeper object. The getChildren and exit methods can use this default Watcher object or specify a New Watcher object.

Code 1: Watcher Interface

Public interface Watcher {

/**
* Event status
*/
Public interface Event {
/**
* ZooKeeper status when an event occurs
*/
Public enum KeeperState {

@ Deprecated
Unknown (-1 ),

Disconnected (0 ),

@ Deprecated
NoSyncConnected (1 ),

SyncConnected (3 ),

AuthFailed (4 ),

ConnectedReadOnly (5 ),

SaslAuthenticated (6 ),

Expired (-112 );

Private final int intValue;

KeeperState (int intValue ){
This. intValue = intValue;
}

......
}

/**
* Events in ZooKeeper
*/
Public enum EventType {
None (-1 ),
NodeCreated (1 ),
NodeDeleted (2 ),
NodeDataChanged (3 ),
NodeChildrenChanged (4 );

Private final int intValue; // Integer representation of value
// For sending over wire
EventType (int intValue ){
This. intValue = intValue;
}
......
}
}

// Watcher callback Method
Abstract public void process (WatchedEvent event );
}


 

Code 2: Zookeeper. getChildren (final String, Watcher) Method

Public List <String> getChildren (final String path, Watcher watcher)
Throws KeeperException, InterruptedException
{
Final String clientPath = path;
PathUtils. validatePath (clientPath );

WatchRegistration wcb = null;
// If watcher is not equal to null, construct the WatchRegistration object,
// This object describes the relationship between watcher and path.
If (watcher! = Null ){
Wcb = new ChildWatchRegistration (watcher, clientPath );
}

// Add the root path prefix to the input path to form the absolute path of the server.
Final String serverPath = prependChroot (clientPath );

// Construct the RequestHeader object
RequestHeader h = new RequestHeader ();
// Set the operation type to OpCode. getChildren.
H. setType (ZooDefs. OpCode. getChildren );
// Construct the GetChildrenRequest object
GetChildrenRequest request = new GetChildrenRequest ();
// Set path
Request. setPath (serverPath );
// Set whether to use watcher
Request. setWatch (watcher! = Null );
// Construct the GetChildrenResponse object
GetChildrenResponse response = new GetChildrenResponse ();
// Submit the request and block waiting for the result
ReplyHeader r = cnxn. submitRequest (h, request, response, wcb );
If (r. getErr ()! = 0 ){
Throw KeeperException. create (KeeperException. Code. get (r. getErr ()),
ClientPath );
}
Return response. getChildren ();
}


The NIOServerCnxn class of Follower receives a request from the Client and calls the ZookeeperServer. processPacket () method. This method constructs a Request object and calls the first processor FollowerRequestProcessor.

Because our request is only a read operation, not a Quorum request or sync request, FollowerRequestProcessor does not need to call Follower. the request () method transfers the request to the Leader. You only need to pass the request to the next processor CommitProcessor.

When the CommitProcessor thread finds that the request is a read request, it directly adds the Requet object to the toProcess queue. In the next loop, the FinalRequestProcessor. processRequest method is called for processing.

FinalRequestProcessor. the processRequest method will eventually call the read operation methods (such as the statNode and getData methods) in ZKDatabase ), these methods of ZKDatabase will eventually call the methods of the DataTree class to obtain the znode information of the specified path and return it to the Client. Watcher is also set.

Code 3: How does FinalRequestProcessor process OpCode. getData requests?

Case OpCode. getData :{
LastOp = "GETD ";
GetDataRequest getDataRequest = new GetDataRequest ();
ByteBufferInputStream. byteBuffer2Record (request. request,
GetDataRequest );
// Obtain the znode object
DataNode n = zks. getZKDatabase (). getNode (getDataRequest. getPath ());
// If n is null, A NoNodeException is thrown.
If (n = null ){
Throw new KeeperException. NoNodeException ();
}
Long aclL;
Synchronized (n ){
AclL = n. acl;
}
// Check whether you have read permission
PrepRequestProcessor. checkACL (zks, zks. getZKDatabase (). convertLong (aclL ),
ZooDefs. Perms. READ,
Request. authInfo );
// Build state object stat
Stat stat = new Stat ();
// Obtain the znode data of the specified path,
// If GetDataRequest. getWatcher () returns true, the ServerCnxn type object cnxn is passed in.
// ServerCnxn implements the Watcher Interface
Byte B [] = zks. getZKDatabase (). getData (getDataRequest. getPath (), stat,
GetDataRequest. getWatch ()? Cnxn: null );
// Construct the GetDataResponse object
Rsp = new GetDataResponse (B, stat );
Break;
}


Code 4: DataTree. getData () method

Public byte [] getData (String path, Stat stat, Watcher watcher)
Throws KeeperException. NoNodeException {
// Obtain the DataNode object of the specified path from nodes map
DataNode n = nodes. get (path );
// If n is null, A NoNodeException is thrown.
If (n = null ){
Throw new KeeperException. NoNodeException ();
}
Synchronized (n ){
// Copy n to stat
N. copyStat (stat );
// If watcher is not null, put the (path, watcher) Key-value pair into the dataWatchers Map
If (watcher! = Null ){
DataWatches. addWatch (path, watcher );
}
// Return node data
Return n. data;
}
}


2. Modify znode data to trigger Watcher
The COMMIT phase submitted in Zookeeper Stage 2. When Follower receives a Write Request Leader. COMMIT packet from the Leader, the FinalRequestProcessor. processRequest () method is called. After the Leader sends the Leader. COMMIT packet, it also calls the FinalRequestProcessor. processRequest () method.

If setData is used to modify the data request, FinalRequestProcessor. the processRequest () method is finally called to the DataTree. the setData method applies txn to the specified znode, triggers Watcher, and sends notification to the Client.

The sequence diagram of the SetData request is as follows:

TriggerWatcher

Code 5: DataTree. setData () method

Public Stat setData (String path, byte data [], int version, long zxid,
Long time) throws KeeperException. NoNodeException {
Stat s = new Stat ();
// Obtain the DataNode object n according to the path
DataNode n = nodes. get (path );
// If n is null, A NoNodeException is thrown.
If (n = null ){
Throw new KeeperException. NoNodeException ();
}
Byte lastdata [] = null;
Synchronized (n ){
Lastdata = n. data;
N. data = data;
N. stat. setMtime (time );
N. stat. setMzxid (zxid );
N. stat. setVersion (version );
N. copyStat (s );
}
// Now update if the path is in a quota subtree.
String lastPrefix = getMaxPrefixWithQuota (path );
If (lastPrefix! = Null ){
This. updateBytes (lastPrefix, (data = null? 0: data. length)
-(Lastdata = null? 0: lastdata. length ));
}
// Trigger Watcher
DataWatches. triggerWatch (path, EventType. NodeDataChanged );
Return s;
}

 


Code 6: WatchManage. triggerWatcher () method to trigger Watcher.

Set <Watcher> triggerWatch (String path, EventType type, Set <Watcher> supress ){
WatchedEvent e = new WatchedEvent (type,
KeeperState. SyncConnected, path );
HashSet <Watcher> watchers;
Synchronized (this ){
// Remove watcher for path from watchTable
Watchers = watchTable. remove (path );
If (watchers = null | watchers. isEmpty ()){
If (LOG. isTraceEnabled ()){
ZooTrace. logTraceMessage (LOG,
ZooTrace. EVENT_DELIVERY_TRACE_MASK,
"No watchers for" + path );
}
Return null;
}
For (Watcher w: watchers ){
HashSet <String> paths = watch2Paths. get (w );
If (paths! = Null ){
Paths. remove (path );
}
}
}
// Process all Watcher statements about path cyclically. Here, the Watcher object is actually a ServerCnxn object.
For (Watcher w: watchers ){
If (supress! = Null & supress. contains (w )){
Continue;
}
W. process (e );
}
Return watchers;
}

 

Code 7: NIOServerCnxn. process method, send notification to the Client

Synchronized public void process (WatchedEvent event ){
ReplyHeader h = new ReplyHeader (-1,-1L, 0 );
If (LOG. isTraceEnabled ()){
ZooTrace. logTraceMessage (LOG, ZooTrace. EVENT_DELIVERY_TRACE_MASK,
"Deliver event" + event + "to 0x"
+ Long. toHexString (this. sessionId)
+ "Through" + this );
}

// Convert WatchedEvent to a type that can be sent over the wire
WatcherEvent e = event. getWrapper ();

// Send the notification to the Client
SendResponse (h, e, "notification ");
}


3. Summary
Watcher has the one-time trigger feature. In the code, we can also see that a watcher is deleted from the watchTable immediately after being processed.

-------------------------------------- Split line --------------------------------------

Ubuntu 14.04 installs distributed storage Sheepdog + ZooKeeper

CentOS 6 installs sheepdog VM distributed storage

ZooKeeper cluster configuration

Use ZooKeeper to implement distributed shared locks

Distributed service framework ZooKeeper-manage data in a distributed environment

Build a ZooKeeper Cluster Environment

Test Environment configuration of ZooKeeper server cluster

ZooKeeper cluster Installation

This article permanently updates the link address:

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.