ZooKeeper distributed lock simple practice

The realization principle of ZooKeeper distributed lock

Simple practice of ZooKeeper distributed lock} else {
System.out.println("Thread:" + Thread.currentThread ().getName() + ", Please try again if the purchase timeout failed:" + System.currentTimeMillis());
}
Thread.sleep(1000);
} catch (Exception e ) {

} finally {
zkLock.unLock();
}
}

public static void main(String[] args) {
System.out.println("zk distributed The lock begins. . ");
for (int i = 0; i <100; i++) {
new Thread(new ZkLockTest()).start();
}
}

}
Simulate 100 threads to compete for locks at the same time. Of course, 100 threads will not be started at the same time. If necessary, it can be controlled in the form of semaphores.

Secondly, Write a lock interface

public interface ZkLock {

// Acquire a lock
Boolean getLock(Long acquireTimeout,Long endTime);

/ / Release the lock
void unLock();

}
Here I define two interfaces, which correspond to acquiring and releasing the lock.

In acquiring the lock There are two parameters, the meanings are the lock timeout time and the final calculated timeout time. See the code below for details.

public class ZkDistributedLock implements ZkLock {
// Cluster connection address
private String CONNECTION = "127.0.0.1:2181";
// zk client connection
private ZkClient zkClient = new ZkClient(CONNECTION);
// path path
private String lockPath = "/ lock";
private CountDownLatch countDownLatch;
//Request to set the timeout time: acquireTimeout milliseconds. The final timeout time endTime
public Boolean getLock(Long acquireTimeout,Long endTime) {
Boolean lock = false;
if (endTime == null) {
//Wait timeout time
endTime = System.currentTimeMillis() + acquireTimeout;
}
if (tryLock()) {
System .out.println("####Get Get lock successful######");
lock = true;
} else {
if (waitLock(endTime)) {
if (getLock(null,endTime)) {< br>lock = true;
}
}
}
return lock;
}

public void unLock() {
if (zkClient != null) {
System.out.println("#######release lock#########");
zkClient.close();
}
}

private boolean tryLock() {
try {
zkClient.createEphemeral(lockPath);
return true;
} catch ( Exception e) {

return false;
}

}

private Boolean waitLock(Long endTime) {
/ / System.out.println("Enter waiting");
// Use zk temporary event listener
IZkDataListener iZkDataListener = null;
try {
// use zk temporary event listener
iZkDataListener = new IZkDataListener() {

public void handleDataDeleted(String path) throws Exception {
if (countDownLatch != null) {
countDownLatch.countDown() ;
}
}
public void handleDataChange(String arg0, Object arg1) throws Exception {

}
};
// Register event notification
zkClient.subscribeDataChanges(lockPath, iZkDataListener);
if (System.currentTimeMillis() if (zkClient.exists(lockPath)) {
countDownLatch = new CountDownLatch(1) ;
try {
countDownLatch.await();
return true;
} catch (Exception e) {

}
} else {
return true;
}
} else {
System.out.println("Timeout return");
}
} catch (Exception e ) {

} finally {
// After listening, remove the event notification
zkClient.unsubscribeDataChanges(lockPath, iZkDataListener);
}
return false;
}

}
This class is my real The core class of zk lock is similar to the above schematic diagram. First, the user needs to acquire the lock when requesting the lock. The first user who competes for the lock executes the relevant logic and releases the lock. If the program fails to disconnect during this process, the node will automatically delete the release lock because of the temporary node.

Other users who failed to compete for the lock, I set a certain waiting time here. When the original lock is released within the time, you can still get the lock again. Here I want to talk about the monitoring of lock release. In native zookeeper, you need to register each time you use the watcher, and you need to register once when you use it. In zkClient, there is no need to register a watcher, but the concept of a listener is introduced, that is, as long as the client registers a listener in a certain node, as long as the server changes, it will notify the client currently registered with the listener. What I use here is IZkDataListener. This class is an interface provided by zkClient. It can be triggered when the data content or version of the current node changes or the current node is deleted.

After the trigger is triggered, we can regain the lock. When the competition fails again and enters the waiting, it will again check whether the current request has timed out.

Let’s take a look at the implementation effect of the above code:

The start of zk distributed lock. .
####Get lock success######
Thread: Thread-3, rush to buy: 1544183770509
#######release lock#########
####Get lock success######
Thread: Thread-81, rush to buy successfully: 1544183771555
#######release lock#########

.........

Return on timeout
Thread: Thread-11, failed to buy timeout, please try again: 1544183800677
Return on timeout
Thread: Thread-1, please try again if the rush timeout failed: 1544183800681
#######release lock#########
#######release锁### ######
####Getting the lock successful######
Thread: Thread-49, rush to buy: 154183801710
Timeout return
Thread: Thread-25, rush to buy Timeout failure, please try again: 1544183801729
Timeout return
#######release lock#########
#######release lock##### ####
Release the lock may not be accurate. It should be said that the connection is closed. Some threads are actually not locked.

I simply tried zk to implement distributed locks. Of course, if the above code is applied to production, there are still a lot of problems, because the point of interest is not here, so I won't study it carefully. Simply put, the steps are more complicated than other methods, and it feels more prone to problems.

Summary
After three ways of application and simple practice, summarize the advantages and disadvantages of the three ways of implementing distributed locks as follows

1. Database implementation:

< p>Advantages, simple implementation is only the display lock for update. Disadvantages, performance problems are large, and the system itself needs to be designed to reduce the pressure on the database as much as possible.

2. Redis implementation:

Advantages: General Internet projects will be integrated, itself is a nosql database, cache implementation is simple, high concurrency is easy to cope with, and the new version of Jedis perfectly solves the previous programs An error occurs, and the timeout period is not set to deadlock.

Disadvantages: network problems may cause lock deletion failure, and there is a certain delay in the timeout period.

3. ZooKeeper implementation:

Advantages: Zookeeper temporary node has a congenital controllable validity period setting, which avoids the deadlock problem caused by the program.

Disadvantage: too much implementation Complicated, more prone to problems than the other two writing methods, and also need to maintain zk separately.

Conclusion:

I personally recommend the Redis implementation method, which is simple to implement and has better performance. At the same time, the introduction of clusters can improve availability. Jedis's multi-parameter setting method also better guarantees the validity period control and deadlock problems

Leave a Comment

Your email address will not be published.