背景

最近写了一个定时任务,期望是同一时间只有一台机器运行即可。因为是应用是在集群环境下跑的,所以需要自己实现类一个简陋的Redis单机锁。

原理

主要是使用了Redis的SET NX特性,成功设置的那个客户端则被认为拿到了锁,没有设置成功的其他客户单则认为没有拿到锁。

在分布式环境下使用锁是挺危险的一件事情,我们可能会遇到一些问题:

  1. Redis单点故障;
  2. 应用与Redis网络不通;
  3. 应用异常导致锁没有得到释放;
  4. 误操作锁。

对于问题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的两点需要注意:

  1. 在锁的场景,testOnBorrow应该为true;
  2. 设置合理的空闲连接数;
  3. 设置连接上限,防止占用过多资源。

分布式应用下的Redis单机锁设计与实现的更多相关文章

  1. redis互斥锁简易设计原理【原】

    redis互斥锁设计 方式一: 使用 set(arg1,arg2,arg3,arg4,arg5) 绿线部分代码 //如果不存在就设置,且设置成功60秒后key自动失效,成功会返回字符串"OK ...

  2. 亿级流量场景下,大型缓存架构设计实现【1】---redis篇

    *****************开篇介绍**************** -------------------------------------------------------------- ...

  3. 【分布式缓存系列】集群环境下Redis分布式锁的正确姿势

    一.前言 在上一篇文章中,已经介绍了基于Redis实现分布式锁的正确姿势,但是上篇文章存在一定的缺陷——它加锁只作用在一个Redis节点上,如果通过sentinel保证高可用,如果master节点由于 ...

  4. 使用Redis实现锁(支持分布式应用)(整理网络资料)

    使用Redis实现锁(支持分布式应用) 1.    简介 使用Redis指令setnx.expire.getset等操作实现互斥资源的访问 本文内容来着网络整理,参考: http://www.linu ...

  5. 探索Redis设计与实现15:Redis分布式锁进化史

    本文转自互联网 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial ...

  6. 基于Redis的分布式锁设计

    前言 基于Redis的分布式锁实现,原理很简单嘛:检测一下Key是否存在,不存在则Set Key,加锁成功,存在则加锁失败.对吗?这么简单吗? 如果你真这么想,那么你真的需要好好听我讲一下了.接下来, ...

  7. 利用redis 分布式锁 解决集群环境下多次定时任务执行

    定时任务: @Scheduled(cron= "0 39 3 * * *") public void getAllUnSignData(){ //检查任务锁,若其它节点的相同定时任 ...

  8. [原创] PHP 使用Redis实现锁

    目录 锁实现的注意点 加锁 connect 与 pconnect 解锁 Redis 中使用 Lua 脚本的注意点 Redis集群分布式锁 RedLock 算法 锁实现的注意点 互斥: 任意时刻, 只能 ...

  9. Redis分布式锁实战

    什么是分布式锁 在单机部署的情况下,要想保证特定业务在顺序执行,通过JDK提供的synchronized关键字.Semaphore.ReentrantLock,或者我们也可以基于AQS定制化锁.单机部 ...

随机推荐

  1. Java集合之泛型的使用

    Java集合之泛型的使用 泛型提供了一种轻便灵活的数据操作,数据的安全性相对提高. 泛型提供了对列表元素的约束条件,比如ArrayList有序链表,可存储任意类型的元素. 此处构建一个ArrayLis ...

  2. HTML 学习笔记 JavaScript(创建对象)

    原博地址:http://www.cnblogs.com/dolphinX/p/3288118.html JavaScript 有Date Array String等这样的内置对象,功能强大实用简单,但 ...

  3. BZOJ 1853 【Scoi2010】 幸运数字

    Description 在中国,很多人都把6和8视为是幸运数字!lxhgww也这样认 为,于是他定义自己的"幸运号码"是十进制表示中只包含数字6和8的那些号码,比如68,666,8 ...

  4. javaScript 中的布尔运算符 && 和 ||

    布尔运算符 && 和 ||的返回结果不一定是布尔值!由此来展开一定的研究及理解. 1.首先先介绍下常见的数据类型转化为bool后的值. (常用地方)在if表达式中,javascript ...

  5. 快速部署tomcat项目的Shell脚本

    为了做集群测试,在每台机器上装了3个tomcat,每次发布项目的时候都要反复敲一些命令,重启tomcat之前先检查tomcat进程有没有停掉,没有还要手动kill该进程. 发布次数多了,操作就比较繁琐 ...

  6. RGW/SWIFT对象存储性能测试工具--COSBench安装

    Cosbench是Intel的开源云存储性能测试软件,COSBench目前已经广泛使用与云存储测试,并作为云存储的基准测试工具使用 https://github.com/intel-cloud/cos ...

  7. 【CF 710F】String Set Queries

    在校内OJ上A了,没有加强制在线的东西..不放链接了. 这道题题意是维护一个字符串集合,支持三种操作: 1.加字符串 2.删字符串 3.查询集合中的所有字符串在给出的模板串中出现的次数 操作数\(m ...

  8. hdu3415 单调队列

    Max Sum of Max-K-sub-sequence Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & ...

  9. JavaScript中URL的解码和编码

    这些URI方法encodeURI.encodeURIComponent().decodeURI().decodeURIComponent()代替了BOM的escape()和unescape()方法. ...

  10. Spring 4支持的Java 8新特性一览

    有众多新特性和函数库的Java 8发布之后,Spring 4.x已经支持其中的大部分.有些Java 8的新特性对Spring无影响,可以直接使用,但另有些新特性需要Spring的支持.本文将带您浏览S ...