什么是分布式锁?
分布式锁就是分布式系统中实现互斥访问的机制,可以保证在分布式系统中多个节点同时对共享资源进行访问时的互斥性。
分布式锁是分布式系统中常用的一种机制,通常被用于解决分布式系统中的竞态条件问题。
分布式锁的原理
分布式锁的原理:
- 是在分布式系统中选定一台节点作为锁管理节点,其他节点需要获取锁时,需要向锁管理节点发送请求,
- 如果锁管理节点确认当前没有其他节点获得该锁,则向请求节点返回获得锁的信号,否则会返回请求失败的信息。
- 获得锁的节点可以访问共享资源,其他节点则需要等待。
怎么实现分布式锁?
基于数据库的实现
可以利用数据库的唯一性约束来实现分布式锁。在需要获取锁时,向数据库中插入一条记录,并设置该记录的唯一性约束。如果插入成功,则获得锁;否则需要等待。释放锁时,删除该记录即可。
基于缓存的实现
可以利用缓存系统的CAS(Compare and Swap)操作来实现分布式锁。在需要获取锁时,将锁作为缓存键值对的key,值可以是任意值,利用缓存的CAS操作进行加锁。释放锁时,直接删除该缓存键值对即可。
基于ZooKeeper的实现
可以利用ZooKeeper提供的节点临时性和有序性特点来实现分布式锁。在需要获取锁时,创建一个临时顺序节点,如果该节点是所有顺序节点中最小的,说明获得了锁;否则需要等待。释放锁时,删除该临时节点即可。
举例使用java 实现分布所主要步骤:
- 在ZooKeeper中创建一个永久节点作为锁节点的父节点,例如“/locks”。
- 在获取锁的方法中,创建一个临时有序节点,例如“/locks/lock_”。
- 获取锁时,获取所有子节点,并对子节点按照序号进行排序,如果当前节点是序号最小的节点,则获取锁成功。
- 如果获取锁失败,则等待锁释放或超时,释放锁时需要删除对应的子节点。
下面是Java代码实现:
public class DistributedLock {
private final ZooKeeper zk;
private final String lockPath;
private final String lockName;
public DistributedLock(ZooKeeper zk, String lockPath, String lockName) {
this.zk = zk;
this.lockPath = lockPath;
this.lockName = lockName;
}
public void lock() throws Exception {
// 创建一个临时有序节点
String lockNode = zk.create(lockPath + "/" + lockName + "_", new byte[0],
ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
// 获取所有子节点,并按序号排序
List<String> nodes = zk.getChildren(lockPath, false);
Collections.sort(nodes);
// 获取当前节点的序号
int index = nodes.indexOf(lockNode.substring(lockPath.length() + 1));
// 如果当前节点是序号最小的节点,则获取锁成功
if (index == 0) {
return;
}
// 否则等待锁释放或超时
String prevNode = nodes.get(index - 1);
final CountDownLatch latch = new CountDownLatch(1);
Stat stat = zk.exists(lockPath + "/" + prevNode, new Watcher() {
public void process(WatchedEvent event) {
if (event.getType() == Event.EventType.NodeDeleted) {
latch.countDown();
}
}
});
if (stat != null) {
latch.await();
}
}
public void unlock() throws Exception {
// 删除对应的节点
zk.delete(lockPath + "/" + lockName + "_", -1);
}
}
测试代码
public static void main(String[] args) {
String connectionString = "127.0.0.1:2181";
int sessionTimeout = 5000;
ZooKeeper zk = new ZooKeeper(connectionString, sessionTimeout, null);
String lockPath = "/locks";
String lockName = "test";
DistributedLock lock = new DistributedLock(zk, lockPath, lockName);
try {
lock.lock();
// 获取到锁后执行业务操作
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
本文参考: 分布式服务架构:原理、设计与实战