网站首页 > java教程 正文
1. 前言
在我们的应用中,经常会碰见多个请求去访问同一个资源的情况。如果请求 A 拿到这个资源数据,想要对它进行修改,但是还没有进行事务提交,此时请求 B 访问这个资源就会拿到修改前的数据,很显然请求 B 拿到的是历史数据,是不正确的。
在单个服务器的应用中,我们可以使用系统的线程来对这个资源进行加锁。那么在分布式环境中我们有什么方案来解决这个问题呢?答案就是使用分布式锁。那么什么是分布式锁?分布式锁又是如何实现的呢?本节我们就来讲解如何使用 Zookeeper 实现分布式锁,以及它的实现原理。
2. 分布式锁
在讲解 Zookeeper 实现的分布式锁之前,我们先来了解什么是分布式锁,分布式锁的实现技术,以及分布式锁常用的类型。
2.1 分布式锁的特点
顾名思义,分布式锁就是实现在分布式网络环境中的锁。也就是说,在锁的基础上加上分布式的特性,我们来分析一下分布式锁实现的必要条件:
- 在分布式环境中,多个进程对资源的访问必须具有顺序性;
- 获取锁和释放锁的过程需要高可用和高性能;
- 具有锁失效的机制,避免死锁;
- 非阻塞的锁,没有获取到锁直接返回获取锁失败。
介绍了分布式锁的特点,那么有哪些技术能够实现分布式锁呢?
2.2 分布式锁的实现技术
- Memcached: 使用 add 命令来添加 key,key 添加成功说明当前无人使用此 key,也就是说无人使用此资源,相当于获取锁。再次使用 add 命令来添加相同的 key 时,此时 key 已存在就会添加失败,说明有人已经使用了这个 key,也就是说此资源被人占用,相当于获取锁失败;
- Redis: 使用 setnx 命令来添加 key,key 添加成功说明当前无人使用此 key,也就是说无人使用此资源,相当于获取锁。再次使用 setnx 命令来添加相同的 key 时,此时 key 已存在就会添加失败,说明有人已经使用了这个 key,也就是说此资源被人占用,相当于获取锁失败;
- Chubby: Google 使用 Paxos 一致性算法实现的粗粒度分布式锁;
- Zookeeper: 使用 Zookeeper 临时顺序节点的特性,实现分布式锁和锁的等待队列。
介绍了分布式锁的实现技术,接下来我们来介绍分布式锁常用的类型。
2.3 分布式锁常用的类型
分布式锁常用的类型有两种:一种是排他锁,一种是共享锁。接下来我们分别介绍这两锁的特点。
- 排他锁
排他锁也叫独占锁,顾名思义,也就是对资源进行独占。排他锁只允许获取了该锁的线程,对具有排他锁的资源进行访问,无论是写操作还是读操作,直到该线程主动释放掉排他锁。
- 共享锁
共享锁也就是把资源进行共享,当然共享的只有读操作。共享锁只对写操作进行加锁,其它线程的读操作不做加锁操作,这样的共享机制提高了对资源访问的性能。
介绍完分布式锁的常用类型,接下来我们开始学习如何使用 Zookeeper 实现分布式锁。
3. Zookeeper 实现分布式锁
上面我们提到,Zookeeper 是根据它的临时顺序节点来实现的分布式锁,这里我们来回顾一下临时顺序节点的特性。
3.1 临时顺序节点
临时顺序节点:
- 节点具有临时性,创建该节点的 Zookeeper 客户端与 Zookeeper 服务端断开连接时,该节点会自动被 Zookeeper 服务端删除;
- 节点具有顺序性,创建该节点时,Zookeeper 服务端会根据创建时间的顺序在该节点名称后面加上顺序编号。
回顾了临时顺序节点的特性,接下来我们就使用 Zookeeper 的 Java 客户端 Curator 来创建临时顺序节点,我们可以使用在 Zookeeper Curator 一节创建的 Spring Boot 测试项目来进行测试。
我们可以在测试类 CuratorDemoApplicationTests 中编写测试用例:
@SpringBootTest
class CuratorDemoApplicationTests {
@Autowired
private CuratorService curatorService;
@Test
void contextLoads() throws Exception {
// 获取客户端
CuratorFramework client = curatorService.getCuratorClient();
// 开启会话
client.start();
// 第一次创建临时顺序节点
String s1 = client.create()
// 如果有父节点会一起创建
.creatingParentsIfNeeded()
// 节点类型:临时顺序节点
.withMode(CreateMode.EPHEMERAL_SEQUENTIAL)
// 节点路径 /wiki
.forPath("/wiki-");
// 输出
System.out.println(s1);
// 第二次创建临时顺序节点
String s2 = client.create()
// 如果有父节点会一起创建
.creatingParentsIfNeeded()
// 节点类型:临时顺序节点
.withMode(CreateMode.EPHEMERAL_SEQUENTIAL)
// 节点路径 /wiki
.forPath("/wiki-");
// 输出
System.out.println(s2);
// 关闭客户端
client.close();
}
}
执行测试方法,控制台输出:
/wiki-0000000000
/wiki-0000000001
我们可以发现,控制台一共输出了两个 /wiki 节点,而且每个 /wiki 节点后面都增加了编号,此时我们去 zkCli 命令行客户端查看所有节点,发现并没有 /wiki 节点。因为在我们的测试程序中,我们关闭了客户端,所以临时节点会被移除。
Tips: 如果这里创建失败,请同学们注意父节点是否存在 ACL 访问控制。
回顾了临时顺序节点,那么如何使用 Zookeeper 的临时顺序节点来实现分布式锁呢?接下来我们就开始介绍如何使用 Zookeeper 的临时顺序节点来控制它们的访问顺序。
3.2 分布式锁实现
本节我们来介绍分布式锁实现的具体步骤:
- 创建临时顺序节点: 每一次获取资源的请求,我们都需要使用 Zookeeper 客户端创建一个临时顺序节点,用这个临时顺序节点在 Zookeeper 服务端中获取锁。
- 获取锁: 这里的锁并不具体指代什么,而是根据 Zookeeper 的临时顺序节点的顺序来决定是否获取了锁。如果该节点的顺序编号是最小的,则说明该节点是排在最前面的,在它之前无人占领资源,也就可以说该节点获取了锁,具有访问资源的权限。
- 监听锁: 如果获取锁这一步发现 Zookeeper 客户端创建的临时顺序节点的顺序编号不是最小的,也就是在这个临时顺序节点之前存在其它临时顺序节点,那么就可以说这个节点获取锁失败了,它会进入等待队列。我们可以监听它的前一个节点,只要它的前一个临时顺序节点的删除事件触发,我们就可以获取临时顺序节点的列表来重新确认这个节点的顺序。
- 释放锁: 当一个请求对资源的操作结束后,我们可以使用 Zookeeper 客户端的节点删除 API 来删除这个请求创建的临时顺序节点。除了使用 API 来主动释放锁之外,根据临时顺序节点的特性,当创建这个临时顺序节点的 Zookeeper 客户端与 Zookeeper 服务端断开连接时,这个临时顺序节点会被 Zookeeper 服务端移除。这两种方式都会触发临时节点的删除事件,让下一个临时顺序节点来确认自身的顺序。
4. 总结
本节内容中,我们学习了什么是分布式锁,以及它的特点和类型,还学习了使用 Zookeeper 实现分布式锁的主要步骤。以下是本节内容的总结:
- 分布式锁的特点和常用类型。
- 临时顺序节点的特性。
- 使用 Zookeeper 实现分布式锁的主要步骤。
猜你喜欢
- 2024-11-22 redis分布式锁
- 2024-11-22 4K字深度剖析redisson分布式锁原理
- 2024-11-22 java都为我们提供了各种锁,为什么还需要分布式锁?
- 2024-11-22 一文带你了解Java手写分布式锁的实现
- 2024-11-22 如何用Redisson框架实现分布式锁?
- 2024-11-22 基于 Redis 实现的分布式锁
- 2024-11-22 京东秒杀系统模块的Redis分布式锁深度剖析,没给你讲明白你打我
- 2024-11-22 面试官:Redis分布式锁超时了,任务还没执行完怎么办?
- 2024-11-22 聊聊Redis分布式锁
- 2024-11-22 浅谈分布式锁
你 发表评论:
欢迎- 最近发表
-
- 五,网络安全IDA Pro反汇编工具初识及逆向工程解密实战
- 「JAVA8」- Lambda 表达式(java lambda表达式原理)
- 深入探讨Java代码保护:虚拟机保护技术的新时代
- Nginx反向代理原理详解(图文全面总结)
- 逆向拆解日本IT,哪些Java技术栈薪资溢价高
- mybatis 逆向工程使用姿势不对,把表清空了,心里慌的一比
- Spring Boot集成ProGuard轻松实现Java 代码混淆, Java 应用固若金汤
- 从 Java 代码逆向工程生成 UML 类图和序列图
- 人与人相处:尊重是标配,靠谱是高配,厚道是顶配
- Windows系统安装日期如何修改(windows10怎么修改安装日期)
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)