第5章:基于zookeeper的分布式锁实现

时间:2022-12-05 08:26:42

1、使用场景

分布式锁主要应用在跨主机,跨网络资源访问中的协调控制,例如多个task处理任务,但是要求一个任务只能限制在一台task上处理,这里就可以采取分布式锁来实现访问控制协调。

2、实现逻辑

第5章:基于zookeeper的分布式锁实现

基本思想:通过创建一个持久化根节点作为本次锁行为根节点,每个需要获取资源的分布式业务注册一个临时顺序节点挂载到根节点下,每个分布式业务通过节点的顺序来获取锁。在通过顺序节点获取锁的时候有两种方式:

方式1:简单轮询,只要没有超时限制,就不断尝试获取锁,直到获取到了就返回。

方式2:通过监听前一个顺序节点的方式,只要前一个顺序节点释放(删除了节点)就再次获取,否则就阻塞等待,这种方式存在并发竞争问题,例如,节点已经释放后,再挂载监听此节点的事件,就会导致事件一直不会触发,不断等待下去。

3、代码实现

接口定义如下
/**
*
*/
package com.flykingmz.zookeeper.dLock;

import java.util.concurrent.TimeUnit;

/**
* @author flyking
*
*/
public interface DistributedLock {
/**
* 获取锁,如果没有得到就一直等待
* @throws Exception
*/
void lock() throws Exception;

/**
* 获取锁,直到指定时间time超时返回
* @param time
* @param unit
* @return
* @throws Exception
*/
boolean lock(long time, TimeUnit unit) throws Exception;

/**
* 释放锁
* @throws Exception
*/
void unLock() throws Exception;

/**
* 释放根节点锁
* @throws Exception
*/
void release() throws Exception;
}
简单实现类
/**
*
*/
package com.flykingmz.zookeeper.dLock;

