Author: HelloGitHub- Lao Xun
Hello, here is HelloGitHub’s HelloZooKeeper series, free, open source, fun, entry-level ZooKeeper tutorials for beginners with basic programming skills.
ZooKeeper is a software project of the Apache Software Foundation that provides open source distributed configuration services, synchronization services, and naming registries for large-scale distributed computing. ZooKeeper was once a subproject of Hadoop, but is now a top independent open source project.
ZK is often used to see in the actual development work, which is the guy to eat, that can be played through, with the right hand, or how to advance and promotion pay? Learn how to use HelloGitHub
This series of tutorials is to explain ZooKeeper from scratch, from the most basic installation and use to the principle and source code behind the explanation, the whole series hope to let ZK knowledge “drill” into your smart brain through interesting text, humorous atmosphere. This tutorial is open: open source and collaborative, so whether you’re a novice or veteran driver, we hope you can contribute to make this tutorial even better:
- Novice: participate in the revision of the wrong words, sentences, spelling, typesetting and other problems in the article
- Users: Participate in content discussions, answering questions, and helping others
- Old driver: Get involved in writing the article and put your name in the author column
Project address: github.com/HelloGitHub…
Today we will explain how to use the Java code client to manipulate ZK.
First, basic operation
1.1 New regulations of Magoguo
As always, before we get into the actual combat, I will tell you a short story (the characters in the story are completely fictional, please do not compare them, if there is any similarity, it is pure coincidence).
Since Ma Guoguo served as the head of the office, every day that is busy, the villagers have something to come to him, his little book has been densely recorded a lot of:
Especially chicken too the United States, more peremptory has become the day UP, daily updated frequently make the horse blended feeling overwhelmed, he thought, if such a hash of it, not only oneself will be more and more tired, later after their retirement, somebody else to handover will not start, that also not behind my mismanagement, pointing to me. I’m afraid I’ll have to apologize to the whole village.
Therefore, the office issued a new regulation, each time the villagers who came to register must classify their affairs to be registered, and Ma Guoguo recorded them according to these classifications, so Ma Guoguo’s notes (hereinafter referred to as: little red book) became like this:
But after implementing the rules for some time, the villagers’ representatives, led by Jitaimei and Ma Xiaoyun, proposed to Ma Guoguo: “We are old acquaintances, and every time we come to visit, we have to announce our home. Can we do something to make it easier for the people? Isn’t that the purpose of your office to serve our people?”
Ma Guo Guo listened to also feel very reasonable, so to each old acquaintance have created a label. For example, in the future, records created by Jitaimei were directly placed under the label Jitaimei, so that jitaimei only needs to care about what they want to record, so the notebook ended up like this:
It is also the same for receiving notification to villagers. Magoguo will note next to the event that needs notification. For example, Kun Kun, the number one fan of Jitaimei, is very interested in the dancing video of Jitaimei, so he will note next to the dancing event of Jitaimei:
Then take out another Yellow Book and write down who needs to be notified:
With the passage of time, ji Tai Beauty’s popularity is increasing, and now even Ma Xiaoyun, Dongdong have become her fans, have to pay attention to her updates:
So now when Ma Guoguo records the little red book, he will see if other people subscribe to the notice of the current affairs. If so, he will take out the little Yellow Book to find the corresponding villagers who need to be notified, and call them one by one.
Ma Guoguo is very satisfied with his regulations this time, when facing the reporter interview, proud of the said, this is his retirement persistence in learning the computer, from the computer file directory to get inspiration, people really want to “live and learn” ah!
This is the end of the story. Here is the translation in ape language:
ZK defines that each record must have a corresponding path, which is the classification specified by the office in the corresponding story. The structure of the whole record is indeed similar to the file tree in Linux, with a root node/and parent-child relationship between nodes. The path is divided by /, for example:
/ Chicken too beautiful/update video/dance /20201101Copy the code
The tag in the story, which is actually the chroot specified by the client, is actually maintained by the client without the server knowing it.
1.2 Code Practice
In particular, the next step is to use the official Java client as a demonstration, create a new empty Maven project, and introduce ZK dependencies:
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.6.2</version>
</dependency>
Copy the code
To manipulate ZK, you need to create a client object. Let’s take Chicken Taimei as an example
ZooKeeper client = new ZooKeeper("127.0.0.1:2181/ Chicken is beautiful".3000.null);
Copy the code
The first string of ZooKeeper is the server address of the connection, followed by chroot, which is the label in the small story. After that, all operations of the client will be handled with/jicaimei as the top-level path.
Finally, when the client exits, remember to close the client
client.close();
Copy the code
1.2.1 Creating a Path
Note that the official client does not have recursive creation, so when creating a multilevel path, the client needs to ensure that the parent node in the path exists!
Unsafe. zoodefs.ids. OPEN_ACL_UNSAFE is a type of ACL permission that does not need to be checked. And we’ll talk about it later, so I’ll skip it.
client.create("/ Update video/Dance /20201101"."This is Data, you can either record some business Data or you can write whatever you want.".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
Copy the code
The final createmode. PERSISTENT indicates that the current node is of a PERSISTENT type. There are 7 types in 3.6.2, which are listed below and explained briefly:
PERSISTENT // A persistent node, once created, will not be deleted unless the client initiates a deletion request
PERSISTENT_SEQUENTIAL // A persistent sequential node, which concatenates a non-repeating prefix suffix after the user path, as above
EPHEMERAL // A temporary node that is automatically deleted when the client that created the node is disconnected
EPHEMERAL_SEQUENTIAL // Temporary sequence nodes, basically the same as above, also add a numeric suffix
CONTAINER // The container node is deleted by the server once the child node has been deleted
PERSISTENT_WITH_TTL // A persistent node with an expiration time, a node with a timeout period, will be deleted if no child nodes are created within the timeout period
PERSISTENT_SEQUENTIAL_WITH_TTL // Persistent sequential nodes with expiration time, basically the same as above, with a numeric suffix
Copy the code
You may be familiar with the first four types, but are not familiar with the last three types, especially the last two types with TTL, these two types are not supported by default ZK configuration, need to add extendedTypesEnabled=true to zoo. CFG configuration to enable the extension function. Otherwise you will receive an Unimplemented for error.
In the example, after the path is created, it looks like this:
Chicken is beautiful | - update video dance | -- - | - 20201101Copy the code
1.2.2 Deleting a Path
The official client also does not support recursive deletion, so you need to ensure that the node to be deleted is a leaf node, otherwise you will receive an error, so we delete 20201101 here:
client.delete("/ Update video/Dance /20201101", -1);
Copy the code
-1 is a version field, which is equivalent to the optimistic locking mechanism provided by ZK. If it is -1, the version information of the node is ignored.
This is what happens when you delete:
Chicken is beautiful | - update video | - dancingCopy the code
1.2.3 Setting Data
Each node can have its own data, which can be specified at creation time or later through Settings.
client.setData("/ Update video/Dance"."This is Data, I can write some parameters about the business.".getBytes(), -1);
Copy the code
The meaning of -1 is the same as that in the deletion path, but the version information is ignored.
1.2.4 Checking whether a path exists
Since neither create nor delete supports recursion, the existence of the target path needs to be checked to decide whether to proceed to the next step
Stat stat = client.exists("/ Update video".false); System.out.println(stat ! =null ? "There" : "Doesn't exist."); / / there
Copy the code
False means do not subscribe, about subscription will be said later.
1.2.5 Obtaining Data
If you can set data, you can also get data, so ZK can play the role of data store occasionally
byte[] data = client.getData("/ Update video/Dance".false.null);
System.out.println(new String(data)); // This is Data, you can write some parameters about the business
Copy the code
1.2.6 Obtaining the Child Node List
ZK is a tree structure with parent-child nodes, so you can query all the children of a node
List<String> children = client.getChildren("/ Update video".false);
System.out.println(children); // [dancing]
Copy the code
1.2.7 Setting a Subscription
All three methods (including their overloaded methods) can subscribe to a path in one of two ways: check the existence of a path, get data, and get a list of child nodes.
- Pass a
boolean
Value, in which case the callback object is createdZooKeeper
The third parameter ofdefaultWatcher
, except in the previous examplenull
- Pass one directly into the method
Watcher
The implementation class is used as a callback object after this path (recommended)
The following are demonstrated respectively:
1 / / way
ZooKeeper client = new ZooKeeper("127.0.0.1:2181/ Chicken is beautiful".3000.new Watcher() {
// This is the defaultWatcher parameter, which is the current client's default callback implementation
@Override
public void process(WatchedEvent event) {
System.out.println("This is the default callback object for this client globally."); }});// exists
client.exists("/ Update video".true);
// getData
client.getData("/ Update video/Dance".true.null);
// getChildren
client.getChildren("/ Update video".true);
Copy the code
2 / / way
// exists
client.exists("/ Update video".new Watcher() {
@Override
public void process(WatchedEvent event) {
System.out.println("I'm the implementation of the callback object."); }});// getData
client.getData("/ Update video/Dance".new Watcher() {
@Override
public void process(WatchedEvent event) {
System.out.println("I'm the implementation of the callback object."); }},null);
// getChildren
client.getChildren("/ Update video".new Watcher() {
@Override
public void process(WatchedEvent event) {
System.out.println("I'm the implementation of the callback object."); }});Copy the code
How the callback triggers the corresponding method each time is a mystery here and will be explained in more detail later.
About ZK client so roughly several operation, limited to the space I can’t one by one, for example, the purpose of this article series is not an official document translation, important is to inspire enthusiasm for technology, the rest of those who use it when I gave all the exercises after class ~ about ZK’s basic operation is finished.
Second, advanced operation
Once we’re done with the basics, let’s move on to something more advanced.
2.1 Chicken Beauty book signing
Let’s continue with the story of animal Village.
With the growing popularity and attention of live broadcast, Jitaimei has become a big star in animal Village, and has released an album, so I plan to hold a book signing in return for the fans.
Decided to decorate the site in front of the office of Ma Guoguo, by the strong tai Chi master Ma Guoguo as security to ensure the order of the scene
Ma Guoguo said that if you want to go in and meet one-to-one fans of Chicken Taimei, you need to take away the voucher in my hand, come out quickly after signing, and return the voucher to me.
Sorry, I put the wrong picture
Chicken too beautiful fans heard can meet one on one with the star, are crazy, are in a hurry to the door of the office, do not know who shouted in the crowd: “rob ah! First come, first served!” , like a hungry tiger pounced on the general Ma Guo Guo to the ground, the scene is quite chaotic!
Finally, the chicken is too beautiful hardcore fan Kun Kun took the lead, grabbed the only certificate in the hands of Ma Guoguo, in exchange for the opportunity to one on one with the star idol
Kun Kun holding the center of love album, satisfied to go back.
Ma Guoguo, who took back the voucher, looked at the group of hungry wolves
Suddenly I knew what I had to face next…
(After 4 hours)
Finally, all the fans took the album in hand is still warm and happy to go home. Busy a day of ma Guoguo thought next time can not be so, if it were not for my old and healthy fear is not to be carried into the hospital!
2.2 The Chicken is Too Beautiful concert
The number of fans has finally surpassed 100 million. It was time to find a reason to market themselves again, so I discussed with the agency whether I could hold a concert and sell tickets on the spot, which could not only create publicity for myself but also meet the requirements of the fans. This time the same found Ma Guogo, I hope ma Guogo can continue to help organize the order of the scene, the ma Guogo did not want to take these chores, but after hearing the price of the brokerage company…
However, he was too old to withstand the last time, so he decided to issue a new rule to deal with the crazy behavior of fans. The new venue was arranged like this:
Every fans have to pick up a the horse blended from small to large, the number of the blended each time starting from 1, hair also call, from the smallest number to call, a call to the number of fans before the ticket office to buy tickets, will focus on discharge after every fans to get the number one in front of his situation, if he bought, oneself hurriedly to prepare, Because it’s gonna be your turn next.
In this way, the whole sale scene is in perfect order, everyone praises Ma Guoguo’s superb management skills in succession.
The little story is finished again, and the following is translated in ape language:
These two stories tell the general principle of ZK distributed lock, and basically correspond to the two cases of unfair lock and fair lock, although the actual situation and the story will be different, but through the story hope to give you a perceptual understanding.
The disadvantage of not fair lock and embodied in the story, is a current and holding the lock release process, after all the process of waiting for the lock will be notified, this is often mentioned in the interview question “flock effect”, thus to compete for the lock, but because only one process can get the lock, other processes will continue to wait for again, Therefore, in the case of dealing with high concurrency scenarios, this scheme has serious performance problems and greatly increases the pressure on the server side.
In the case of fair lock, each process that does not acquire the lock usually only needs to care about the situation of the previous process, and only one process will be awakened at a time. Therefore, if ZK is used as the middleware of distributed lock, it is recommended to adopt fair lock.
2.3 Actual Code
Below is a simple (pseudo) code demonstration, how to use ZK to write distributed lock logic
2.3.1 Unfair Lock
Let’s say the object I want to lock now is the concert of Chicken Taimei
ZooKeeper client = new ZooKeeper("127.0.0.1:2181".3000.null);
try {
// It was mentioned earlier that you must ensure that the chicken too beautiful path exists
client.create("/ The Chicken is beautiful/Concert"."Data is not written casually.".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
// Create the lock successfully, then execute the business logic
System.out.println("I'm the business logic after I get the lock."); .// Delete the node after processing services to release the lock. In actual scenarios, this step should be deleted in the finally block
client.delete("/ The Chicken is beautiful/Concert", -1);
} catch (KeeperException.NodeExistsException e) {
// If NodeExistsException is reported, the lock is not obtained
System.out.println("The lock was taken by someone else.");
// Listen on this node
client.exists("/ The Chicken is beautiful/Concert".new Watcher() {
@Override
public void process(WatchedEvent event) {
if (event.getType().equals(Event.EventType.NodeDeleted)) {
// If the delete event is detected, the previous process released the lock
// This refers to the recursive process of listening for the refetch failure. In fact, it requires a wrapped method similar to the lock method, which will not be demonstrated here
client.create("/ The Chicken is beautiful/Concert"."Data is not written casually.".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); . }}}); }...Copy the code
ZK’s unfair lock uses the same path and cannot be repeatedly created, plus the feature of temporary nodes. Temporary nodes are used because if the process that acquired the lock crashes and cannot release the lock in time, it will cause a deadlock. However, the temporary node will be deleted automatically after the client is disconnected, so it avoids the risk of deadlock.
2.3.2 fair lock
Same concert, this time switch to fair lock to try
ZooKeeper client = new ZooKeeper("127.0.0.1:2181".3000.null);
String currentPath = client.create("/ The Chicken is beautiful/Concert"."Data is not written casually.".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
// The creation will be successful because the order number exists
// Get the names of all the children under the parent node
List<String> children = client.getChildren("/ The chicken is beautiful.".false);
/ / first order
Collections.sort(children);
if (children.get(0).equals(currentPath)) {
// The current path is the smallest node, obtaining the lock successfully
System.out.println("I'm the business logic after I get the lock."); .// Also remember to delete this node after processing services
client.delete(currentPath, -1);
} else {
// Not the smallest node, failed to obtain the lock
// Get the node whose ordinal number is 1 less than your own among all children based on the current node path
String preNode = getPreNode(currentPath, children);
// Listen on the node
client.exists(preNode, new Watcher() {
@Override
public void process(WatchedEvent event) {
if (event.getType().equals(Event.EventType.NodeDeleted)) {
// As with an unfair lock, try to acquire the lock again, because of the order of the nodes, so the lock should not fail this time. }}}); }...Copy the code
ZK’s fair lock uses the temporary sequence node, the sequence number cannot be repeated, the current smallest node is considered to have obtained the lock successfully.
2.3.3 Curator for Recipes
You might ask, well, neither of these pieces of code works directly, so what if I want to use them in my project?
This kind of live open source community has already been done for us
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>5.1.0</version>
</dependency>
Copy the code
Here’s a simple example
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000.3);
CuratorFramework client = CuratorFrameworkFactory.newClient("127.0.0.1:2181", retryPolicy);
client.start();
InterProcessMutex lock = new InterProcessMutex(client, "/lock");
try (Locker locker = new Locker(lock)) {
// Use the try-with-resources syntax sugar to automatically release the lock
System.out.println("Business logic after obtaining the lock");
}
client.close();
Copy the code
Exhibit Curator has several built-in locks for us to use, and all work with Locker packaging
InterProcessMultiLock
Multiple paths can be locked and released simultaneouslyInterProcessMutex
Reentrant exclusive lockInterProcessReadWriteLock
Read-write lockInterProcessSemaphoreMutex
Do not reenter exclusive locks
If you want to see how to write a great ZK distributed lock, check out the source code for co-recipes
Exhibit exhibit exhibit exhibit exhibit exhibit exhibit Exhibit exhibit exhibit exhibit exhibit exhibit exhibit exhibit exhibit exhibit exhibit exhibit exhibit exhibit exhibit exhibit exhibit exhibit exhibit exhibit exhibit exhibit exhibit exhibit
2.3.4 Integration with Spring Boot
Is there an easier one than this? Of course!
I have a demonstration project ready for you:
- Project address: github.com/HelloGitHub…
Note that this project is only intended to demonstrate how to integrate toth into Spring Boot, everything is configured in a simple way, and only demonstrates distributed locking. Other advanced features are available if you need them!
I’m not going to show you the regular Spring Boot dependencies, I’m just going to list the ones that are associated with that:
The project uses Maven as a dependency management tool
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-zookeeper</artifactId>
</dependency>
Copy the code
An @Configuration object to create the corresponding Bean
@Configuration
public class ZookeeperLockConfiguration {
@ Value (" ${zookeeper. Host: 127.0.0.1:2181} ")
private String zkUrl;
@Bean
public CuratorFrameworkFactoryBean curatorFrameworkFactoryBean(a) {
return new CuratorFrameworkFactoryBean(zkUrl);
}
@Bean
public ZookeeperLockRegistry zookeeperLockRegistry(CuratorFramework curatorFramework) {
return new ZookeeperLockRegistry(curatorFramework, "/HG-lock"); }}Copy the code
Two interfaces for testing
// Inject directly into the bean where the lock is needed
@Resource
private LockRegistry lockRegistry;
@GetMapping("/lock10")
public String lock10(a) {
System.out.println("lock10 start " + System.currentTimeMillis());
final Lock lock = lockRegistry.obtain("lock");
try {
lock.lock();
System.out.println("lock10 get lock success " + System.currentTimeMillis());
TimeUnit.SECONDS.sleep(10);
} catch (Exception e) {
} finally {
lock.unlock();
}
return "OK";
}
@GetMapping("/immediate")
public String immediate(a) {
System.out.println("immediate start " + System.currentTimeMillis());
final Lock lock = lockRegistry.obtain("lock");
try {
lock.lock();
System.out.println("immediate get lock success " + System.currentTimeMillis());
} finally {
lock.unlock();
}
return "immediate return";
}
Copy the code
To explain the logic, I call the LOCK10 interface first and then the IMMEDIATE interface. The lock10 interface will sleep for 10 seconds after acquiring the lock, and the immediate interface will attempt to acquire the lock, but will not sleep. Assuming the distributed lock is valid, the immediate interface will wait for 10 seconds. However, the time of obtaining the lock was about 10 seconds different, proving that the lock was valid. The obtrain method string parameter of lockRegistry is the corresponding business scenario, such as: order number, user ID, etc. If the string is the same, it can be considered as the same lock.
If you need to test a distributed environment, you just need to change the startup port in the configuration file to start multiple processes to simulate a distributed environment
lock10 start 1607417328823
lock10 get lock success 1607417328855
immediate start 1607417329943
immediate get lock success 1607417338872
Copy the code
While I was sleeping, I went to ZK and found that the framework would create two temporary nodes under the specified node to control concurrency, similar to what we demonstrated earlier, but the details are left up to you as a reader to dig.
/
|--zookeeper
|--HG-lock
|--lock
|--_c_41d75f28-2346-4cf2-89e8-accccce9ad1a-lock-0000000000
|--_c_98549447-0ee4-4c93-8194-4ed428225f75-lock-0000000001
Copy the code
For more details on the example (which is not very detailed, it’s a very simple project), visit the project address above to see the source code.
Third, summary
This article uses the story and actual combat to explain the basic operation of ZK and part of the advanced operation. In the next article, I’ll get into the Principles, and I’ll show you how the ZK server handles each request.
Follow the HelloGitHub public account to receive the first updates.
There are more open source projects and treasure projects waiting to be discovered.