前言

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

  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. python:类4——魔法方法(定制序列、迭代)、生成器、推导式

    一.定制序列(容器类型) http://bbs.fishc.com/forum.php?mod=viewthread&tid=48793&extra=page%3D1%26filter ...

  2. VS Code 之 Jupyter NoteBook 初试

    一.前言 在今年九月的 PyCon China 大会上,官宣了一项 VS Code Python 的全新功能:Visual Studio Code Python 插件将提供 Jupyter Noteb ...

  3. Andorid开发中遇到的问题

    最近开始学习开发Android App,找了本教程,学了一些基本知识后,就开始着手做一个例子. 我始终觉得在做中学,可能会稍微快一点.很快,一个具有初步功能的App被我撸出来了. 在模拟器上运行,我发 ...

  4. requirements.txt的创建及使用

    python的包管理 pip方式: 创建 (venv) $ pip freeze >requirements.txt 执行 (venv) $ pip install -r requirement ...

  5. Install python3

    wget https://www.python.org/ftp/python/3.7.4/Python-3.7.4.tgz tar xf Python-3.7.4.tgz cd Python-3.7. ...

  6. elementui分页记录,reserve-selection

    第一步:在<el-table></el-table>标签中加上 :row-key="getRowKeys" 第二步:在<el-table-column ...

  7. call() 、 apply() 、bind()方法的作用和区别!

    从一开始,我是在书上看到关于bind().call() 和 apply(), 不过长久以来,在工作中与网上接触到了很多关于这三个方法的使用场景,对这三个方法也算是比较熟悉了.所以把他们的作用和区别简单 ...

  8. MacOS安装Docker傻瓜式教程

    最近电脑越来越卡了,为了减少系统开销,以及后期维护方便,所以考虑将本地安装一些服务迁移到docker中去管理,这一切的基础是要先有docker服务,所以本文就先记录怎样在mac上安装配置docker, ...

  9. Spire.Cloud.Word 添加Word水印(文本水印、图片水印)

    概述 Spire.Cloud.Word提供了watermarksApi接口可用于添加水印,包括添加文本水印(SetTextWatermark).图片水印(SetImageWatermark),本文将对 ...

  10. 【开发者portal在线开发插件系列三】字符串 及 可变长度字符串

    基础篇 基础场景见上面两个帖子,这里单独说明字符串和可变长度字符串的用法. 话不多说,开始今天的演(表)示(演) Profile和插件开发 添加一个string类型的属性: 在插件里添加一条数据上报消 ...