import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import org.I0Itec.zkclient.IZkChildListener;
import org.I0Itec.zkclient.IZkDataListener;
import org.I0Itec.zkclient.ZkClient;
import org.I0Itec.zkclient.exception.ZkNoNodeException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* 实现的一个基于zookeeper的简单分布式锁实现
* 初始化需要传递zookeeper的host+port
* @author flyking
*
*/
public class SimpleDistributedLock implements DistributedLock {
private final static Logger logger = LoggerFactory
.getLogger(SimpleDistributedLock.class);

private ZkClient client;
private String rootLockerName;
private String SUB_LOCK_NAME_PREFIX = "sublock-";
private ThreadLocal<String> currentLockPath = new ThreadLocal<String>();

public SimpleDistributedLock(String serverstring, String rootLockerName) {
this.client = new ZkClient(serverstring);
this.rootLockerName = rootLockerName;
this.createRootLock();
}

/**
* 基于临时序列节点创建的一个锁
*/
private void createLock() {
String selfPath = client.createEphemeralSequential(rootLockerName.concat("/").concat(SUB_LOCK_NAME_PREFIX), 1);
logger.info("thread {},create Lock and path is {}",Thread.currentThread().getId(),selfPath);
currentLockPath.set(selfPath.substring(selfPath.lastIndexOf("/")+1));
}

/**
* 基于持久节点创建的锁的根节点
*/
private void createRootLock() {
this.client.createPersistent(rootLockerName);
}

/**
* 删除锁
*/
private void deleteLock() {
String selfPath = currentLockPath.get();
client.delete(rootLockerName.concat("/").concat(
selfPath));
logger.info("thread {},delete Lock and path is {} and time {}",Thread.currentThread().getId(),rootLockerName.concat("/").concat(
selfPath),System.currentTimeMillis());
}

/**
* 尝试获取锁,如果有返回已获取,如果锁被占用就返回未获取
* @return
*/
private boolean tryLock() {
List<String> sortedLocks = this.getSortedChildren();
String lockPath = currentLockPath.get();
if (sortedLocks.indexOf(lockPath) == 0) {
logger.info("thread {} try get Lock ",Thread.currentThread().getId());
return true;
}
return false;
}

/**
* 获取排序好的子节点
* @return
*/
private List<String> getSortedChildren() {
try {
List<String> children = client.getChildren(rootLockerName);
Collections.sort(children, new Comparator<String>() {
public int compare(String lhs, String rhs) {
return getLockNodeNumber(lhs).compareTo(
getLockNodeNumber(rhs));
}
});
return children;

} catch (ZkNoNodeException e) {
this.createRootLock();
return getSortedChildren();
}
}

/**
* 截取锁编号
* @param str zk的临时序列节点名称
* @return
*/
private String getLockNodeNumber(String str) {
int index = str.lastIndexOf(SUB_LOCK_NAME_PREFIX);
if (index >= 0) {
return index <= str.length() ? str.substring(index) : "";
}
return str;
}

/**
* 阻塞获取锁,存在锁释放和锁监听的并发竞争问题
* 需要优化
* @param timeToWait
* 阻塞最大等待的时间(ms)
* @param usePollMode
* 是否采用简单轮询模式,还是阻塞等待模式
* @return
*/
@Deprecated
private boolean blockLock(long timeToWait, boolean usePollMode) {
logger.info("thread {} enter block Lock ",Thread.currentThread().getId());
if(usePollMode){
return this.blockLock(timeToWait);
}
long starTime = System.currentTimeMillis();
while ((System.currentTimeMillis() - starTime) < timeToWait) {
boolean getLock = this.tryLock();
if(getLock){
logger.info("thread {} block get Lock ",Thread.currentThread().getId());
return true;
}
if (!getLock) {
List<String> sortedLocks = this.getSortedChildren();
String lockPath = currentLockPath.get();
if(sortedLocks.indexOf(lockPath) == 0){
logger.info("thread {} block get Lock ",Thread.currentThread().getId());
return true;
}
int preIndex = sortedLocks.indexOf(lockPath) - 1;
String preIndexPath = sortedLocks.get(preIndex);
String preSequencePath = rootLockerName.concat("/").concat(
preIndexPath);
logger.info("thread {} start listen {} and time {} ",Thread.currentThread().getId(),preSequencePath,System.currentTimeMillis());
final CountDownLatch latch = new CountDownLatch(1);
IZkDataListener dataListener = new IZkDataListener() {

public void handleDataDeleted(String dataPath) throws Exception {
logger.info("data path {} handleDataDeleted ",dataPath);
}

public void handleDataChange(String dataPath, Object data) throws Exception {
logger.info("data path {} handleDataChange ",dataPath);
}
};

try {
client.subscribeDataChanges(preSequencePath,dataListener);
logger.info("thread {} start wait {} and time {}",Thread.currentThread().getId(),rootLockerName,System.currentTimeMillis());
latch.await();
} catch (ZkNoNodeException e){
logger.info("zk client ZkNoNodeException error!", e);
client.subscribeDataChanges(preSequencePath, dataListener);
}catch (Exception e) {
}

} else {
logger.info("thread {} block get Lock ",Thread.currentThread().getId());
return true;
}

}
return false;
}

/**
* 阻塞等待获取锁,简单的轮询实现
* @param timeToWait
* 阻塞最大等待的时间(ms)
* @return
*/
private boolean blockLock(long timeToWait) {
logger.info("thread {} enter block Lock ",Thread.currentThread().getId());
long starTime = System.currentTimeMillis();
while ((System.currentTimeMillis() - starTime) < timeToWait) {
boolean getLock = this.tryLock();
if(getLock){
logger.info("thread {} block get Lock ",Thread.currentThread().getId());
return true;
}
}
return false;
}

public void lock() throws Exception {
this.createLock();
boolean isLock = this.tryLock();
if (isLock) {
return;
}
this.blockLock(Long.MAX_VALUE);
}

public boolean lock(long time, TimeUnit unit) throws Exception {
this.createLock();
boolean isLock = this.tryLock();
if (isLock) {
return true;
}
isLock = this.blockLock(unit.toMillis(time));
if (isLock) {
return true;
}
return false;
}

public void unLock() throws Exception {
this.deleteLock();
}

public void release() throws Exception {
this.client.delete(rootLockerName);
this.client.close();
}

}
测试代码类
package com.flykingmz.zookeeper.dLock;

