zookeeper-4
zookeeper 2和3系列正在画图,就先把代码实现的部分发表出来,后面会补上2-3系列的。如需转载请标注一下备注
文章目录
一、zk实现分布式锁
zk要实现分布式锁,首先需要了解分布式锁实现的几个问题:- 争抢锁-(只有一个人获得了锁)
- 获取锁的那个人出现了问题?
- 获取锁的人成功抢到了锁-》锁释放
- 锁释放了删除了,别人怎么知道
zk锁和redis锁虽然都是分布式锁,但是本质上是不一样的,例如redis锁的实现的几个问题:
- 争抢锁 - (只有一个人抢到了)
- 锁没有过期时间,业务挂了,锁还继续?
- 设置锁超时时间,时间超时了但业务没执行完?
- 设置较长的过期时间,但业务挂了,锁还继续?锁的时间多长合适?
这些都是加锁带来的问题。ZK锁的几个问题在这里会给出讲解,而redis锁的问题在下个系列redis中会讲解出来。
zk锁问题的解决
-
获取锁的那个人出现了问题?
解决:在zk中znode是有几个特点,第一章的时候讲过,znode包含(持久节点,持久序列节点,临时节点,临时序列节点)在这里可以使用znode的临时节点。当加锁的人出现了问题,会断开和zk的链接,(临时节点特点是:当client断开链接后,临时节点消失)临时节点的消失,是不是就意味着锁消失了。
那么zk锁和redis锁一对比大家是不是就发现,在redis里面你需要对锁设置时间,还需要额外开启安全线程对锁进行监控。例如业务没执行完需要增加锁的超时时间等。成本就上去了。 -
zk锁释放了,别人怎么知道?三种方案
(1)主动轮训,心跳 。 弊端:延迟,多台机器对zookeeper压力
(2)zk的watch来实现,可以解决延迟问题。 弊端:多台机器对zookeeper有压力
(3)利用序列节点+watch 来实现,watch谁?每个序列节点watch前面一个(这里可以理解为队列,每个队列的后面一个watch前面一个目标),最小的一个获得锁,一旦最小的释放了锁,后面一个就会根据watch来获取锁,这样就不用队列所有都对节点watch。成本:zk只给第二个发事件回调。减少了zookeeper压力
zk分布式锁的代码实现
下面展示一些 代码片
。倒入maven中zk的jar
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</dependency>
curator对zk的api做了封装,直接使用curator,初始化
/**
* 使用 curator 操作 zookeeper
* @author niney
*/
@Bean(name = "curatorFramework")
public CuratorFramework curatorFramework(ZkProperties zk) throws Exception {
//构建 CuratorFramework
CuratorFramework curatorFramework =
CuratorFrameworkFactory.builder()
//连接地址集群用,隔开
.connectString(zk.getConnectIps())
//连接创建超时时间,单位毫秒
.connectionTimeoutMs(zk.getConnectionTimeOutMs())
//会话超时时间
.sessionTimeoutMs(zk.getSessionTimeOutMs())
//配置zookeeper连接的重试策略
.retryPolicy(
new ExponentialBackoffRetry(
//每次重试时间间隔,单位毫秒
//重试次数
zk.getSleepTimeMs(),zk.getMaxRetries()
))
//设置命名空间 在操作节点的时候,会以这个为父节点
.namespace("fm-zk")
.build();
curatorFramework.start();
return curatorFramework;
}
zk锁实现
//通过 InterProcessMutex 该类来获取可重入共性锁
InterProcessMutex lock = new InterProcessMutex(
(CuratorFramework) SpringContext.getBean("curatorFramework"), path
);
try {
lock.acquire();
//这里业务逻辑实现 xxxxx
//xxxxxxxxxx
} catch (Exception e) {
throw new ServerRuntimeException(e);
} finally {
if (lock.isAcquiredInThisProcess()) {
try {
lock.release();
} catch (Exception e) {
throw new