分布式应用下的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定制化锁.单机部 ...
随机推荐
- NUOJ 88
思路1: 找规律,本题若是直接暴力搜索,就是f(n)=2^n-1,然后f(n)%1000000,那么结果会越界:所以考虑用f(n+1)=(2*f(n)+1)%1000000,不过遇到较大数据的时候,会 ...
- request.getcontextPath() 详解
request.getcontextPath() 详解 文章分类:Java编程 <%=request.getContextPath()%>是为了解决相对路径的问题,可返回站点的根路径. 但 ...
- C语言编程实现Linux命令——who
C语言编程实现Linux命令--who 实践分析过程 who命令是查询当前登录的每个用户,它的输出包括用户名.终端类型.登录日期及远程主机,在Linux系统中输入who命令输出如下: 我们先man一下 ...
- npm+node+cordova+ionic 版本匹配
npm 2.15.8 node 4.4.7 cordova 6.1.0 ionic 1.7.16
- Javascript读写文件
<script type="text/javascript" language="javascript"> //读文件 function readF ...
- [转]在html中控制自动换行
其实只要在表格控制中添加一句 <td style="word-break:break-all">就搞定了. 其中可能对英文换行可能会分开一个单词问题:解决如下: 语法: ...
- 【51Nod 1501】【算法马拉松 19D】石头剪刀布威力加强版
http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1501 dp求出环状不连续的前缀和,剩下东西都可以算出来,比较繁琐. 时间 ...
- IIS注册.netframework4.0指令
C:\Windows\Microsoft.NET\Framework\v4.0.30319\aspnet_regiis.exe -i
- mysql习惯及主从复制参数设置
mysql 重复数据插入 replace into t(id, update_time) values(1, now()); 或 replace into t(id, update_time) sel ...
- oracle---jdbctest--laobai
import java.sql.CallableStatement; import java.sql.Connection; import java.sql.ResultSet; import ora ...