import java.util.concurrent.CountDownLatch;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class TestSimpleDistributedLock {
private final static Logger logger = LoggerFactory
.getLogger(TestSimpleDistributedLock.class);

private static final int THREAD_NUM = 100;
private static final CountDownLatch threadSemaphore = new CountDownLatch(THREAD_NUM);

/**
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
final DistributedLock dc = new SimpleDistributedLock("host:port","/rootLock");
for(int i=0; i < THREAD_NUM; i++){
new Thread(){
@Override
public void run() {
try{
logger.info("thread start no "+Thread.currentThread().getId());
dc.lock();
dc.unLock();
threadSemaphore.countDown();
} catch (Exception e){
e.printStackTrace();
}
}
}.start();
}
try {
threadSemaphore.await();
dc.release();
logger.info("所有线程运行结束!");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}

}

4、结果

10个线程模拟分布式业务获取锁,日志打印如下:
2016-10-20 18:07:04 [Thread-1] INFO com.flykingmz.zookeeper.dLock.TestSimpleDistributedLock  - thread start no 12
2016-10-20 18:07:04 [Thread-4] INFO com.flykingmz.zookeeper.dLock.TestSimpleDistributedLock - thread start no 15
2016-10-20 18:07:04 [Thread-5] INFO com.flykingmz.zookeeper.dLock.TestSimpleDistributedLock - thread start no 16
2016-10-20 18:07:04 [Thread-3] INFO com.flykingmz.zookeeper.dLock.TestSimpleDistributedLock - thread start no 14
2016-10-20 18:07:04 [Thread-8] INFO com.flykingmz.zookeeper.dLock.TestSimpleDistributedLock - thread start no 19
2016-10-20 18:07:04 [Thread-2] INFO com.flykingmz.zookeeper.dLock.TestSimpleDistributedLock - thread start no 13
2016-10-20 18:07:04 [Thread-9] INFO com.flykingmz.zookeeper.dLock.TestSimpleDistributedLock - thread start no 20
2016-10-20 18:07:04 [Thread-7] INFO com.flykingmz.zookeeper.dLock.TestSimpleDistributedLock - thread start no 18
2016-10-20 18:07:04 [Thread-6] INFO com.flykingmz.zookeeper.dLock.TestSimpleDistributedLock - thread start no 17
2016-10-20 18:07:04 [Thread-10] INFO com.flykingmz.zookeeper.dLock.TestSimpleDistributedLock - thread start no 21
2016-10-20 18:07:04 [Thread-6] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 17,create Lock and path is /rootLock/sublock-0000000000
2016-10-20 18:07:04 [Thread-3] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 14,create Lock and path is /rootLock/sublock-0000000001
2016-10-20 18:07:04 [Thread-1] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 12,create Lock and path is /rootLock/sublock-0000000002
2016-10-20 18:07:04 [Thread-8] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 19,create Lock and path is /rootLock/sublock-0000000003
2016-10-20 18:07:04 [Thread-5] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 16,create Lock and path is /rootLock/sublock-0000000004
2016-10-20 18:07:04 [Thread-2] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 13,create Lock and path is /rootLock/sublock-0000000005
2016-10-20 18:07:04 [Thread-7] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 18,create Lock and path is /rootLock/sublock-0000000006
2016-10-20 18:07:04 [Thread-4] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 15,create Lock and path is /rootLock/sublock-0000000007
2016-10-20 18:07:04 [Thread-9] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 20,create Lock and path is /rootLock/sublock-0000000008
2016-10-20 18:07:04 [Thread-10] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 21,create Lock and path is /rootLock/sublock-0000000009
2016-10-20 18:07:04 [Thread-3] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 14 enter block Lock
2016-10-20 18:07:04 [Thread-6] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 17 try get Lock
2016-10-20 18:07:04 [Thread-1] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 12 enter block Lock
2016-10-20 18:07:04 [Thread-8] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 19 enter block Lock
2016-10-20 18:07:04 [Thread-5] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 16 enter block Lock
2016-10-20 18:07:04 [Thread-2] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 13 enter block Lock
2016-10-20 18:07:04 [Thread-7] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 18 enter block Lock
2016-10-20 18:07:04 [Thread-4] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 15 enter block Lock
2016-10-20 18:07:04 [Thread-9] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 20 enter block Lock
2016-10-20 18:07:04 [Thread-10] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 21 enter block Lock
2016-10-20 18:07:04 [Thread-6] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 17,delete Lock and path is /rootLock/sublock-0000000000 and time 1476958024362
2016-10-20 18:07:04 [Thread-3] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 14 try get Lock
2016-10-20 18:07:04 [Thread-3] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 14 block get Lock
2016-10-20 18:07:04 [Thread-3] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 14,delete Lock and path is /rootLock/sublock-0000000001 and time 1476958024377
2016-10-20 18:07:04 [Thread-1] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 12 try get Lock
2016-10-20 18:07:04 [Thread-1] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 12 block get Lock
2016-10-20 18:07:04 [Thread-1] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 12,delete Lock and path is /rootLock/sublock-0000000002 and time 1476958024393
2016-10-20 18:07:04 [Thread-8] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 19 try get Lock
2016-10-20 18:07:04 [Thread-8] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 19 block get Lock
2016-10-20 18:07:04 [Thread-8] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 19,delete Lock and path is /rootLock/sublock-0000000003 and time 1476958024408
2016-10-20 18:07:04 [Thread-5] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 16 try get Lock
2016-10-20 18:07:04 [Thread-5] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 16 block get Lock
2016-10-20 18:07:04 [Thread-5] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 16,delete Lock and path is /rootLock/sublock-0000000004 and time 1476958024408
2016-10-20 18:07:04 [Thread-2] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 13 try get Lock
2016-10-20 18:07:04 [Thread-2] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 13 block get Lock
2016-10-20 18:07:04 [Thread-2] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 13,delete Lock and path is /rootLock/sublock-0000000005 and time 1476958024424
2016-10-20 18:07:04 [Thread-7] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 18 try get Lock
2016-10-20 18:07:04 [Thread-7] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 18 block get Lock
2016-10-20 18:07:04 [Thread-7] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 18,delete Lock and path is /rootLock/sublock-0000000006 and time 1476958024440
2016-10-20 18:07:04 [Thread-4] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 15 try get Lock
2016-10-20 18:07:04 [Thread-4] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 15 block get Lock
2016-10-20 18:07:04 [Thread-4] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 15,delete Lock and path is /rootLock/sublock-0000000007 and time 1476958024440
2016-10-20 18:07:04 [Thread-9] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 20 try get Lock
2016-10-20 18:07:04 [Thread-9] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 20 block get Lock
2016-10-20 18:07:04 [Thread-9] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 20,delete Lock and path is /rootLock/sublock-0000000008 and time 1476958024455
2016-10-20 18:07:04 [Thread-10] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 21 try get Lock
2016-10-20 18:07:04 [Thread-10] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 21 block get Lock
2016-10-20 18:07:04 [Thread-10] INFO com.flykingmz.zookeeper.dLock.SimpleDistributedLock - thread 21,delete Lock and path is /rootLock/sublock-0000000009 and time 1476958024455
2016-10-20 18:07:04 [ZkClient-EventThread-9-172.16.10.58:2181] INFO org.I0Itec.zkclient.ZkEventThread - Terminate ZkClient event thread.
2016-10-20 18:07:04 [main-EventThread] INFO org.apache.zookeeper.ClientCnxn - EventThread shut down
2016-10-20 18:07:04 [main] INFO org.apache.zookeeper.ZooKeeper - Session: 0x25776ad02740082 closed
2016-10-20 18:07:04 [main] INFO com.flykingmz.zookeeper.dLock.TestSimpleDistributedLock - 所有线程运行结束!

基于zookeeper实现的分布式锁全部源代码可以到github上下载,地址:https://github.com/flykingmz/zookeeper-step.git,具体项目名称为:distributedLock