This tutorial will introduce you to Apache ZooKeeper data model covering its internal data storage structure. Additionally, it will demonstrate the various operations that a client can perform using Apache ZooKeeper client libraries.
Apache ZooKeeper, at its core, provides an API to let you manage your application state in a highly read-dominant concurrent and distributed environment. It is optimized for and performs well in the scenario where read operations greatly outnumber write operations.
This tutorial assumes that you have got the basic idea of technical architecture and components of Apache ZooKeeper. If you are totally new to Apache ZooKeeper, you are strongly recommended to read the article - Introduction to Apache ZooKeeper.
Here are the required softwares and services that you would need to try the code mentioned in this tutorial -
- Access to a running ZooKeeper cluster in the form of URL to any of the nodes machine in cluster. If you are looking to set up ZooKeeper cluster, please follow these simple instructions mentioned here.
- JDK 7 or later installed on your machine
- Eclipse or any other IDE that you prefer with maven plug-in installed in it
- Access to Internet to let Maven download required dependency jars
Before we start with data model, let's talk briefly about the sessions in Apache ZooKeeper. Whenever we use Apache ZooKeeper client libraries to establish the connection, it starts a session which is active until either client process remains live or ZooKeeper cluster goes down or client itself disconnects.
Apache ZooKeeper stores its data in hierarchical Znodes (data nodes as in a tree structure) structure where each Znode can have its own data along with the child Znodes depending upon the type of Znode. Here is the graphical depiction of this structure -
As shown in the above diagram, there are three different types of ZNodes that a client can create in ZooKeeper -
- Persistent ZNodes - As the name suggests, these Znodes are persistent and will not be deleted by ZooKeeper.
- Ephemeral ZNodes - ZooKeeper also supports Ephemeral node which exists as long as the client session that created the node is active. As soon as the client's session is over, ZooKeeper deletes these Znodes. These Znodes are quite useful for its client by allowing them to get process shutdown notifications. However due to the client session dependency, these Znodes are not allowed to have child nodes.
- Sequential ZNodes - Sequential Znode always has unique names as ZooKeeper appends a sequence number to this Znode path. E.g. If you create sequential znode with path "/a/b", ZooKeeper will create the Znode with path "/a/b<sequence_no>".
Apart from this, data stored into znodes should not be more than few KBs as it is supposed to be coordination data such as status information, configuration etc. The ZooKeeper client and the server implementations have sanity checks to ensure that znodes have less than 1M of data, but the data should be much less than that on average.
Additionally, Znodes maintain the versions of stored data and a new version number is created each time a client updates the data in a Znode. For instance, whenever a client retrieves data, it also receives the version of the data. And when a client performs an update or a delete, it must supply the version of the data of the znode it is changing. If the version it supplies doesn't match the actual version of the data, the update will fail. However this behavior can be overridden by passing in the version as -1 while writing the data to a Znode.
In order to start working with ZooKeeper, we would need to connect to ZooKeeper service. Here is the code to connect to ZooKeeper service using the minimum arguments constructor.
final ZooKeeper zooKeeper = new ZooKeeper("192.168.111.130:2181,192.168.111.132:2181", 2000, new ZooKeeperWatcher());
First argument to constructor is the comma separate list of host:pair of ZooKeeper nodes. Second constructor argument is session timeout in milliseconds. Third and last argument is the reference to ZooKeeper Watcher Implementation reference. In the above case, a new instance of class ZooKeeperWatcher is passed in and here is how ZooKeeperWatcher inner class looks like -
public class ZooKeeperWatcher implements Watcher {
public void process(WatchedEvent event) {
//process the event
}
}
Once we have got the ZooKeeper service instance, we can now proceed to perform the various znode operations such as creating and deleting a znode. Here is the code to create and delete an ephemeral node with path "/demo"
//create a node with path "/demo", data as blank, ACL giving everyone rights on this node and create mode as Ephemeral
final String createdNodePath = zooKeeper.create("/demo", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
//Delete a node with path "/demo" and version as -1 means don't care for current version
zooKeeper.delete(createdNodePath, -1);
package com.sts.allprogtutorials.apachezookeeper.main;
import java.io.IOException;
import org.apache.log4j.Logger;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
public class ZooKeeperDemo {
private static final Logger LOG = Logger.getLogger(ZooKeeperDemo.class);
public static void main(String[] args) throws IOException {
final ZooKeeper zooKeeper = new ZooKeeper("192.168.111.144:2181", 2000, new ZooKeeperWatcher());
try {
//create a node with path "/demo", data as blank, ACL giving everyone rights on this node and create mode as Ephemeral
final String createdNodePath = zooKeeper.create("/demo", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
LOG.info("Node created with path: " + createdNodePath);
//Delete a node with path "/demo" and version as -1 means don't care for current version
zooKeeper.delete(createdNodePath, -1);
} catch (KeeperException | InterruptedException e) {
e.printStackTrace();
}
}
public static class ZooKeeperWatcher implements Watcher {
public void process(WatchedEvent event) {
//process the event
}
}
}
Thank you for reading through the tutorial. In case of any feedback/questions/concerns, you can communicate same to us through your comments and we shall get back to you as soon as possible.