接上篇 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
相关推荐
SpringBoot整合Redisson实现RedLock分布式锁同步源码包
Java开发基于SpringBoot+WebSocket+Redis分布式即时通讯群聊系统。一个基于Spring Boot + WebSocket + Redis,可快速开发的分布式即时通讯群聊系统。适用于直播间聊天、游戏内聊天、客服聊天等临时性群聊场景。 ...
SpringBoot基于redis的分布式锁,有word使用文档,根据文档配置即可使用
之前看很多人手写分布式锁,其实 Spring Boot 现在已经做的足够好了,开箱...本篇栈长以 Redis 为例(这也是用得最多的方案),教大家如何利用 Spring Boot 集成 Redis 实现缓存,如何简单、快速实现 Redis 分布式锁。
http://localhost:8080/ass/getUser 测试是否登录 http://localhost:8080/any/user/passLogin 登录测试 http://localhost:8080/lock 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...
本篇内容主要讲解的是redis分布式锁,这个在各⼤⼚⾯试⼏乎都是必备的,下⾯结合模拟抢单的场景来使⽤她;本篇不涉及到的 redis环境搭建,快速搭建个⼈测试环境,这⾥建议使⽤docker;
redis分布式锁工具包,提供纯Java方式调用,支持传统Spring工程, 为spring boot应用提供了starter,更方便快捷的调用
SpringBoot实现的Redis分布式锁代码示例
基于Spring Boot + WebSocket + Redis,可快速开发的分布式即时通讯群聊系统.zip 1、该资源内项目代码经过严格调试,下载即用确保可以运行! 2、该资源适合计算机相关专业(如计科、人工智能、大数据、数学、电子信息...
redis分布式锁工具包,提供纯Java方式调用,支持传统Spring工程, 为spring boot应用提供了starter,更方便快捷的调用。 项目结构 原生redis分布式锁实现,支持注解,不推荐项目中使用,仅供学习使用 redis-...
springboot整合Redis实现在分布式情况,使用分布式锁解决数据并发的方案,主要使用Redis提供的setIfAbsent方法实现,并且考虑到了setIfAbsent在极端情况下的多实例同时设置一个key成功的情况。 本案例通过实现对库存...
基于Spring Boot+WebSocket+Redis开发的分布式即时通讯群聊系统源码+项目使用说明.zip 该项目是个人毕设项目源码,评分达到95分,都经过严格调试,确保可以运行!放心下载使用。 该项目资源主要针对计算机、自动化等...
一款基于 Spring Boot Starter 机制的分布式锁框架,实现了redis和zookeeper两种模式的分布式锁功能,以注解的方式(@RLock和@ZLock)对方法进行加锁操作,零代码实现业务加锁能力,涵盖各种加锁方式,并支持redis和...
但是服务的多副本运行随之也会引来一些分布式问题,比如某个接口的处理逻辑是这样的:接收到请求后,先查询 DB 看是否有相关的数据,如果没有则插入数据,如果有则更新数据。在这种场景下如果相同的 N 个请求并发发...
基于redis的分布式锁spring-boot starter组件,使得项目拥有分布式锁能力变得异常简单,支持spring boot,和spirng mvc等spring相关项目
redis客户端连接、spring boot整合、分布式锁
使用Redis实现分布式锁 5.1 设置分布式锁 5.2 释放分布式锁 Redis常用操作 6.1 字符串操作 6.2 哈希操作 6.3 列表操作 6.4 集合操作 6.5 有序集合操作 Redis持久化配置 7.1 RDB持久化 7.2 AOF持久化 7.3 持久化配置...