分布式应用下的Redis单机锁设计与实现
背景
最近写了一个定时任务,期望是同一时间只有一台机器运行即可。因为是应用是在集群环境下跑的,所以需要自己实现类一个简陋的Redis单机锁。
原理
主要是使用了Redis的SET NX特性,成功设置的那个客户端则被认为拿到了锁,没有设置成功的其他客户单则认为没有拿到锁。
在分布式环境下使用锁是挺危险的一件事情,我们可能会遇到一些问题:
- Redis单点故障;
- 应用与Redis网络不通;
- 应用异常导致锁没有得到释放;
- 误操作锁。
对于问题1,避免Redis单点故障,可以使用Redis分布式锁的实现,提供了多种语言的开源实现(http://redis.io/topics/distlock),也可以使用其他配置管理类组件实现(比如:zk、consul、etcd等),不在此文讨论。
对于问题2,我们需要把业务限定在不强依赖Redis锁的范围,虽然绝大多数情况不会发生问题,但是不能完全保证Redis锁不出问题。
对于问题3,为了防止异常引起的死锁情况,需要为每个锁设置超时时间,以确保不会因为应用问题导致无法释放锁。同时要设置一个合理的超时时间以免,达不到或者削弱锁的效果。
对于问题4,为每个锁存储一个标记,当解锁的时候,进行验证,用以保证每个客户端只能操作自己的锁。
实现
public class RedisLock {
private static final Logger logger = LoggerFactory.getLogger(RedisLocker.class);
// update on 2019-11-08 22:55:00 修改成线程安全版本
private final Map<String, String> LOCK_MAP = new ConcurrentHashMap<String, String>(4);
private JedisPool pool;
public enum EXPX {
EX,PX
}
public boolean tryAcquire(String topic, EXPX expx,long time) throws Exception{
if (!LOCK_MAP.containsKey(topic)) {
String uuid = UUID.randomUUID().toString();
Jedis jedis = null;
// 存在key,返回空,不存在返回OK
try {
jedis = pool.getResource();
String result = jedis.set(topic, uuid, "NX", expx.name(), time);
logger.error("获取返回值,result {}", result);
if ("OK".equals(result)) {
LOCK_MAP.put(topic, uuid);
return true;
}
logger.error("获取锁失败,result {}", result);
} catch (Exception e) {
pool.returnBrokenResource(jedis);
throw e;
}finally {
pool.returnResource(jedis);
}
}
return false;
}
public boolean unlock(String topic) {
Jedis jedis = null;
try {
jedis = pool.getResource();
String random = jedis.get(topic);
if (random != null && random.equals(LOCK_MAP.get(topic))) {
jedis.del(topic);
}
LOCK_MAP.remove(topic);
} catch (Exception e) {
pool.returnBrokenResource(jedis);
throw e;
} finally {
pool.returnResource(jedis);
}
return true;
}
public void setPool(JedisPool pool) {
this.pool = pool;
}
}
总结
本文做了一种简单暴力的锁的实现。没有做高可用的方案,主要胜在轻量,简单,在平时场景可以使用。
除了使用配置中心相关的组件也是可以实现锁的,并且是分布式的锁。
关于RedisPool的两点需要注意:
- 在锁的场景,testOnBorrow应该为true;
- 设置合理的空闲连接数;
- 设置连接上限,防止占用过多资源。
分布式应用下的Redis单机锁设计与实现的更多相关文章
- redis互斥锁简易设计原理【原】
redis互斥锁设计 方式一: 使用 set(arg1,arg2,arg3,arg4,arg5) 绿线部分代码 //如果不存在就设置,且设置成功60秒后key自动失效,成功会返回字符串"OK ...
- 亿级流量场景下,大型缓存架构设计实现【1】---redis篇
*****************开篇介绍**************** -------------------------------------------------------------- ...
- 【分布式缓存系列】集群环境下Redis分布式锁的正确姿势
一.前言 在上一篇文章中,已经介绍了基于Redis实现分布式锁的正确姿势,但是上篇文章存在一定的缺陷——它加锁只作用在一个Redis节点上,如果通过sentinel保证高可用,如果master节点由于 ...
- 使用Redis实现锁(支持分布式应用)(整理网络资料)
使用Redis实现锁(支持分布式应用) 1. 简介 使用Redis指令setnx.expire.getset等操作实现互斥资源的访问 本文内容来着网络整理,参考: http://www.linu ...
- 探索Redis设计与实现15:Redis分布式锁进化史
本文转自互联网 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial ...
- 基于Redis的分布式锁设计
前言 基于Redis的分布式锁实现,原理很简单嘛:检测一下Key是否存在,不存在则Set Key,加锁成功,存在则加锁失败.对吗?这么简单吗? 如果你真这么想,那么你真的需要好好听我讲一下了.接下来, ...
- 利用redis 分布式锁 解决集群环境下多次定时任务执行
定时任务: @Scheduled(cron= "0 39 3 * * *") public void getAllUnSignData(){ //检查任务锁,若其它节点的相同定时任 ...
- [原创] PHP 使用Redis实现锁
目录 锁实现的注意点 加锁 connect 与 pconnect 解锁 Redis 中使用 Lua 脚本的注意点 Redis集群分布式锁 RedLock 算法 锁实现的注意点 互斥: 任意时刻, 只能 ...
- Redis分布式锁实战
什么是分布式锁 在单机部署的情况下,要想保证特定业务在顺序执行,通过JDK提供的synchronized关键字.Semaphore.ReentrantLock,或者我们也可以基于AQS定制化锁.单机部 ...
随机推荐
- final finally finalize 区别
public class Demo { public static void main(String[] args) { long start = System.currentTimeMillis() ...
- iOS通知
链接: IOS之推送通知(本地推送和远程推送) iOS 10推送通知开发 活久见的重构 - iOS 10 UserNotifications 框架解析 iOS10全新推送功能的实现
- JS实现Observable观察者模式
欢迎讨论与交流 : ) 注 代码参考自——汇智网 RxJS教程 前言 Observable观察者模式令小白笔者眼前一亮.数据生产者(observable)负责生产新鲜的数据,同时在生产完毕后'通知“消 ...
- Swift之 ? 和 !
Swift之 ? 和 ! 转载于:http://joeyio.com/ios/2014/06/04/swift---/ Swift语言使用var定义变量,但和别的语言不同,Swift里不会自动给变量赋 ...
- jQuery下拉框扩展和美化插件Chosen
Chosen 是一个支持jquery的select下拉框美化插件,它能让丑陋的.很长的select选择框变的更好看.更方便.不仅如此,它更扩展了select,增加了自动筛选的功能.它可对列表进行分组, ...
- AutoHotKey实现将站点添加到IE的Intranet本地站点
最近在内部推行CRM系统,其中的CPQ组件要求必须将站点加入到"本地Intranet”才可以正常使用,但是由于使用用户比较多(超过几千人),并且每个用户的计算机水平都不一样,所以让用户手工去 ...
- CCPC2016沈阳站
A.模拟 B.模拟 C(hdu5950):(矩阵快速幂) 题意:求f(n)=2f(n-2)+f(n-1)+n^4 分析:矩阵快速幂,(f(n),f(n-1),n^4,n^3,n^2,n,1) 注意:矩 ...
- MongoDB的基本操作
环境:CentOS6.8 Mongodb3.2.10 启动 启动mongoDB服务器 # service mongod start 启动mongoDB客户端 # mongo 该客户端是一个JavaS ...
- smarty下如何将一个数保存为两位小数
smarty模板是一种缓存技术,下面介绍一下smarty string_format用法 取小数点后2位: 用法如下: //index.php$smarty = new Smarty; $smarty ...
- React.js常识
1.React的特点: 简单.声明式-自动dom操作. React核心:组件,组件的设计目的:提高代码复用率,降低测试难度和代码复杂度. 提高代码复用率:组建将数据和逻辑进行封装,类似面向对象的类: ...