In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-02-24 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Servers >
Share
Shulou(Shulou.com)06/02 Report--
In "distributed sharp weapon Zookeeper (1)", we give a preliminary introduction to ZK and build a ZK cluster environment. The topic of this blog will be: operating ZK,Watch mechanism based on native API, discussion of distributed locking and so on.
Native API operation ZK what is native API operation ZK? In fact, using zookeeper.jar is based on the native API way to operate ZK, because this native API is not very comfortable to use, so the emergence of zkclient, and later based on the Curator framework, making it more convenient for people to use ZK. There is a saying, Guava is to JAVA what Curator is to Zookeeper.
Description:
When initializing Zookeeper, there are several construction methods to choose from, and three parameters are required: connectionString (separated by multiple ZK SERVER), sessionTimeout (that is, tickTime in zoo.cfg), and Watcher (event handler notifier).
It is important to note that the connection to ZK is asynchronous, so we need CountDownLatch to help us ensure that ZK initialization is complete.
For events (WatchedEvent), there are states as well as types.
Next, let's take a look at the additions, deletions, modifications and queries based on native API:
Note that there are two types of nodes, persistent nodes and temporary nodes. On this basis, it can be divided into persistence sequence node (PERSISTENT_SEQUENTIAL) and temporary sequence node (EPHEMERAL_SEQUENTIAL).
Node types only support byte [], which means we can't give an object directly to ZK and let ZK help us with the serialization operation!
It should be noted here that native API operations on ZK are actually divided into synchronous and asynchronous modes.
Rc stands for return code, which is the error code, and 0 is normal.
Path is the parameter passed in API, and ctx is also passed in.
Note that version checking is required during deletion, so we generally provide a-1 skip version checking mechanism.
Watch mechanism
ZK has a watch event, which is triggered at once. When the data monitored by watch changes, the client that has the monitoring set, namely watcher, will be notified. Zookeeper's watch has some features of its own:
Disposable: remember, just watch one time! Because ZK monitoring is one-time, monitoring must be set up each time.
Lightweight: WatchedEvent is the smallest unit for ZK to notify watch. The whole data structure includes: event state, event type, node path. Note that ZK only notifies the client node that the data has changed and does not directly provide specific data content.
Client-side serial execution mechanism: note that the process of client-side watch callback is a serial synchronous process, which ensures the order for us, and we should also be aware that the entire client-side watch callback can not be affected by the callback processing logic of one watch.
Let's look directly at the code:
It is important to note that monitoring the node and monitoring the node's children are two things.
For example, exists (path,true) monitors the create/delete/setData;getChildren (path,watcher) of the path node and monitors the changes of the child nodes under the path node (the creation, modification and deletion of child nodes are all monitored, and the event types are all the same. Think about how to distinguish them? Give me an idea, that is, we need to first have a list of child nodes under the path, and then after the watch is triggered, we can compare the SIZE size and content of the child nodes under the path to know which child nodes are added and which child nodes are deleted! )
What is the difference between getChildren (path,true) and getChildren (path,watcher)? The former follows the Watcher in the context, while the latter can set a new Watcher! (therefore, if you want to monitor all the time, there are two ways, one is to set it to true each time, or simply to set a new Watcher at a time.)
From the above discussion, you can probably understand that the native API is not very powerful, some of which we have to worry about. When we introduce the Curator framework later, there will be a better way to deal with it.
Distributed locking idea
First of all, instead of talking about how Zookeeper helps us deal with distributed locks, let's think about what distributed locks are. Why do you need distributed locks? What are the scenes? What attention should be paid to the use of distributed locks? What are the characteristics of distributed locks?
Speaking of locks, we naturally think of the synchronized/Lock that Java provides for us, but this is obviously not enough, because it can only target operations on shared resources by multiple threads in a JVM. Then for multiple machines, if multiple processes operate on the same kind of resources, it is the so-called lock in the distributed scenario.
The "second kill" activities often carried out by various e-commerce platforms need to protect the inventory of goods, and 12306 train tickets can not be sold more, let alone allow a ticket to be bought by multiple individuals, such a scene requires distributed locks to protect shared resources!
Now that there is nothing Java can do about locking in a distributed scenario, we have to rely on something else!
Our old friend: DB
Yes, that's right. Can we do it with the help of DB? You should know that DB has some features for us to take advantage of, such as DB itself has a locking mechanism (table locks, row locks), unique constraints, and so on.
Suppose we have a table T (id,methodname,ip,threadname,.) in our DB, where id is the primary key and methodname is the unique index.
For multiple machines, for multiple threads on each machine, select whether the record method exists in the T table before operating on a method method, and if not, insert a record into T. Of course, it is possible to concurrency select, but due to the only constraints of the T table, only one request can be inserted successfully, that is, the lock is obtained. As for releasing the lock, you can delete the record after the method has been executed.
Consider some questions: what if DB dies? If the delete is not executed successfully for some reason, then this record will result in that the method can no longer be accessed! Why select first, why not just insert? How is the performance?
In order to avoid a single point, we can switch between master and standby; in order to avoid deadlock, we can have a scheduled task to clean up the records in the T table regularly; select before insert is actually to ensure the reentrant of the lock, that is, if a thread on an IP acquires the lock, it can continue to acquire the lock without releasing the lock. In terms of performance, if there are a large number of requests, it will test the DB, which will become a bottleneck.
At this point, there is an obvious problem that we need to consider: although the above scheme ensures that only one request will acquire the lock, the other requests fail to obtain the lock and return instead of waiting for the lock! Of course, we can implement blocking locks by retrying the mechanism, but the locking mechanism of the database itself can help us do so. Don't forget select... For update is a blocking row locking mechanism, and commit releases the lock. And for for update this exclusive lock, if you do not submit the release for a long time, it will always occupy the DB connection, the connection burst, kneel!
Needless to say, old friends can only help us so far!
Our new friend: Redis or other distributed caches (Tair/...)
Since it is caching, it has better performance than DB, and since it is distributed, it certainly avoids the single point problem.
For example, using Redis as the setnx of distributed locks, I won't go into details here. In short, distributed caches need to pay special attention to cache expiration time. (the valid time is too short, maybe the lock will be released before the business is finished; the valid time is too long, and other threads wait in vain, wasting time and slowing down the processing speed of the system.)
See how Zookeeper helps us implement distributed locks.
Characteristics of temporary sequential nodes in Zookeeper:
First, the life cycle of the node is bound to the client reply, that is, once the client reply that creates the node expires, then the node will be deleted. (temporary)
Second, each parent node maintains the order in which the child nodes are created, automatically assigns a × × value to the child nodes, and the suffix is automatically appended to the node name as the final node name of the node. (sequential)
Then, based on the characteristics of temporary sequential nodes, the general idea for Zookeeper to implement distributed locks is as follows:
1.client calls the create () method to create a "/ root/lock_" node. Note that the node type is EPHEMERAL_SEQUENTIAL.
2.client calls getChildren ("/ root/lock_", watch) to get all the child nodes that have been created and registers the Watcher of the child node change notification on this node at the same time
3. After obtaining all the child nodes Path, the client is considered to have acquired the lock if it finds that the node it created in step 1 is the smallest of all nodes.
4. If in step 3, it is found that it is not the smallest, then wait until the next child node change notification, and the child node is acquired to determine whether the lock is acquired or not.
5. It is also easier to release the lock by deleting the node you created.
In the above way of thinking, in the case of a large cluster, there will be a "herding effect" (Herd Effect):
One detail in the competition for distributed locks above is that the child node change notification Watcher is registered on getChildren. Is there a problem with this? In fact, this will lead to a large number of repeated runs of the client, and the vast majority of the running results are to judge that they are not the node with the lowest sequence number, thus continuing to wait for the next notification, that is, many clients have done a lot of useless work. What's more, in the case of a large cluster, this obviously affects the performance of Server, and once multiple clients are disconnected at the same time, the server will send a large number of event notifications to the rest of the clients, which is the so-called herding effect!
The root cause of this problem is that the above ideas do not find the "pain point" of the client:
The core demand of the client is to determine whether it is the smallest node, so the creator of each node does not care about all the node changes, what it really cares about is whether the node with a smaller serial number exists!
1.client calls the create () method to create a "/ root/lock_" node. Note that the node type is EPHEMERAL_SEQUENTIAL.
2.client calls getChildren ("/ root/lock_", false) to get all the created child nodes. No Watcher is registered here.
3. After obtaining all the child nodes Path, the client is considered to have acquired the lock if it finds that the node it created in step 1 is the smallest of all nodes.
4. If you find that it is not the smallest in step 3, find the node that is smaller than you, and then call the exist () method to register the event listener on it.
5. Once the concerned node is removed, the client will receive a notification, and the client needs to call getChildren ("/ root/lock_", false) again to ensure that it is the smallest node, and then proceed to step 3.
OK,talk is cheap show me the code, the next article will bring you the code for Zookeeper to implement distributed locks.
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.