共计 2199 个字符,预计需要花费 6 分钟才能阅读完成。
分布式锁
1、在分布式系统环境下,一个办法在同一时间只能被一个机器的一个线程执行
2、高可用的获取锁与开释锁
3、高性能的获取锁与开释锁
4、具备可重入个性(可了解为从新进入,由多于一个工作并发应用,而不用放心数据谬误)
5、具备锁生效机制,避免死锁
6、具备非阻塞锁个性,即没有获取到锁将间接返回获取锁失败
基于 zookeeper 的分布式锁
1.zookeeper 的一些个性
- 有序节点:如果以后有一个父节点为 /lock,咱们能够在这个父节点上面创立子节点;zookeeper 提供了一个可选的有序个性,例如咱们能够创立子节点“
/lock/node-
”并且指明有序,那么 zookeeper 在生成子节点时会依据以后的子节点数量主动增加整数序号,也就是说如果是第一个创立的子节点,那么生成的子节点为/lock/node-0000000000
,下一个节点则为/lock/node-0000000001
,顺次类推。 - 长期节点:客户端能够建设一个长期节点,在会话完结或者会话超时后,zookeeper 会主动删除该节点。
- 事件监听:在读取数据时,咱们能够同时对节点设置事件监听,当节点数据或构造变动时,zookeeper 会告诉客户端。以后 zookeeper 有如下四种事件:
1. 节点创立;
2. 节点删除;
3. 节点数据批改;
4. 子节点变更。
2. 实现
①客户端连贯 zookeeper,并在 /lock 下创立长期的且有序的子节点,第一个客户端对应的子节点为 /lock/lock-1,第二个为 /lock/lock-2,以此类推。
②客户端获取 /lock 下的子节点列表,判断本人创立的子节点是否为以后子节点列表中序号最小的子节点,如果是则认为取得锁,否则监听 /lock 的子节点变更音讯,取得子节点变更告诉后反复此步骤直至取得锁;
③执行业务代码;
④实现业务流程后,删除对应的子节点开释锁。
基于 curator 的 zookeeper 分布式锁实现
public static void main(String[] args) throws Exception { | |
// 创立 zookeeper 的客户端 | |
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3); | |
CuratorFramework client = CuratorFrameworkFactory.newClient("10.21.41.181:2181,10.21.42.47:2181,10.21.49.252:2181", retryPolicy); | |
client.start(); | |
// 创立分布式锁, 锁空间的根节点门路为 /curator/lock | |
InterProcessMutex mutex = new InterProcessMutex(client, "/curator/lock"); | |
mutex.acquire(); | |
// 取得了锁, 进行业务流程 | |
System.out.println("Enter mutex"); | |
// 实现业务流程, 开释锁 | |
mutex.release(); | |
// 敞开客户端 | |
client.close();} |
基于 redis 的分布式锁
1. 流程
1、服务 A 为了取得锁,向 Redis 发动如下命令: SET productId:lock 0xx9p03001 NX PX 30000
其中,”productId” 由本人定义,能够是与本次业务无关的 id,”0xx9p03001″ 是一串随机值,必须保障全局惟一(起因在后文中会提到),“NX” 指的是当且仅当 key(也就是案例中的 ”productId:lock”) 在 Redis 中不存在时,返回执行胜利,否则执行失败。”PX 30000″ 指的是在 30 秒后,key 将被主动删除。执行命令后返回胜利,表明服务胜利的取得了锁。
2、服务 B 为了取得锁,向 Redis 发动同样的命令: SET productId:lock 0000111 NX PX 30000
因为 Redis 内曾经存在同名 key,且并未过期,因而命令执行失败,服务 B 未能取得锁。服务 B 进入循环申请状态,比方每隔 1 秒钟 (自行设置) 向 Redis 发送申请,直到执行胜利并取得锁。
3、服务 A 的业务代码执行时长超过了 30 秒,导致 key 超时,因而 Redis 主动删除了 key。此时服务 B 再次发送命令执行胜利,假如本次申请中设置的 value 值为 0000222。
4、服务 A 执行结束,为了开释锁,服务 A 会被动向 Redis 发动删除 key 的申请。
2. 实现
① 加锁
客户端集成 Redisson,在加锁之前,首先须要通过 hash 算法选定集群内某一个 Redis Master,后续加锁、解锁等各种过程都是在这个 Redis Master 和与之绑定的 slave 节点之间。
②执行 lua 脚本实现加锁
③watch dog 主动延期
watch dog 是一个后盾线程,它会每隔 10 秒察看以后客户端是否依然持有锁,如果持有,阐明客户端可能依然在应用锁,因而缩短锁的残余生存工夫。
④ 开释锁机制
如果执行 lock.unlock(),Redis 会找到上方 test 数据结构,将加锁次数减一。如果减完后发现加锁次数为 0,则阐明以后客户端不再持有锁,因而执行: del test 命令,从 Redis 中删除这条 key。
// 筹备为名为 "test" 的 key 加锁 | |
RLock lock = redisson.getLock("test"); | |
// 加锁 | |
lock.lock(); | |
// 解锁 | |
lock.unlock(); |