1. 概述
分布式锁,如果你有多个机器在访问同一个共享资源,
那么这个时候,如果你需要加个锁,让多个分布式的机器在访问共享资源的时候串行起来
那么这个时候,那个锁,多个不同机器上的服务共享的锁,就是分布式锁
分布式锁当然有很多种不同的实现方案,redis分布式锁,zookeeper分布式锁
2. 原理
我们通过去创建zk的一个临时node,来模拟加锁
zk会给你保证说,只会创建一个临时node,其他请求过来如果再要创建临时node,就会报错,NodeExistsException
那么所以说,我们的所谓上锁,其实就是去创建某个临时node
如果临时node创建成功了,那么说明我们成功加锁了,此时就可以去执行业务操作
如果临时node创建失败了,说明有人已经在拿到锁了,在进行业务操作,那么就不断的等待,直到自己可以获取到锁为止
释放一个分布式锁,去删除掉那个临时node就可以了,就代表释放了一个锁,
那么此时其他的机器就可以成功创建临时node,获取到锁
3. 应用场景
3.1 缓存重建
1、主动更新
监听消息队列,后台修改数据,获取到一个缓存变更的消息之后,去哪个源服务中调用接口拉取数据,更新到ehcache和redis中
先获取分布式锁,然后才能更新redis,同时更新时要比较时间版本
2、被动重建
直接读取源头数据,直接返回给nginx,同时推送一条消息到一个队列,后台线程异步消费
后台线程负责先获取分布式锁,然后才能更新redis,同时要比较时间版本
4. zk分布式锁的解决并发冲突的方案
(1)变更缓存重建以及空缓存请求重建,更新redis之前,都需要先获取对应的分布式锁
(2)拿到分布式锁之后,需要根据时间版本去比较一下,如果自己的版本新于redis中的版本,那么就更新,否则就不更新
(3)如果拿不到分布式锁,那么就等待,不断轮询等待,直到自己获取到分布式的锁
5. 封装类
/**
* @author kris
* @create 2018-08-05 上午 11:42
*/
public class ZooKeeperSession {
private static CountDownLatch connectedSemaphore = new CountDownLatch(1);
private ZooKeeper zookeeper;
public ZooKeeperSession() {
//去连接zookeeper server,创建会话的时候,是异步去进行的
//所以要给一个监听器,说告诉我们什么时候才是真正完成了跟zk server的连接
try {
this.zookeeper = new ZooKeeper("192.168.92.132:2181,192.168.92.133:2181,192.168.92.134:2181", 50000, new ZooKeeperWatcher());
//给一个状态connecting,连接中
System.out.println(zookeeper.getState());
// CountDownLatch
// java多线程并发同步的一个工具类
// 会传递进去一些数字,比如说1,2 ,3 都可以
// 然后await(),如果数字不是0,那么久卡住,等待
// 其他的线程可以调用coutnDown(),减1
// 如果数字减到0,那么之前所有在await的线程,都会逃出阻塞的状态
// 继续向下运行
connectedSemaphore.await();
System.out.println("ZooKeeper session established......");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取分布式锁
*
* @param productId
*/
public void acquireDistributedLock(Long productId) {
String path = "/product-lock-" + productId;
try {
zookeeper.create(path, "".getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
System.out.println("success to acquire lock for product[id=" + productId + "]");
} catch (Exception e) {
//如果那个商品对应的锁的node,已经存在了,就是已经被别人加锁了,那么这里就会报错
//NodeExistsException
int count = 0;
while (true) {
try {
Thread.sleep(20);
zookeeper.create(path, "".getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
} catch (Exception e1) {
e1.printStackTrace();
count++;
continue;
}
System.out.println("success to acquire lock for product[id=" + productId + "] after " + count + " times try......");
break;
}
}
}
/**
* 释放掉一个分布式锁
*
* @param productId
*/
public void releaseDistributedLock(Long productId) {
String path = "/product-lock-" + productId;
try {
zookeeper.delete(path, -1);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 封装单例的静态内部类
*/
private static class Singleton {
private static ZooKeeperSession instance;
static {
instance = new ZooKeeperSession();
}
public static ZooKeeperSession getInstance() {
return instance;
}
}
public static ZooKeeperSession getInstance() {
return Singleton.getInstance();
}
/**
* 建立zk session的watcher
*/
private class ZooKeeperWatcher implements Watcher {
@Override
public void process(WatchedEvent watchedEvent) {
System.out.println("Receive watched event: " + watchedEvent.getState());
if (Event.KeeperState.SyncConnected == watchedEvent.getState()) {
connectedSemaphore.countDown();
}
}
}
}