preface

As for distributed lock, there are many application scenarios in the Internet industry, such as inventory deduction of e-commerce, seckilling activities, cluster timed task execution and other scenarios requiring mutually exclusive processes. And the means to achieve distributed lock are also many, we are more common is Redis and ZooKeeper, today we mainly introduce the distributed lock based on ZooKeeper.

This article is mainly to borrow Curator framework implementation approach of zk distributed lock, everyone understand the later can fully manual to realize again, but in the work or recommend the use of mature open source framework, others have helped us a lot of pit stepping well, unless as a last resort, needs highly customized meeting the needs of your project, started their own packaging.

The body of the

This section describes ZooKeeper

Since it is based on ZooKeeper distributed lock, first of all must have a certain understanding of this ZooKeeper, here is not much to explain, only with distributed lock associated with a simple introduction, more detailed features you can refer to the official documentation.

Zookeeper maintains a file system-like data structure with four types of nodes

  • PERSISTENT: PERSISTENT nodes. Once created, the node still exists even if the client is disconnected from zK.
  • PERSISTENT_SEQUENTIAL: Persistent sequential numbered node. More than PERSISTENT nodes nodes are automatically numbered sequentially.
  • EPHEMERAL: the temporary node. When the client disconnects from ZK, the node is removed.
  • EPHEMERAL_SEQUENTIAL: temporary sequential node. More than EPHEMERAL nodes are automatically numbered sequentially. (Distributed lock implementation uses this node type)
Toth implements distributed locking principle

Ok, when we simply understand the node type of ZK, now we formally analyze the realization principle of distributed lock for Curator. Here we define a “/curator_lock” lock node to hold temporary sequential nodes created by related clients.

If two clients, ClientA and ClientB, are competing for a lock at the same time, and ClientA wins the lock first, it will create a temporary order node of “/curator_lock/ XXX-0000000000” on our ZK.

It then holds the lock on all the children of the “/curator_lock/” node. Since these nodes are ordered, it determines whether the node it created is the first in order. ClientA is the first client that created the node, so it holds the lock.

[zk: localhost:2182(CONNECTED) 4] ls /curator_lock
[_c_f3f38067-8bff-47ef-9628-e638cfaad77e-lock-0000000000]
Copy the code

ClientB follows the same steps, first creating a temporary sequential node “/curator_lock/ XXX-0000000001” under “/curator_lock/” and then obtaining all the child nodes under that node. And check whether the node serial number generated by ClientA is the smallest. Since the node with the smallest serial number is created by ClientA and has not been released, ClientB cannot get the lock by itself.

[zk: localhost:2182(CONNECTED) 4] ls /curator_lock
[_c_2a8198e4-2039-4a3c-8606-39c65790d637-lock-0000000001,
_c_f3f38067-8bff-47ef-9628-e638cfaad77e-lock-0000000000]

Copy the code

Since ClientB can’t get the lock and won’t give it up, it adds a listener to its previous node (the API implementation provided by ZK), and as soon as it listens for the previous node to be deleted, that is, the lock is released, it will immediately re-acquire the lock.

When the following ClientC, ClientD… The same is true when it comes in, only the number on the node changes, which increases according to the number of Client connections.

You may also worry about what happens if the client that got the lock goes down. Will it not release the lock? In fact, this problem has been solved above. Because the distributed lock is implemented by temporary sequential node, when the client disconnects from ZK, the node will disappear, which is equivalent to releasing the lock.

The code below shows the basic usage of Curator. This is only a reference example, so don’t use it casually in a production environment.

CuratorFramework client = CuratorFrameworkFactory.newClient("127.0.0.1:2182".5000.10000.new ExponentialBackoffRetry(1000.3));
        client.start();
        InterProcessMutex interProcessMutex = new InterProcessMutex(client, "/curator_lock");
        / / lock
        interProcessMutex.acquire();
        
        // Business logic
        
        / / releases the lock
        interProcessMutex.release();
        client.close();
    
Copy the code

conclusion

Once we understand the principle, we can abandon the Curator and implement a distributed lock ourselves. We believe that everyone can implement basic functions well, but to achieve production level, we may need to work on details, such as exception handling, performance optimization and so on.