Zookeeper的使用典型场景之一:分布式锁
使用zk的分布式锁,无非是通过zk的两大特性:节点和事件监听
互斥锁:
1)创建临时顺序节点
2)判断是否是临时顺序节点最小的,如果是,直接获得锁,如果不是,对之前的一个节点进行监听
3)获得锁,处理业务逻辑,释放锁,即delete节点,监听当前节点的后续节点收到事件通知,开始从第二步循环
采用这种方式,每个线程对应的只需要监听一个节点,避免了多个线程竞争一个节点,可以缓解服务端压力,效率较高。这种实现是公平锁、互斥锁。
读写锁:
使用互斥锁,如果同一时间大量的请求涌入是会性能下降的,但是实际上很多情况下不是所有的请求都需要阻塞的,通过zk同样可以实现读写锁。
写入的时候,后来的请求不能读,同样也不能写。读的时候,后面的请求可以读,但是不能写。基于这样的原理,需要监听的节点就少了很多。读读之间不需要监听,如果有写,需要监听。
写锁实际上可以看作是互斥锁。
zk分布式锁的具体代码实现,可以使用Apache开源的一款zk客户端Curator,实现起来相当简单。
使用springboot项目,引入依赖:
<dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>5.0.0</version> </dependency>
注入客户端:
@Bean(initMethod = "start") public CuratorFramework curatorFramework() { RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3); CuratorFramework client = CuratorFrameworkFactory.newClient("192.168.0.15:2181", retryPolicy); return client; }
# 互斥锁demo
InterProcessMutex interProcessMutex = new InterProcessMutex(curatorFramework, "/item_" + id); try { // 一直等待,直到加锁成功 interProcessMutex.acquire(); // TODO: 2020-11-24 业务逻辑 } catch (Exception e) { if (e instanceof RuntimeException) { throw e; } } finally { interProcessMutex.release(); }
# 读写锁实现demo,可以加读锁或者写锁
InterProcessReadWriteLock interProcessReadWriteLock = new InterProcessReadWriteLock(curatorFramework, "/item_" + id); // InterProcessMutex interProcessMutex = interProcessReadWriteLock.readLock(); InterProcessMutex interProcessMutex = interProcessReadWriteLock.writeLock(); try { interProcessMutex.acquire(); // TODO: 2020-11-24 业务逻辑 } catch (Exception e) { if (e instanceof RuntimeException) { throw e; } } finally { interProcessMutex.release(); }
curator客户端已经帮我们封装好了,我们只需要专注处理业务逻辑即可。
具体的原理也可以进到curator源码中去看,就是基于上面的原则去实现的。