接上篇 http://liaoke0123.iteye.com/blog/2375469
有的朋友可能会问,如果一个获得一个锁,执行一个耗时任务,耗时任务耗时大于锁默认的放锁时间,那么会怎么样。
redisson其实已经自动放锁了,避免饿死锁。
在超时上,我们业务也能不允许,所以我添加了fallback策略。
同样的分布式锁回调接口
package com.example.demo.redis2; import javax.persistence.Transient; /** * 分布式锁回调接口 * * @author lk */ public interface DistributedLockCallback<T> { /** * 调用者必须在此方法中实现需要加分布式锁的业务逻辑 * * @return */ public T process(); /** * 调用者业务 * 当超时的时候业务降级 * 与process一同使用 */ public T fallback(); /** * 得到分布式锁名称 * * @return */ public String getLockName(); }
可以看见我新建了fallback接口
package com.example.demo.redis2.service; import com.example.demo.redis2.DistributedLockCallback; import com.example.demo.redis2.DistributedLockTemplate; import com.example.demo.redis2.dao.TestEntityRepository; import com.example.demo.redis2.entity.TestEntity; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import javax.annotation.Resource; import javax.transaction.Transactional; /** * Created by LiaoKe on 2017/5/22. */ @Service public class AsyncService { @Resource TestEntityRepository ts; @Resource DistributedLockTemplate distributedLockTemplate; /** * 加锁 */ @Async @Transactional public void addAsync(){ distributedLockTemplate.lock(new DistributedLockCallback<Object>(){ @Override public Object process() { add(); return null; } @Override public Object fallback() { reduce(); return null; } @Override public String getLockName() { return "MyLock"; } }); } /** * 未加锁 */ @Async public void addNoAsync(){ add(); } /** * 测试异步方法 * 在不加分布式锁的情况下 * num数目会混乱 */ @Async public void add(){ try { Thread.sleep(6000); } catch (InterruptedException e) { e.printStackTrace(); } if(ts.findAll().size()==0){ TestEntity t = new TestEntity(); t.setNum(1); ts.saveAndFlush(t); }else{ TestEntity dbt = ts.findAll().get(0); dbt.setNum(dbt.getNum()+1); ts.saveAndFlush(dbt); } } /** * fallback */ @Async private void reduce(){ if(ts.findAll().size()==0){ TestEntity t = new TestEntity(); t.setNum(1); ts.saveAndFlush(t); }else{ TestEntity dbt = ts.findAll().get(0); dbt.setNum(dbt.getNum()-1); ts.saveAndFlush(dbt); } } }
fallback实现了对业务超时的进行的回退,数目减1,并且在方法上加上了@Transactional 事物注解,以防止在fallback发生异常,但是数目缺+1
由于我们使用的默认锁设置,超时是5秒,为了模拟超时,我在add()方法中让线程暂停了6秒。我们来看效果。
package com.example.demo.redis2; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import javax.persistence.Transient; import java.util.concurrent.TimeUnit; /** * Single Instance mode 分布式锁模板 * * @author lk */ public class SingleDistributedLockTemplate implements DistributedLockTemplate { private static final long DEFAULT_TIMEOUT = 5; private static final TimeUnit DEFAULT_TIME_UNIT = TimeUnit.SECONDS; private RedissonClient redisson; public SingleDistributedLockTemplate() { } public SingleDistributedLockTemplate(RedissonClient redisson) { this.redisson = redisson; } @Override public <T> T lock(DistributedLockCallback<T> callback) { return lock(callback, DEFAULT_TIMEOUT, DEFAULT_TIME_UNIT); } @Override public <T> T lock(DistributedLockCallback<T> callback, long leaseTime, TimeUnit timeUnit) { RLock lock = null; try { System.out.println("获取锁......."); lock = redisson.getLock(callback.getLockName()); lock.lock(leaseTime, timeUnit); T d = callback.process(); return d; } finally { if (lock != null) { if(!lock.isHeldByCurrentThread()){ System.out.println("超时自动放锁......."); callback.fallback(); }else{ System.out.println("释放锁......."); lock.unlock(); } } } } public void setRedisson(RedissonClient redisson) { this.redisson = redisson; } }
同样的url(见上篇文章)访问结果如下
可见数据库并未+1 ,fallback策略成功。
事物是必须加的,我们现在来模拟fallback失败
/** * fallback */ @Async private void reduce(){ int i = 5 /0 ; if(ts.findAll().size()==0){ TestEntity t = new TestEntity(); t.setNum(1); ts.saveAndFlush(t); }else{ TestEntity dbt = ts.findAll().get(0); dbt.setNum(dbt.getNum()-1); ts.saveAndFlush(dbt); } }
加上了一个runtimeException
并且去掉@Trabsactional
我们来看结果
但是数据库中数据却增加了,显示是不行的。
现在我们加上事务注解
可见在事务下,连第一次+1都没有提交,我们事务策略成功
下面附上demo
相关推荐
本篇文章将详细探讨如何使用Redisson实现Redis分布式事务锁,以及在Spring Boot环境中如何进行集成。 首先,Redis作为一个内存数据库,其高速读写性能使其成为实现分布式锁的理想选择。分布式锁的主要作用是在多...
Java开发基于SpringBoot+WebSocket+Redis分布式即时通讯群聊系统。一个基于Spring Boot + WebSocket + Redis,可快速开发的分布式即时通讯群聊系统。适用于直播间聊天、游戏内聊天、客服聊天等临时性群聊场景。 ...
3. **简单的API**:Redis提供了丰富的命令集,如`SETNX`、`EXPIRE`等,方便实现锁的逻辑。 要在SpringBoot中集成Redis,你需要先在项目中添加对应的依赖。SpringBoot提供了对Redis的自动配置支持,只需在`pom.xml`...
RedLock是Redisson中的一个特性,它由Redis社区提出,旨在提供一种更健壮的分布式锁实现,比单节点Redis的`setnx`命令更可靠。 RedLock的实现基于以下假设: 1. 时钟偏移:所有参与的Redis服务器的时钟大致同步。 2...
Spring Boot+Redis 分布式锁:模拟抢单
本次文档将围绕Spring Boot结合Redis实现分布式锁的机制进行详细解读,并通过模拟抢单的场景来展示其实际运用。 首先,了解Redis的分布式锁是如何运作的至关重要。在Redis的诸多操作中,`set`命令的`nx`选项(即`...
本篇文章将详细讲解如何利用 Spring Boot 结合 Redis 实现分布式锁。 首先,理解分布式锁的基本概念。分布式锁是在分布式系统中用于控制并发访问的机制,它允许只有一台机器或一个线程在任何时候持有锁,从而确保对...
Redis分布式锁利用Redis的SETNX命令实现分布式锁,确保在分布式环境下对资源的互斥访问。 Redisson分布式锁使用Redisson库提供的分布式锁实现,提供更高级的功能,如锁的自动续期和可重入锁。 2. 锁的续期和释放...
基于Spring Boot + WebSocket + Redis,可快速开发的分布式即时通讯群聊系统.zip 1、该资源内项目代码经过严格调试,下载即用确保可以运行! 2、该资源适合计算机相关专业(如计科、人工智能、大数据、数学、电子信息...
http://localhost:8080/ass/getUser 测试是否登录 http://localhost:8080/any/user/passLogin 登录测试 http://localhost:8080/lock redis分布式锁测试
7. **Spring Boot Starter**:对于Spring Boot应用,工具包提供的Starter可以通过简单的配置,自动将Redis分布式锁集成到应用中,减少手动配置的工作量。 在使用这个工具包时,开发者需要注意以下几点: - **正确...
项目为spring boot实现,maven生成jar包能直接运行 ...三种方式实现redis分布式锁 1.redis incr计数器实现 2.redis setIfAbsent 3.redisson 博客:https://blog.csdn.net/u011974797/article/details/81238079
Redisson实现分布式锁 有关Redisson实现分布式锁前面写了三篇博客作为该项目落地的铺垫。 1、 2、 3、 该项目可以直接运用于实际开发中,作为分布式锁使用。 一、项目概述 1、技术架构 项目总体技术选型 SpringBoot...
在SpringBoot应用中集成Redis分布式锁,可以创建一个`RedisLock`工具类,包含加锁和解锁的方法。加锁操作需要确保原子性,即同时设置键和过期时间,这在SpringBoot 2.x版本以上可以通过`opsForValue().setIfAbsent()...
读书笔记:架构实战篇十八Spring Boot Redis实现分布式锁
本篇将深入探讨如何将RedisTemplate封装成RedisUtils工具类,并实现分布式锁功能。 首先,我们需要引入Spring Data Redis的依赖库,这通常在项目的pom.xml或build.gradle文件中完成。添加对应版本的Redis连接器和...
通过上述步骤,你可以成功地在Spring Boot项目中集成Redis和Redission,实现高效、可靠的分布式缓存方案。记得在实际开发中根据业务需求选择合适的数据结构和功能,以达到最佳性能和可维护性。同时,定期检查和优化...
总结起来,这个项目展示了如何利用Spring Boot的便捷性、JPA的ORM能力以及Redis的高速缓存特性,构建一个高效且可扩展的分布式系统。通过学习和实践这个项目,开发者可以深入理解这三种技术的协同工作方式,提升自己...