前言

分布式锁一般有三种实现方式:

  1. 数据库乐观锁;2. 基于Redis的分布式锁;3. 基于ZooKeeper的分布式锁。

本篇博客将介绍第二种方式,基于Redis实现分布式锁。

虽然网上已经有各种介绍Redis分布式锁实现的博客,然而他们的实现却有着各种各样的问题,为了避免误人子弟,本篇博客将详细介绍如何正确地实现Redis分布式锁。

可靠性

首先,为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件:

互斥性:在任意时刻,只有一个客户端能持有锁。

不会发生死锁:即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。

具有容错性:只要大部分的Redis节点正常运行,客户端就可以加锁和解锁。

解铃还须系铃人:加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。

代码实现

一、引入redis 依赖

<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency> 备注:根据版本不同,jedis 的set 方法也有所不同

二、配置文件增加redis 配置

# redis.properties 配置文件:

# region Redis jedis
# redis配置开始 # Redis数据库索引(默认为0)
spring.redis.database=0 # Redis服务器地址
spring.redis.host=localhost # Redis服务器连接端口
spring.redis.port=6379 # Redis服务器连接密码(默认为空)
spring.redis.password=redis123456 # 连接池最大连接数(使用负值表示没有限制)
spring.redis.jedis.pool.max-active=1024 # 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.jedis.pool.max-wait=10000 # 连接池中的最大空闲连接
spring.redis.jedis.pool.max-idle=100 # 连接池中的最小空闲连接
spring.redis.jedis.pool.min-idle=5 # 连接超时时间(毫秒)
spring.redis.timeout=10000 # 连接耗尽时是否阻塞, false报异常,ture阻塞直到超时
spring.redis.block-when-exhausted=true # endregion

三、利用Spring 把JedisPool 加入Bean 工厂

@Configuration
@PropertySource("classpath:application.properties")
@Slf4j
public class RedisConfig { @Value("${spring.redis.host}")
private String host; @Value("${spring.redis.port}")
private int port; @Value("${spring.redis.timeout}")
private int timeout; @Value("${spring.redis.jedis.pool.max-idle}")
private int maxIdle; @Value("${spring.redis.jedis.pool.max-wait}")
private long maxWaitMillis; @Value("${spring.redis.password}")
private String password; @Value("${spring.redis.block-when-exhausted}")
private boolean blockWhenExhausted; @Bean
public JedisPool redisPoolFactory() throws Exception {
log.info("JedisPool注入开始!!");
log.info("redis地址:" + host + ":" + port);
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxIdle(maxIdle);
jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
// 连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true
jedisPoolConfig.setBlockWhenExhausted(blockWhenExhausted);
// 是否启用pool的jmx管理功能, 默认true
jedisPoolConfig.setJmxEnabled(true);
JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout, password);
log.info("JedisPool注入成功!!");
return jedisPool;
}
}

四、封装RedisService 对外提供服务

@Service
@Slf4j
public class RedisService { //Redis 成功返回结果标识
private static final String LOCK_SUCCESS = "OK";
//Reis 操作返回成功
private static final Long RELEASE_SUCCESS = 1L;
//Redis锁不存在时才设置成功
private static final String SET_IF_NOT_EXIST = "NX";
//Redis锁超时时间单位
private static final String SET_WITH_EXPIRE_TIME = "PX"; @Autowired
private JedisPool jedisPool; /**
* 尝试获取分布式锁
*
* @param lockKey 锁
* @param requestId 请求唯一标识(可以通过uuid等方式获取唯一ID)
* @param expireTime 超期时间(毫秒)
* @return 是否获取成功
*/
public boolean tryGetDistributedLock(String lockKey, String requestId, int expireTime) { Jedis jedis = jedisPool.getResource(); //Jedis 3.0.0 以前的版本
//String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime); //Jedis 3.0.0 及以后的版本
SetParams setParams = SetParams.setParams().nx().px(expireTime);
String result = jedis.set(lockKey, requestId, setParams); return LOCK_SUCCESS.equals(result);
} /**
* 释放分布式锁
*
* @param lockKey 锁
* @param requestId 请求唯一标识(加锁时用的唯一标识)
* @return 是否释放成功
*/
public boolean releaseDistributedLock(String lockKey, String requestId) { Jedis jedis = jedisPool.getResource(); String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId)); return RELEASE_SUCCESS.equals(result);
}
}

五、简单解释

1. 利用redis 的set 命令的 5个参数保证操作的原子性

2. 利用Lua 脚本保证在释放锁时的原子性

3. 利用requestId 唯一标识保证不会释放别人的锁

