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 ...
随机推荐
- 22.3 Extends 构造方法的执行顺序
/** 1.有子父类继承关系的类中,创建父类对象未调用,执行父类无参构造* 2.有子父类继承关系的类中,创建子类对象未调用,执行顺序:默认先调用 父类无参构造---子类无参构造* 在子类的构造方法的第 ...
- Feature list, Standard and Test plan for BETA Release 12/22/2015
===================BETA RELEASE FEATRURE LIST==================== 1. Log in and account manager for ...
- D. Minimax Problem Codeforces 1288D binary_search+二进制
题目大意:n*m的矩阵中,找到两行数,可以形成两个一维数组,数组1的位置i和数组2的位置i去最大构成新数组b的元素b[i],最终目的要使数组b中最小的数尽可能的大 题解: m的范围是(1,8),比较小 ...
- C语言二维数组超细讲解
用一维数组处理二维表格,实际是可行的,但是会很复杂,特别是遇到二维表格的输入.处理和输出. 在你绞尽脑汁的时候,二维数组(一维数组的大哥)像电视剧里救美的英雄一样显现在你的面前,初识数组的朋友们还等什 ...
- 面试官:兄弟,说说Java到底是值传递还是引用传递
二哥,好久没更新面试官系列的文章了啊,真的是把我等着急了,所以特意过来催催.我最近一段时间在找工作,能从二哥的文章中学到一点就多一点信心啊! 说句实在话,离读者 trust you 发给我这段信息已经 ...
- 4.JS跳转路由/刷新/返回页面
1.JS跳转路由(需要拿到父组件的history) clickHandle(){ let history = this.props.history; history.push( '/home') } ...
- 二维vector的使用
和数组一样,数组有二维的数组,vector也有二维的vector.下面就介绍一下二维vector的使用方法. 一般声明初始化二维vector有三种方法 (1) vector< vector< ...
- ip的运用
1------获取ip$ip=$_SERVICE['REMOTE_ADDR'];2------根据ip获取主机名gethostbyaddr($ip);3------根据主机名获取IPgethostby ...
- ARP/RARP报文格式
arp协议 地址解析协议ARP(Address Resolution Protocol)是用来将IP地址解析为MAC地址的协议. arp格式: 一个字节等于8位 硬件类型:指明发送方想知道的硬件接口类 ...
- [Inno Setup] 卸载 重启之后 删除文件
某些系统文件,例如驱动,不重启无法删除. 利用windows注册表里的 RunOnce.注意必须在HKLM下,否则可能权限不够. 不能直接填cmd命令,要以cmd的参数形式填写. procedure ...