Redis分布式锁应用
Redis锁的使用
起因:分布式环境下需对并发进行逻辑一致性控制
架构:springboot2、Redis
IDEA实操
先新建RedisLock组件
注:释放锁使用lua脚本保持原子性@Component
@Slf4j
public class RedisLock { private final RedisTemplate redisTemplate; public RedisLock(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
} /**
* 如果已经存在返回false,否则返回true
*
* @param key
* @param value
* @return
*/
public Boolean setNx(String key, String value, Long expireTime, TimeUnit mimeUnit) { if (key == null || value == null) {
return false;
} // 在spiring boot 2 可以直接使用 redisTemplate的setIfAbsent设置key-value和过期时间,是原子性
Boolean tf = redisTemplate.opsForValue().setIfAbsent(key, value, expireTime, mimeUnit); return tf; } /**
* 获取数据
*
* @param key
* @return
*/
public Object get(String key) { if (key == null) {
return null;
}
return redisTemplate.opsForValue().get(key);
} /**
* 删除
*
* @param key
* @return
*/
public void remove(Object key) { if (key == null) {
return;
} redisTemplate.delete(key);
} /**
* 加锁
*
* @param key key
* @param waitTime 等待时间,在这个时间内会多次尝试获取锁,超过这个时间还没获得锁,就返回false
* @param interval 间隔时间,每隔多长时间尝试一次获的锁
* @param expireTime key的过期时间
*/
public Boolean lock(String key, Long waitTime, Long interval, Long expireTime) { String value = UUID.randomUUID().toString().replaceAll("-", "").toLowerCase(); Boolean flag = setNx(key, value, expireTime, TimeUnit.MILLISECONDS); // 尝试获取锁 成功返回
if (flag) {
return true;
} else {
// 获取失败 // 现在时间
long newTime = System.currentTimeMillis(); // 等待过期时间
long loseTime = newTime + waitTime; // 不断尝试获取锁成功返回
while (System.currentTimeMillis() < loseTime) { Boolean testFlag = setNx(key, value, expireTime, TimeUnit.MILLISECONDS);
if (testFlag) {
return true;
} try {
Thread.sleep(interval);
} catch (InterruptedException e) {
log.error("获取锁异常", e);
}
}
}
return false;
} /**
* 释放锁
*
* @param key
* @return
*/
public void unLock(String key) {
remove(key);
} public Boolean setIfAbsent(String key, String value) {
Boolean tf = redisTemplate.opsForValue().setIfAbsent(key, value);
redisTemplate.expire(key, 60, TimeUnit.DAYS);
return tf;
} /**
* 获取分布式锁
* @param key
* @param value
* @param expireTime
* @return
*/
public Boolean lock(String key, String value, Long expireTime) {
Boolean flag = setNx(key, value, expireTime, TimeUnit.MILLISECONDS);
if (flag == null || !flag) {
return false;
} return true;
} /***
* lua释放分布式锁
* @param key
* @param value
*/
public void unLock(String key, String value) {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
Long result = (Long) redisTemplate.execute(redisScript, Arrays.asList(key),value);
if (result == null || result == 0) {
log.info("释放锁(" + key + "," + value + ")失败,该锁不存在或锁已经过期");
} else {
log.info("释放锁(" + key + "," + value + ")成功");
}
} /***
* lua不存在才插入队列
* @param key
* @param values
* jackson序列化后String会变成\"xxx\",不要直接用ARGV去转成number使用
*/
public void putAllIfAbsent(String key, List<String> values,long timeout,TimeUnit unit) {
Long rawTimeout = TimeoutUtils.toMillis(timeout, unit);
String script = "if redis.call('exists', KEYS[1]) == 0 then local listValues = ARGV" +
" for k,v in ipairs(listValues) do " +
" redis.call('RPUSH',KEYS[1],v) " +
" end " +
" redis.call('expire',KEYS[1],KEYS[2])" +
" end";
RedisScript<Void> redisScript = new DefaultRedisScript<>(script);
redisTemplate.execute(redisScript, Arrays.asList(key,String.valueOf(rawTimeout)),values.toArray());
}
}业务使用
进入逻辑前先判断有没有锁
用try-catch包住业务逻辑,finally释放锁,以防抛错直接占有锁if (redisLock.lock(redisLockKey, value, 60 * 1000L)) {
try {
return reactiveMongoTemplate.find(query, JrRedPacketTask.class)
.collectList()
.flatMap(taskList -> {
if (taskList.size() > 0) {
//缓存到redis中
return reactiveRedisTemplate.opsForValue().set(redisKey, taskList, Duration.ofDays(1))
.then(Mono.just(taskList));
}
return Mono.just(taskList);
});
} finally {
redisLock.unLock(redisLockKey, value);// 释放锁
} } else {
try {
//休息,休息一会儿
Thread.sleep(8);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Redis分布式锁应用的更多相关文章
- 利用redis分布式锁的功能来实现定时器的分布式
文章来源于我的 iteye blog http://ak478288.iteye.com/blog/1898190 以前为部门内部开发过一个定时器程序,这个定时器很简单,就是配置quartz,来实现定 ...
- Redis分布式锁
Redis分布式锁 分布式锁是许多环境中非常有用的原语,其中不同的进程必须以相互排斥的方式与共享资源一起运行. 有许多图书馆和博客文章描述了如何使用Redis实现DLM(分布式锁管理器),但是每个库都 ...
- redis分布式锁和消息队列
最近博主在看redis的时候发现了两种redis使用方式,与之前redis作为缓存不同,利用的是redis可设置key的有效时间和redis的BRPOP命令. 分布式锁 由于目前一些编程语言,如PHP ...
- redis咋么实现分布式锁,redis分布式锁的实现方式,redis做分布式锁 积极正义的少年
前言 分布式锁一般有三种实现方式:1. 数据库乐观锁:2. 基于Redis的分布式锁:3. 基于ZooKeeper的分布式锁.本篇博客将介绍第二种方式,基于Redis实现分布式锁.虽然网上已经有各种介 ...
- spring boot redis分布式锁
随着现在分布式架构越来越盛行,在很多场景下需要使用到分布式锁.分布式锁的实现有很多种,比如基于数据库. zookeeper 等,本文主要介绍使用 Redis 做分布式锁的方式,并封装成spring b ...
- Redis分布式锁的正确实现方式
前言 分布式锁一般有三种实现方式:1. 数据库乐观锁:2. 基于Redis的分布式锁:3. 基于ZooKeeper的分布式锁.本篇博客将介绍第二种方式,基于Redis实现分布式锁.虽然网上已经有各种介 ...
- Redis分布式锁---完美实现
这几天在做项目缓存时候,因为是分布式的所以需要加锁,就用到了Redis锁,正好从网上发现两篇非常棒的文章,来和大家分享一下. 第一篇是简单完美的实现,第二篇是用到的Redisson. Redis分布式 ...
- redis分布式锁实践
分布式锁在多实例部署,分布式系统中经常会使用到,这是因为基于jvm的锁无法满足多实例中锁的需求,本篇将讲下redis如何通过Lua脚本实现分布式锁,不同于网上的redission,完全是手动实现的 我 ...
- Redis分布式锁的try-with-resources实现
Redis分布式锁的try-with-resources实现 一.简介 在当今这个时代,单体应用(standalone)已经很少了,java提供的synchronized已经不能满足需求,大家自然 而 ...
- 关于分布式锁原理的一些学习与思考-redis分布式锁,zookeeper分布式锁
首先分布式锁和我们平常讲到的锁原理基本一样,目的就是确保,在多个线程并发时,只有一个线程在同一刻操作这个业务或者说方法.变量. 在一个进程中,也就是一个jvm 或者说应用中,我们很容易去处理控制,在j ...
随机推荐
- docker bridge 到 k8s pod 跨节点网络通信机制演进
- Elastic Stack 8.0 再次创建enrollment token
enrollment token 在第一个 Elasticsearch 启动后的有效时间为30分钟.超过30分钟的时间上述 token 将会无效. enrollment token分两个,一个是kib ...
- 使用Metricbeat监控zookeeper遇到的问题
1.metricbeat中启动自动加载模块 metricbeat.config.modules: path: ${path.config}/modules.d/*.yml reload.enabled ...
- 如何在 Docker 之上使用 Elastic Stack 和 Kafka 可视化公共交通
文章转载自:https://blog.csdn.net/UbuntuTouch/article/details/106498568 需要掌握的知识点: 1.使用docker-compose方式部署一套 ...
- MongoDB集群搭建---副本和分片(伪集群)
参考地址:https://blog.csdn.net/weixin_43622131/article/details/105984032 已配置好的所有的配置文件下载地址:https://files. ...
- 基于electron+vue+element构建项目模板之【打包篇】
1.概述 开发平台OS:windows 开发平台IDE:vs code 本项目使用了一款Vue-CLI插件(vue-cli-plugin-electron-builder) 来构建 electron ...
- [题解] Atcoder ABC 213 H Stroll DP,分治FFT
题目 令\(dp_{i,j}\)表示从点1到达点i,路径长度为j的方案数.转移为\(dp_{i,j}=\sum_{(i,v,w)\in E}dp_{v,j-w}p_{i,v,w}\). 显然只能从长度 ...
- winscp报错Server sent passive reply with unroutable address. Using server address instead
找了一堆没用. 最后终于 1.使用winSCP连接ftp时,编辑会话,单击高级. 2.进入高级设置之后,单击连接,查看连接模式,把被动模式的勾,勾掉. 3.单击确定,然后保存配置,重新连接FTP,OK
- java中实现File文件的重命名(renameTo)、将文件移动到其他目录下、文件的复制(copy)、目录和文件的组合(更加灵活方便)
欢迎加入刚建立的社区:http://t.csdn.cn/Q52km 加入社区的好处: 1.专栏更加明确.便于学习 2.覆盖的知识点更多.便于发散学习 3.大家共同学习进步 3.不定时的发现金红包(不多 ...
- 鼠标悬浮上去显示小手CSS
鼠标悬浮上去显示小手CSS只需要添加一句css代码即可 cursor:pointer;