通过Redis 实现分布式锁_利用Jedis 客户端的更多相关文章

  1. 利用redis实现分布式锁知识点总结及相关改进

    利用redis实现分布式锁知识点总结及相关改进 先上原文,本文只为总结及对相关内容的质疑并提出若干意见,原文内容更详细https://www.cnblogs.com/linjiqin/p/800383 ...

  2. 利用redis实现分布式锁

    分布式锁一般有三种实现方式: 1. 数据库乐观锁: 2. 基于ZooKeeper的分布式锁: 3. 基于Redis的分布式锁: 这里大概说一下三种方式的优缺点,数据库乐观锁优点是实现简单,只需要for ...

  3. spring boot 利用redisson实现redis的分布式锁

    原文:http://liaoke0123.iteye.com/blog/2375469 利用redis实现分布式锁,网上搜索的大部分是使用java jedis实现的. redis官方推荐的分布式锁实现 ...

  4. 利用多写Redis实现分布式锁原理与实现分析(转)

    利用多写Redis实现分布式锁原理与实现分析   一.关于分布式锁 关于分布式锁,可能绝大部分人都会或多或少涉及到. 我举二个例子:场景一:从前端界面发起一笔支付请求,如果前端没有做防重处理,那么可能 ...

  5. 基于 Redis 的分布式锁

    前言 分布式锁在分布式应用中应用广泛,想要搞懂一个新事物首先得了解它的由来,这样才能更加的理解甚至可以举一反三. 首先谈到分布式锁自然也就联想到分布式应用. 在我们将应用拆分为分布式应用之前的单机系统 ...

  6. 基于redis的分布式锁(转)

    基于redis的分布式锁 1 介绍 这篇博文讲介绍如何一步步构建一个基于Redis的分布式锁.会从最原始的版本开始,然后根据问题进行调整,最后完成一个较为合理的分布式锁. 本篇文章会将分布式锁的实现分 ...

  7. 基于redis的分布式锁(不适合用于生产环境)

    基于redis的分布式锁 1 介绍 这篇博文讲介绍如何一步步构建一个基于Redis的分布式锁.会从最原始的版本开始,然后根据问题进行调整,最后完成一个较为合理的分布式锁. 本篇文章会将分布式锁的实现分 ...

  8. redis系列:基于redis的分布式锁

    一.介绍 这篇博文讲介绍如何一步步构建一个基于Redis的分布式锁.会从最原始的版本开始,然后根据问题进行调整,最后完成一个较为合理的分布式锁. 本篇文章会将分布式锁的实现分为两部分,一个是单机环境, ...

  9. 基于redis的分布式锁的分析与实践

    ​ 前言:在分布式环境中,我们经常使用锁来进行并发控制,锁可分为乐观锁和悲观锁,基于数据库版本戳的实现是乐观锁,基于redis或zookeeper的实现可认为是悲观锁了.乐观锁和悲观锁最根本的区别在于 ...

随机推荐

  1. suseoj 1209: 独立任务最优调度问题(动态规划)

    1209: 独立任务最优调度问题 时间限制: 1 Sec  内存限制: 128 MB提交: 3  解决: 2[提交][状态][讨论版][命题人:liyuansong] 题目描述 用2台处理机A和B处理 ...

  2. python高阶函数的使用

    目录 python高阶函数的使用 1.map 2.reduce 3.filter 4.sorted 5.小结 python高阶函数的使用 1.map Python内建了map()函数,map()函数接 ...

  3. Blocked a frame with origin XXX from accessing a cross-origin 。iframe跨域问题

    在前端开发的过程中,我们常常会用到iframe去在我们的页面中引用一个子页面,而父子页面又常常会有交互.在同域情况下,子页面如果想要访问父页面中的window对象中的方法的话,直接在当前页面中使用wi ...

  4. 揭秘String类型背后的故事——带你领略汇编语言魅力

    字符串或串(String)是由数字.字母.下划线组成的一串字符.一般记为 s=“a1a2···an”(n>=0).它是编程语言中表示文本的数据类型.在程序设计中,字符串(string)为符号或数 ...

  5. Logistic回归算法梯度公式的推导

    最近学习Logistic回归算法,在网上看了许多博文,笔者觉得这篇文章http://blog.kamidox.com/logistic-regression.html写得最好.但其中有个关键问题没有讲 ...

  6. 使用JSCH执行命令并读取终端输出的一些使用心得

    使用Jsch执行命令,并读取终端输出 jsch http://www.jcraft.com/jsch/ Jsch是java实现的一个SSH客户端.开发JSCH的公司是 jcraft: JCraft成立 ...

  7. react修改端口

    react修改端口 在react官网根据文档安装好项目之后,发现新项目没有了scripst文件夹 之前版本是在scripts文件夹中的starts.js中修改 新版本修改port发现移入到了依赖里面 ...

  8. IDEA如何重置窗口布局

    如何重置窗口布局 我不知道怎么搞的,左边的,上边的,下边的,视图都没有了 , 重启了一下,然后重置为默认视图,就好了

  9. 用墨卡托和GPS坐标计算距离时误差测试

    iOS墨卡托和GPS坐标计算距离时误差测试,测试结果: 墨卡托和gps坐标来回转换没有误差. 墨卡托坐标计算出的距离比gps坐标计算出的距离大,100/92*100 = 108米,每100米多算出8米 ...

  10. 图解leetcode —— 395. 至少有K个重复字符的最长子串

    前言: 每道题附带动态示意图,提供java.python两种语言答案,力求提供leetcode最优解. 描述: 找到给定字符串(由小写字符组成)中的最长子串 T , 要求 T 中的每一字符出现次数都不 ...