springMVC 实现redis分布式锁
1.先配置spring-data-redis
首先是依赖
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.8.4.RELEASE</version>
</dependency>
redisconfig 配置类
@Configuration
@PropertySource("classpath:irongbei.properties")
public class RedisConfig extends JCacheConfigurerSupport {
@Autowired
private Environment environment; @Bean
public RedisConnectionFactory redisConnectionFactory() {
JedisConnectionFactory fac = new JedisConnectionFactory();
fac.setHostName(environment.getProperty("redis.host"));
fac.setPort(Integer.parseInt(environment.getProperty("redis.port")));
fac.setPassword(environment.getProperty("redis.password"));
fac.setTimeout(Integer.parseInt(environment.getProperty("redis.timeout")));
// fac.getPoolConfig().setMaxIdle(Integer.parseInt(environment.getProperty("redis.maxIdle")));
// fac.getPoolConfig().setMaxTotal(Integer.parseInt(environment.getProperty("redis.maxTotal")));
// fac.getPoolConfig().setMaxWaitMillis(Integer.parseInt(environment.getProperty("redis.maxWaitMillis")));
// fac.getPoolConfig().setMinEvictableIdleTimeMillis(
// Integer.parseInt(environment.getProperty("redis.minEvictableIdleTimeMillis")));
// fac.getPoolConfig()
// .setNumTestsPerEvictionRun(Integer.parseInt(environment.getProperty("redis.numTestsPerEvictionRun")));
// fac.getPoolConfig().setTimeBetweenEvictionRunsMillis(
// Integer.parseInt(environment.getProperty("redis.timeBetweenEvictionRunsMillis")));
// fac.getPoolConfig().setTestOnBorrow(Boolean.parseBoolean(environment.getProperty("redis.testOnBorrow")));
// fac.getPoolConfig().setTestWhileIdle(Boolean.parseBoolean(environment.getProperty("redis.testWhileIdle")));
return fac;
} @Bean
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, String> redis = new RedisTemplate<>();
// 设置redis的String/Value的默认序列化方式
DefaultStrSerializer defaultStrSerializer = new DefaultStrSerializer();
redis.setKeySerializer(defaultStrSerializer);
redis.setValueSerializer(defaultStrSerializer);
redis.setHashKeySerializer(defaultStrSerializer);
redis.setHashValueSerializer(defaultStrSerializer);
redis.setConnectionFactory(redisConnectionFactory);
redis.afterPropertiesSet();
return redis;
}
} class DefaultStrSerializer implements RedisSerializer<Object> {
private final Charset charset; public DefaultStrSerializer() {
this(Charset.forName("UTF8"));
} public DefaultStrSerializer(Charset charset) {
Assert.notNull(charset, "Charset must not be null!");
this.charset = charset;
} @Override
public byte[] serialize(Object o) throws SerializationException {
return o == null ? null : String.valueOf(o).getBytes(charset);
} @Override
public Object deserialize(byte[] bytes) throws SerializationException {
return bytes == null ? null : new String(bytes, charset); }
}
redisLock类 分布式锁实现的类
@Component
public class RedisLock { private static final Logger log = LoggerFactory.getLogger(RedisLock.class); @Autowired
private RedisTemplate<String, String> redisTemplate; /**
*
* @param key
* @param value 当前时间+超时时间
* @return
*/
public boolean lock(String key,String value){ if(redisTemplate.opsForValue().setIfAbsent(key,value)){
log.info(" [Redis分布式锁] key:[{}] 获取到锁",key);
return true;
}
//oldvalue 俩个线程都返回A
String currentValue = redisTemplate.opsForValue().get(key);
//如果锁过期->这里如果一个key的value时间是小于当前当前时间 那就是过期了,如果大于当前时间才没有过期
if(StringUtils.isNotEmpty(currentValue) && Long.parseLong(currentValue) <System.currentTimeMillis()){ //获取上一个锁的时间
//第一个线程获取上一个oldvalue 然后设置一个新的值进去 第二个线程就获取到是新的值.
String oldValue = redisTemplate.opsForValue().getAndSet(key,value);
//3 这一步就只有第一个线程能匹配到了 第二个线程就获取不到了
if(StringUtils.isNotEmpty(oldValue) && oldValue.equals(currentValue)){
log.info(" [Redis分布式锁] key:[{}] 获取到锁",key);
return true;
} } return false; } /**
* 解锁
* @param key
* @param value
*/
public void unlock(String key ,String value){
try {
String currentValue = redisTemplate.opsForValue().get(key);
if(StringUtils.isNotEmpty(currentValue) && currentValue.equals(value)){
redisTemplate.opsForValue().getOperations().delete(key);
}
} catch (Exception e) {
log.error(" [redis分布式锁] 解锁异常, {} ",e);
} }
}
测试类:
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(locations = {"/spring-context.xml","/spring-context-jedis.xml","/spring-context-shiro.xml"})
public class RedisLockTest {
@Resource(name = "threadPoolTaskExecutor")
private ThreadPoolExecutor taskExecutor; @Autowired
private RedisLock redisLock;
private long timeout = 5*1000; private static final Logger logger =LoggerFactory.getLogger(RedisLockTest.class); @Test
public void test (){
for (int i = 0; i <200 ; i++) {
taskExecutor.execute(()->{
String time = String.valueOf(System.currentTimeMillis()+timeout);
if(!redisLock.lock("testlock",time)){
logger.info("哎呦喂..人太多了 在排队中..");
return;
}else {
try {
Thread.sleep(4000);
redisLock.unlock("testlock",time);
} catch (InterruptedException e) {
e.printStackTrace();
}
} });
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} }
}
补充
和同事讨论,结果确实如同事所说,解锁的时候有些不严谨,因为是分俩步操作的.可能会在del之前被别人获取到锁然后再del删除掉别人获取的锁.下面是新的解锁方式,目前有些公司redis服务器不支持这样的命令
为什么使用lua语言在操作Redis 主要是保证操作的原子性.一步操作
但是使用lua要非常小心,如果脚本错误可能会阻塞整个Redis实例
private static final Long SUCCESS = 1L;
/**
* 释放锁
* @param key
* @param value
* @return
*/
public boolean releaseLock(String key, String value) {
key =prifix+key;
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; RedisScript<String> redisScript = new DefaultRedisScript<>(script, String.class); try {
Object result = redisTemplate.execute(redisScript, Collections.singletonList(key), value);
System.out.println(result);
if (SUCCESS.equals(result)) {
log.info("[redis 分布式锁解锁成功] key:[{}] 结果:{}",key,result);
return true;
}
log.info("[redis 分布式锁解锁失败] key:[{}] 结果{}",key,result);
} catch (Exception e) {
log.info("[redis 分布式锁解锁失败] key:[{}] msg:{}",key,e.getMessage());
e.printStackTrace();
} return false;
}
测试代码
@Test
public void testPrefix(){
redisLock.lock("test","10000");
redisLock.lock("test1","10000");
// redisLock.lock("test2","10000");
// redisLock.lock("test3","10000");
redisLock.releaseLock("test","10000");
redisLock.releaseLock("test1","10002");
}
springMVC 实现redis分布式锁的更多相关文章
- Lua脚本在redis分布式锁场景的运用
目录 锁和分布式锁 锁是什么? 为什么需要锁? Java中的锁 分布式锁 redis 如何实现加锁 锁超时 retry redis 如何释放锁 不该释放的锁 通过Lua脚本实现锁释放 用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,完全是手动实现的 我 ...
随机推荐
- [Inside HotSpot] hotspot的启动流程与main方法调用
hotspot的启动流程与main方法调用 虚拟机的使命就是执行public static void main(String[])方法,从虚拟机创建到main方法执行会经过一系列流程.这篇文章详细讨论 ...
- java中的int与byte的转化
java中的int与byte的转化 1.基础准备 1.1.原码 就是二进制码,最高位为符号位,0表示正数,1表示负数,剩余部分表示真值 1.2.反码 在原码的基础上,正数反码就是他本身,负数除符号位之 ...
- body标签中l的相关标签
字体标签: h1~h6.font. u.b,.strong. em. sup. sub 排版标签: div,.span.br.hr.center.pre 图片标签: img 超链接: a 列表标签: ...
- Identity Server 4 - Hybrid Flow - MVC客户端身份验证
预备知识 可能需要看一点点预备知识 OAuth 2.0 不完全简介: https://www.cnblogs.com/cgzl/p/9221488.html OpenID Connect 不完全简介: ...
- 网络协议 15 - P2P 协议:小种子大学问
[前五篇]系列文章传送门: 网络协议 10 - Socket 编程(上):实践是检验真理的唯一标准 网络协议 11 - Socket 编程(下):眼见为实耳听为虚 网络协议 12 - HTTP 协议: ...
- 中国.NET:各地微软技术俱乐部汇总(更新中...)
与微软技术的发展历程相似,微软俱乐部的发展同样经历着沉沉浮浮.2002年周庆麒先生创办的著名Office技术论坛Excel Home的上线,各种线上技术社区在中国的互联网世界中萌发.接着以鞠海洋(广州 ...
- 多机同步管理hexo博客
转载自:https://www.zhihu.com/question/21193762/answer/79109280 一.关于搭建的流程 创建仓库,<your github username& ...
- RDIFramework.NET V3.3 WinForm版角色授权管理新增角色对操作权限项、模块起止生效日期的设置
在实际应用在我们可能会有这样的需求,某个操作权限项(按钮)或菜单在某个时间范围内可以让指定角色访问.此时通过我们的角色权限扩展设置就可以办到. 在我们框架V3.3 WinForm版全新增加了角色权限扩 ...
- chrome主页被篡改为hao123 win10系统
应该是开了个从流氓网站下的蓝灯,然后发现主页被篡改 尝试chrome设置修改无效,应该是快捷方式被改了 系统 win10 1.打开对应的下面两个地址,找到chrome的快捷方式,右键属性 C:\Use ...
- Centos7上安装docker
Docker从1.13版本之后采用时间线的方式作为版本号,分为社区版CE和企业版EE. 社区版是免费提供给个人开发者和小型团体使用的,企业版会提供额外的收费服务,比如经过官方测试认证过的基础设施.容器 ...