preface

Zookeeper has two callback methods: Watcher and AsyncCallback. Zookeeper can not be used without these two methods, so it is important to understand the difference between them and their implementation. This article will focus on the following aspects

  • Difference between Watcher and AsyncCallback
  • Watcher’s callback implementation
  • A callback implementation of AsyncCallback
  • IO and event processing

Difference between Watcher and AsyncCallback

Let’s take a look at an example:

zooKeeper.getData(root, new Watcher() {
            public void process(WatchedEvent event) {

            }
        }, new AsyncCallback.DataCallback() {
            public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) {

            }
        }, null);
Copy the code

As you can see, the getData method can set two callbacks at the same time: Watcher and AsyncCallback. To solve this problem, we need to start with the functionality of these two interfaces.

  • Watcher:WatcherIt’s used to listen for node, session status, for examplegetDataPair data nodeaSet upwatcher, then whenaWhen the data content of theNodeDataChangedNotify and proceedwatcherThe callback.
  • AsyncCallback:AsyncCallbackIs used to process returned results when the ZooKeeper API is used asynchronously. Such as:getDataThe version of the synchronous call is:byte[] getData(String path, boolean watch,Stat stat), the version of the asynchronous call is:void getData(String path,Watcher watcher,AsyncCallback.DataCallback cb,Object ctx), as can be seen, the former is directly return the obtained results, the latter is throughAsyncCallbackCallback processing results.

Watcher

Watcher is managed primarily through ClientWatchManager.

The type of the Watcher

There are four types of Watcher in ClientWatchManager

  • defaultWatcher: createZookeeperIncoming when connectingWatcherIs used to monitor the session status
  • dataWatchesDeposit:getDataThe incomingWatcher
  • existWatchesDeposit:existsThe incomingWatcher, if the node already existsWatcherWill be added todataWatches
  • childWatchesDeposit:getChildrenThe incomingWatcher

As you can see from the code, the listener exists in the HashMap, where key is the node name PATH and value is Set

private final Map> dataWatches =
        new HashMap>();
private final Map> existWatches =
        new HashMap>();
private final Map> childWatches =
        new HashMap>();

private volatile Watcher defaultWatcher;
Copy the code

State types and event types of notifications

In the Watcher interface, all state types and event types have been defined

  • KeeperState.Disconnected(0)

    At this point, the client is disconnected and the ZK cluster is not connected.

    • EventType.None(-1)

      Trigger condition: The client receives this event when it is disconnected from the server.

  • KeeperState. SyncConnected(3)

    The client is connected

    • EventType.None(-1)

      Trigger condition: The client receives this notification after a session is successfully established with the server.

    • EventType. NodeCreated (1)

      Trigger condition: The desired node is created.

    • EventType. NodeDeleted (2)

      Trigger condition: The concerned node is deleted.

    • EventType. NodeDataChanged (3)

      Trigger condition: The content of the concerned node has been updated. Note that this place is talking about the version number of the index data called DatSpanning. Therefore, even if you update with the same data content, you will still receive this event notification. In any case, calling the update interface is bound to update datSpanning.

    • EventType. NodeChildrenChanged (4)

      Trigger condition: The child node of the concerned node has changed. Changes in the number and composition of child nodes are not notified of changes in the content of child nodes.

  • KeeperState. AuthFailed(4)

    Authentication failed

  • KeeperState. Expired(-112)

    The session timeout

Materialize method

ClientWatchManager has only one method, materialize, which returns a specific type of Watcher to listen on that node based on event types type and path.

public Set materialize(Watcher.Event.KeeperState state,
    Watcher.Event.EventType type, String path);
Copy the code

The core logic is as follows:

  1. type == None: Returns allWatcherThat is, all of themWatcherWill be triggered. ifdisableAutoWatchReset == trueAnd the currentstate ! = SyncConnected, then it will emptyWatcher, means to remove all of theWatcher.
  2. type == NodeDataChanged | NodeCreated: Return listeningpathThe node’sdataWatches & existWatches
  3. type == NodeChildrenChanged: Return listeningpathThe node’schildWatches
  4. type == NodeDeleted: Return listeningpathThe node’sdataWatches | childWatches

Each return removes the node’s corresponding Watcher from the HashMap, for example, addTo(datawatch.remove (clientPath), result); That’s why Watcher is disposable (except defaultWatcher). It is worth noting that since the HashSet is used to store the Watcher, adding the same instance repeatedly will only be triggered once.

AsyncCallback

Zookeeper exists, getData, getChildren method has the asynchronous version, whether their difference with the synchronization method is only waiting for a response, the underlying sent by sendThread asynchronously. Here’s a picture to illustrate:



The figure above shows synchronous/asynchronous invocationgetDataOther methods are similar.

IO and event processing

The Zookeeper client starts two resident threads

  • SendThread: Performs I/O operations, including sending, receiving responses, and sending pings.
  • EventThread: handles events and executes callback functions.

readResponse

ReadResponse is the core function of SendThread to process the response. The core logic is as follows:

  1. Accept the server’s response and deserialize itReplyHeader: there is a single threadSendThreadIs responsible for receiving the response from the server. Assume that the byte stream received from the server isincomingBufferSo let’s take thisincomingBufferDeserialize toReplyHeader.
  2. Determine the response type: Determine whether ReplyHeader is a Watcher response or an AsyncCallback response: ReplyHeader.getxID () stores the response type.

    1. If it isWatcherType response: fromReplyHeaderCreated in theWatchedEvent.WatchedEventIt stores the path of the node and goes toWatcherManagerFind all of the objects associated with this nodeWatcher, write them toEventThreadthewaitingEventsIn the.
    2. If it isAsyncCallbackType response: fromReplyHeaderReads theresponsetheresponseDescribed isExists, setData, getData, getChildren, create.....Which is the asynchronous callback. frompendingQueueGet in thePacket.PacketIn thecbStore theAsyncCallback, the result callback of the asynchronous API. The final will bePacketWritten to theEventThreadthewaitingEventsIn the.

processEvent

ProcessEvent is the core function for EventThread processing. The core logic is as follows:

  1. ifevent instanceof WatcherSetEventPairTo removepairIn theWatchers, call one by onewatcher.process(pair.event)
  2. Otherwise,eventforAsyncCallback, according to thep.responseDetermine the response type and perform the callback for the responseprocessResult.

Both Watcher and AsyncCallback are handled by EventThread and are separated by processEvent.

conclusion

Both Watcher and AsyncCallback in the Zookeeper client are asynchronous callback methods, but their callback timing is different. The former is triggered by the server sending an event to trigger the client callback, while the latter is actively triggered by the client after the request is executed and the response is received. What they all have in common is that after the server response is retrieved, SendThread writes to EventThread’s waitingEvents, which is then retrieved and processed one by one by EventThread from the event queue.

Resources ZooKeeper personal Notes client Watcher and AsycCallback Callback ZooKeeper Notes 13 Event notification type of ZooKeeper Watcher