springboot利用redis实现分布式锁(redis为单机模式)
1.pom文件添加redis支持
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.application.properties或者(application.yml)添加redis配置
spring.redis.database=1
spring.redis.host=172.xx.xx.xx
spring.redis.password=123456
spring.redis.port=6379
上面的spring.redis.host替换成自己的redis服务地址,如果没有用到密码则删除spring.redis.password配置即可
3.redis工具类
package com.example.demo; import com.example.demo.extend.FastJsonRedisSerializer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component; import javax.annotation.PostConstruct;
import java.util.Collections;
import java.util.List; @Component
public class RedisUtil { @Autowired
private RedisTemplate<String,String> template; @PostConstruct
public void init(){
template.setKeySerializer(template.getStringSerializer());
template.setValueSerializer(template.getStringSerializer());
template.setHashKeySerializer(template.getStringSerializer());
template.setHashValueSerializer(template.getStringSerializer());
} /**
* 通过lua脚本 加锁并设置过期时间
* @param key 锁key值
* @param value 锁value值
* @param expire 过期时间,单位秒
* @return true:加锁成功,false:加锁失败
*/
public boolean getLock(String key,String value,String expire){
DefaultRedisScript<String> redisScript = new DefaultRedisScript<String>();
redisScript.setResultType(String.class);
String strScript = "";
strScript +=" if redis.call('setNx',KEYS[1],ARGV[1])==1 then ";
strScript +=" return redis.call('expire',KEYS[1],ARGV[2]) ";
strScript +=" else";
strScript +=" return 0 ";
strScript +=" end ";
redisScript.setScriptText(strScript);
try{
Object result = this.template.execute(redisScript,template.getStringSerializer(),template.getStringSerializer(), Collections.singletonList(key),value,expire);
System.out.println("redis返回:"+result);
return "1".equals(""+result);
}catch (Exception e){
//可以自己做异常处理
return false;
} } /**
* 通过lua脚本释放锁
* @param key 锁key值
* @param value 锁value值(仅当redis里面的value值和传入的相同时才释放,避免释放其他线程的锁)
* @return true:释放锁成功,false:释放锁失败(可能已过期或者已被释放)
*/
public boolean releaseLock(String key,String value){
DefaultRedisScript<String> redisScript = new DefaultRedisScript<>();
redisScript.setResultType(String.class);
String strScript = "";
strScript +="if redis.call('get',KEYS[1]) == ARGV[1] then ";
strScript +=" return redis.call('del',KEYS[1]) ";
strScript +="else ";
strScript +=" return 0 ";
strScript +="end ";
redisScript.setScriptText(strScript);
try{
Object result = this.template.execute(redisScript,template.getStringSerializer(),template.getStringSerializer(), Collections.singletonList(key),value);
return "1".equals(""+result);
}catch (Exception e){
//可以自己做异常处理
return false;
}
}
}
redis锁用到的是setNx命令,这个命令的意思是如果redis里面存在这个key则不再添加,如果key不存在则添加成功,当setNx设置成功之后再给这个值设置一个超期时间来防止出现极端情况(断网,服务终止)导致锁没有被及时释放的情况。
上面的加锁和解锁都是通过lua脚本进行,redis里面lua脚本执行时是原子操作,可以保证加锁和设置超时同时成功或者失败,不会出现设置值成功 添加超时时间失败的情况
4.使用
在需要加锁的地方注入RedisUtil对象即可。有问题的可以留言一起探讨细节问题
@Autowired
private RedisLockUtil redisUtil;
boolean lock = this.redisUtil.getLock("FORM_SUBMIT"+formId,formId,"2");
if(!lock){
//未获得锁
throw new ServiceException("当前已经有任务在执行!");
} //---------执行业务逻辑 if(lock){
//释放锁不关心成功与否
this.redisUtil.releaseLock("FORM_SUBMIT"+formId,formId);
}
上面的业务逻辑最好放在try catch中执行,释放锁的代码放到finally里面执行。
5.考虑各种情况下会不会导致bug的出现
5.1:加锁失败
拿不到锁业务逻辑不执行,没问题
5.2:加锁成功,释放锁失败
网络原因或者其他原因没有释放掉,没关系 ,超时时间过了就会自己释放,没问题
5.3:加锁成功,业务执行时间过长,锁已经被redis自己释放
此种情况需要根据业务的实际情况设置合理的超时时间,可能会出问题,原因在于 基于分布式的系统是无法避免类似的问题,具体可以参考如下博客的文章,
此文章引入了 关于Redis分布式锁的安全性问题,在分布式系统专家Martin Kleppmann和Redis的作者antirez之间发生过的一场争论,内容很精彩。https://blog.csdn.net/paincupid/article/details/75094550
springboot利用redis实现分布式锁(redis为单机模式)的更多相关文章
- Redis除了做缓存--Redis做消息队列/Redis做分布式锁/Redis做接口限流
1.用Redis实现消息队列 用命令lpush入队,rpop出队 Long size = jedis.lpush("QueueName", message);//返回存放的数据条数 ...
- Redis 中的原子操作(3)-使用Redis实现分布式锁
Redis 中的分布式锁如何使用 分布式锁的使用场景 使用 Redis 来实现分布式锁 使用 set key value px milliseconds nx 实现 SETNX+Lua 实现 使用 R ...
- Java基于redis实现分布式锁(SpringBoot)
前言 分布式锁,其实原理是就是多台机器,去争抢一个资源,谁争抢成功,那么谁就持有了这把锁,然后去执行后续的业务逻辑,执行完毕后,把锁释放掉. 可以通过多种途径实现分布式锁,例如利用数据库(mysql等 ...
- 利用多写Redis实现分布式锁原理与实现分析(转)
利用多写Redis实现分布式锁原理与实现分析 一.关于分布式锁 关于分布式锁,可能绝大部分人都会或多或少涉及到. 我举二个例子:场景一:从前端界面发起一笔支付请求,如果前端没有做防重处理,那么可能 ...
- 利用redis实现分布式锁知识点总结及相关改进
利用redis实现分布式锁知识点总结及相关改进 先上原文,本文只为总结及对相关内容的质疑并提出若干意见,原文内容更详细https://www.cnblogs.com/linjiqin/p/800383 ...
- 利用redis实现分布式锁
分布式锁一般有三种实现方式: 1. 数据库乐观锁: 2. 基于ZooKeeper的分布式锁: 3. 基于Redis的分布式锁: 这里大概说一下三种方式的优缺点,数据库乐观锁优点是实现简单,只需要for ...
- spring boot 利用redisson实现redis的分布式锁
原文:http://liaoke0123.iteye.com/blog/2375469 利用redis实现分布式锁,网上搜索的大部分是使用java jedis实现的. redis官方推荐的分布式锁实现 ...
- SpringBoot电商项目实战 — Redis实现分布式锁
最近有小伙伴发消息说,在Springboot系列文第二篇,zookeeper是不是漏掉了?关于这个问题,其实我在写第二篇的时候已经考虑过,但基于本次系列文章是实战练习,在项目里你能看到Zookeepe ...
- 通过Redis 实现分布式锁_利用Jedis 客户端
前言 分布式锁一般有三种实现方式: 数据库乐观锁:2. 基于Redis的分布式锁:3. 基于ZooKeeper的分布式锁. 本篇博客将介绍第二种方式,基于Redis实现分布式锁. 虽然网上已经有各种介 ...
- SpringBoot集成Redis 一 分布式锁 与 缓存
1.添加依赖及配置(application.yml) <!-- 引入redis依赖 --> <dependency> <groupId>org.springfram ...
随机推荐
- React Native简史
诞生 React Native 诞生于 2013 年的 Facebook 内部黑客马拉松(hackathon): In the essence of Facebook’s hacker culture ...
- Linux环境安装Docker
1. 使用APT安装 # 更新数据源 apt-get update # 安装所需依赖 apt-get -y install apt-transport-https ca-certificates cu ...
- matlab创建HDF5文件
一.例子 1.创建写入 testdata = uint8(magic(5)); h5create('my_example.h5','/dataset1',size(testdata)); %创建 h5 ...
- 好玩的GeoGebra
目前,在网站上看到好多大牛写的信号方面的笔记,有很多好玩的gif好玩又让人能明白其中的原理,工欲善其事必先利其器,在写我的博客方面先来学一个好玩的数学软件吧. GeoGebra官网如图 它是一个小巧的 ...
- CTR学习笔记&代码实现3-深度ctr模型 FNN->PNN->DeepFM
这一节我们总结FM三兄弟FNN/PNN/DeepFM,由远及近,从最初把FM得到的隐向量和权重作为神经网络输入的FNN,到把向量内/外积从预训练直接迁移到神经网络中的PNN,再到参考wide& ...
- 装机摸鱼日志--ubuntu16.04安装网易云音乐客户端
之前装的网易云音乐不指定啥原因不能用了,所以打算重新装一个,但是进官网只有deepin15和ubuntu18.04版本的安装包.然后我装了一下18.04的安装包,但是没有成功.甚至因为更换glibc差 ...
- vue axios post请求下载文件,后台springmvc完整代码
注意请求时要设置responseType,不加会中文乱码,被这个坑困扰了大半天... axios post请求: download(index,row){ var ts = ...
- 一口气带你踩完五个 List 的大坑,真的是处处坑啊!
List 可谓是我们经常使用的集合类之一,几乎所有业务代码都离不开 List.既然天天在用,那就没准就会踩中这几个 List 常见坑. 今天我们就来总结这些常见的坑在哪里,捞自己一手,防止后续同学再继 ...
- 4. js
1.) ~ 操作符 console.log(~-2) // 1 console.log(~-1) // 0 console.log(~0) // -1 console.log(~1) // ...
- 新的知识点来了-ES6 Proxy代理 和 去银行存款有什么关系?
ES给开发者提供了一个新特性:Proxy,就是代理的意思.也就是我们这一节要介绍的知识点. 以前,ATM还没有那么流行的时候(暴露年纪),我们去银行存款或者取款的时候,需要在柜台前排队,等柜台工作人员 ...