基于缓存或zookeeper的分布式锁实现
缓存锁
我们常常将缓存作为分布式锁的解决方案,但是却不能单纯的判断某个 key 是否存在 来作为锁的获得依据,因为无论是 exists 和 get 命名都不是线程安全的,都无法保证只有一个线程可以获得锁,存在线程争抢,可能会有多个线程同时拿到锁的情况(经典的 Redis “读后写”的问题)。
incr 缓存锁
@Component
public class LockClient {
private StringRedisTemplate stringRedisTemplate;
private ValueOperations<String, String> valueOperations;
@Autowired
public void setStringRedisTemplate(StringRedisTemplate stringRedisTemplate) {
this.stringRedisTemplate = stringRedisTemplate;
this.valueOperations = stringRedisTemplate.opsForValue();
}
public void lockIncr() {
Long lockIncr = valueOperations.increment("lockIncr", 1);
// 说明拿到了锁
if (lockIncr == 1) {
// 业务操作
}
}
}
- incr:递增指定键对应的数值,如果不存在 key 对应的值,那么会先将 key 的值设置为 0,然后执行 incr 操作,返回递增的值。
- 这种锁的实现原理主要是利用 incr 命令的原子性,同一时间只会有一个线程操作这个命令。
- 这种锁的实现方式,不在乎结果数据。保证只有唯一线程能够执行到业务代码。
setnx 缓存锁
上面的锁实现方式,我们对资源做了隔离,保证只有唯一线程可以拿到资源并执行操作。但是如果资源并不是唯一线程执行的呢?存在多个线程争抢的情况下呢?
public void lockSetnx() {
String lock = "lockSetnx";
long millis = System.currentTimeMillis();
long timeout = millis + 3000L + 1;
try {
while (true) {
boolean setnx = valueOperations.setIfAbsent(lock, timeout + "");
if (setnx == true) {
break;
}
String oldTimeout = valueOperations.get(lock);
// 这一步是为了解决客户端异常宕机,锁没有被正常释放的时候。
// 当 p1、p2 同时执行到这里,发现锁的时间过期了。p1、p2 同时执行 getSet 命令。
// 假设 p1 先执行成功了,那么 p1 得到的值就是原来锁的过期时间(可以符合下面的判断式),表示争抢锁成功。
// 假设 p2 后执行成功了,那么 p2 得到的值就是 p1 set 进去的值(不会符合下面的表达式),表示争抢锁失败。
String oldValue = valueOperations.getAndSet(lock, timeout + "");
if (millis > Long.valueOf(oldTimeout) && millis > Long.valueOf(oldValue)) {
break;
}
// 休眠 100 毫秒,再去争抢锁
Thread.sleep(100);
}
// 执行业务代码
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (millis < timeout) {
stringRedisTemplate.delete(lock);
}
}
}
- setnx:只有第一个线程会执行成功,返回 true,其余线程执行失败,返回 false。
- getSet:返回 key 中的旧值,并把新的值 set 进去。
- 细细看来,好像似乎 setnx 命令就能够实现分布式锁了,为什么还要 getSet 命名呢?getSet 命令是为了解决客户端异常宕机,锁没有被正常释放的情况下,结合过期时间来保证线程安全。可以看看官网的介绍,有详细解释这个问题。
zookeeper 锁
zookeeper,天生的分布式协调工具,生来就是为了解决各种分布式的难题,比如分布式锁、分布式计数器、分布式队列等等。
zookeeper 分布式锁,如果自己实现的话,大抵的实现方式如下:
公平锁:
- 在 zookeeper 的指定节点(locks)下创建临时顺序节点 node_n ;
- 获取 locks 下面的所有子节点 children。
- 对子节点按节点自增序号从小到大排序。
- 判断本节点是不是第一个子节点,如果是,则获取到锁。如果不是,则监听比该节点小的那个节点的删除事件。
- 若监听事件生效,则回到第二步重新判断,直到获取到锁。
不公平锁
- 在 zookeeper 的某个节点(lock)上创建临时节点 znode。
- 创建成功,就表示获取到了这个锁;其他客户端来创建锁会失败,只能注册对这个锁的监听。
- 其他客户端监听到这个锁被释放(znode节点被删除),就会尝试加锁(创建节点),继续执行第二步。
幸运的是,zookeeper recipes 客户端为我们提供了多种分布式锁实现:
- InterProcessMutex(可重入排他锁)
- InterProcessSemaphoreMutex(不可重入排他锁)
- InterProcessReadWriteLock(分布式读写锁)
- InterProcessSemaphore(共享信号量 —— 设置最大并行数量)
zookeeper recipes 锁的简单使用:
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.14</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.0.1</version>
</dependency>
public InterProcessMutex interProcessMutex(String lockPath) {
CuratorFramework client = CuratorFrameworkFactory.newClient(zookeeper, new ExponentialBackoffRetry(1000, 3));
// 启用命名空间,做微服务间隔离
client.usingNamespace(namespace);
client.start();
return new InterProcessMutex(client, lockPath);
}
public void lockUse() {
InterProcessMutex interProcessMutex = interProcessMutex("/lockpath");
try {
// 获取锁
if (interProcessMutex.acquire(100, TimeUnit.MILLISECONDS)) {
// 执行业务代码
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 释放锁
try {
interProcessMutex.release();
} catch (Exception e) {
e.printStackTrace();
}
}
}
- 推荐一篇 zookeeper 介绍很全面的文章:https://www.cnblogs.com/shamo89/p/9800925.html
比较
- 缓存分布式锁,必须采用轮询的方式去尝试加锁,对性能浪费很大;zookeeper 分布式锁,可以通过监听的方式等待通知或超时,当有锁释放,通知使用者即可。
- 如果缓存获取锁的那个客户端宕机了,锁不会被释放,只能通过其它方式解决(上面的 getSet 判断);而 zookeeper 的话,因为创建的是临时 znode,只要客户端挂了,znode 就没了,此时就自动释放锁。
基于缓存或zookeeper的分布式锁实现的更多相关文章
- 基于 Zookeeper 的分布式锁实现
1. 背景 最近在学习 Zookeeper,在刚开始接触 Zookeeper 的时候,完全不知道 Zookeeper 有什么用.且很多资料都是将 Zookeeper 描述成一个“类 Unix/Linu ...
- 基于Zookeeper的分布式锁
实现分布式锁目前有三种流行方案,分别为基于数据库.Redis.Zookeeper的方案,其中前两种方案网络上有很多资料可以参考,本文不做展开.我们来看下使用Zookeeper如何实现分布式锁. 什么是 ...
- 10分钟看懂!基于Zookeeper的分布式锁
实现分布式锁目前有三种流行方案,分别为基于数据库.Redis.Zookeeper的方案,其中前两种方案网络上有很多资料可以参考,本文不做展开.我们来看下使用Zookeeper如何实现分布式锁. 什么是 ...
- 基于ZooKeeper实现——分布式锁与实现
引言 ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件.它是一个为分布式应用提供一致性服务的软件,提 ...
- 【连载】redis库存操作,分布式锁的四种实现方式[一]--基于zookeeper实现分布式锁
一.背景 在电商系统中,库存的概念一定是有的,例如配一些商品的库存,做商品秒杀活动等,而由于库存操作频繁且要求原子性操作,所以绝大多数电商系统都用Redis来实现库存的加减,最近公司项目做架构升级,以 ...
- 分布式锁(3) ----- 基于zookeeper的分布式锁
分布式锁系列文章 分布式锁(1) ----- 介绍和基于数据库的分布式锁 分布式锁(2) ----- 基于redis的分布式锁 分布式锁(3) ----- 基于zookeeper的分布式锁 代码:ht ...
- 基于Zookeeper的分布式锁(干干干货)
原文地址: https://juejin.im/post/5df883d96fb9a0163514d97f 介绍 为什么使用锁 锁的出现是为了解决资源争用问题,在单进程环境下的资源争夺可以使用 JDK ...
- 基于zookeeper实现分布式锁和基于redis实现分布所的区别
1,实现方式不同 zookeeper实现分布式锁:通过创建一个临时节点,创建的成功节点的服务则抢占到分布式锁,可做业务逻辑.当业务逻辑完成,连接中断,节点消失,继续下一轮的锁的抢占. redis实现分 ...
- java就业指南 zookeeper分布式系统 zookeeper实现分布式锁 有用
目前几乎很多大型网站及应用都是分布式部署的,分布式场景中的数据一致性问题一直是一个比较重要的话题.分布式的CAP理论告诉我们“任何一个 分布式系统都无法同时满足一致性(Consistency).可用性 ...
随机推荐
- Qt侠:像写诗一样写代码,玩游戏一样的开心心情,还能领工资!
[软]上海-Qt侠 2017/7/12 16:11:20我完全是兴趣主导,老板不给我钱,我也要写好代码!白天干,晚上干,周一周五干,周末继续干!编程已经深入我的基因,深入我的骨髓,深入我的灵魂!当我解 ...
- WPF 为资源字典 添加事件响应的后台类
原文:WPF 为资源字典 添加事件响应的后台类 前言,有许多同学在写WPF程序时在资源字典里加入了其它控件,但又想写事件来控制这个控件,但是资源字典没有CS文件,不像窗体XAML还有一个后台的CS文件 ...
- 【shell】分享高通平台刷版本简单的一个shell脚本
#!/bin/shadb wait-for-deviceadb reboot bootloaderecho "start download"wait 5sudo fastboot ...
- win10中使用sqlserver2008r2 SQL Server 配置管理器
原文:win10中使用sqlserver2008r2 SQL Server 配置管理器 使用 Windows10 访问 SQL Server 配置管理器 因为 SQL Server 配置管理器是 Mi ...
- [转]Android 如何有效的解决内存泄漏的问题
Android 如何有效的解决内存泄漏的问题 前言:最近在研究Handler的知识,其中涉及到一个问题,如何避免Handler带来的内存溢出问题.在网上找了很多资料,有很多都是互相抄的,没有实际的 ...
- 【Qt】无边框窗体中带有ActiveX组件时的一个BUG
无意中发现的一个BUG,Qt5.1.1正式版首先创建一个GUI工程,拖入一个QAxWidget控件(为了使ActiveX生效,需要在.pro文件中加入CONFIG += qaxcontainer)接着 ...
- 从Java和JavaScript来学习Haskell和Groovy
直击现场 记得刚接触计算机的时候,我就受到了两个非常巨大的错误观念的影响,这个观念最初是来自于老师的传授还是学长的教诲已经记不清了,但是直到我工作几年以后,才慢慢有了实际的体会: 学习和使用什么编程语 ...
- Qt4.8.6详细安装步骤(使用了i686-4.8.2-release-posix-dwarf-rt_v3-rev3,手动设置gcc和gdb)非常清楚 good
摘要 在网上查看了很多篇关于Qt 4的安装方法,都是以前很久的帖子,所以就想按自己的方式重新总结一下,希望可以帮助到大家. Qt5的安装比较简单只需要下载一个文件qt-opensource-windo ...
- 记一次 qW3xT.4,解决挖矿病毒。
最近感觉我的服务器特别卡,打开数据库都半天,刚开始以为网咯不好也没太在意. 利用top命令: 这时候问题出来了,最高cpu占用100%,那我用啥??? 根据进程id 一看究竟,ps -ef|grep ...
- 分页组件与CBV
一. 自定义分页 1.准备工作 (1).首先在models.py中创建一张book表用来存储数据 from django.db import models class Book(models.Mode ...