Redisson实现分布式锁(3)—项目落地实现

有关Redisson实现分布式锁前面写了两篇博客作为该项目落地的铺垫。

1、Redisson实现分布式锁(1)---原理

2、Redisson实现分布式锁(2)—RedissonLock

这篇讲下通过Redisson实现分布式锁的项目实现,我会把项目放到GitHub,该项目可以直接运用于实际开发中,作为分布式锁使用。

一、项目概述

1、技术架构

项目总体技术选型

SpringBoot2.1.5 + Maven3.5.4 + Redisson3.5.4 + lombok(插件)

2、加锁方式

该项目支持 自定义注解加锁 常规加锁 两种模式

自定义注解加锁

 @DistributedLock(value="goods", leaseTime=5)
public String lockDecreaseStock(){
//业务逻辑
}

常规加锁

 //1、加锁
redissonLock.lock("redisson", 10);
//2、业务逻辑
//3、解锁
redissonLock.unlock("redisson");

3、Redis部署方式

该项目支持四种Redis部署方式

1、单机模式部署
2、集群模式部署
3、主从模式部署
4、哨兵模式部署

该项目已经实现支持上面四种模式,你要采用哪种只需要修改配置文件application.properties,项目代码不需要做任何修改。

4、项目整体结构

redis-distributed-lock-core # 核心实现
|
---src
|
---com.jincou.redisson
|# 通过注解方式 实现分布式锁
---annotation
|# 配置类实例化RedissonLock
---config
|# 放置常量信息
---constant
|# 读取application.properties信息后,封装到实体
---entity
|# 支持单机、集群、主从、哨兵 代码实现
---strategy redis-distributed-lock-web-test # 针对上面实现类的测试类
|
---src
|
---java
|
---com.jincou.controller
|# 测试 基于注解方式实现分布式锁
---AnnotatinLockController.java
|# 测试 基于常规方式实现分布式锁
---LockController.java
---resources
| # 配置端口号 连接redis信息(如果确定部署类型,那么将连接信息放到core项目中)
---application.properties

二、测试

模拟1秒内100个线程请求接口,来测试结果是否正确。同时测试3中不同的锁:lock锁、trylock锁、注解锁。

1、lock锁

   /**
* 模拟这个是商品库存
*/
public static volatile Integer TOTAL = 10; @GetMapping("lock-decrease-stock")
public String lockDecreaseStock() throws InterruptedException {
redissonLock.lock("lock", 10);
if (TOTAL > 0) {
TOTAL--;
}
Thread.sleep(50);
log.info("======减完库存后,当前库存===" + TOTAL);
//如果该线程还持有该锁,那么释放该锁。如果该线程不持有该锁,说明该线程的锁已到过期时间,自动释放锁
if (redissonLock.isHeldByCurrentThread("lock")) {
redissonLock.unlock("lock");
}
return "=================================";
}

压测结果

没问题,不会超卖!

2、tryLock锁

   /**
* 模拟这个是商品库存
*/
public static volatile Integer TOTAL = 10; @GetMapping("trylock-decrease-stock")
public String trylockDecreaseStock() throws InterruptedException {
if (redissonLock.tryLock("trylock", 10, 5)) {
if (TOTAL > 0) {
TOTAL--;
}
Thread.sleep(50);
redissonLock.unlock("trylock");
log.info("====tryLock===减完库存后,当前库存===" + TOTAL);
} else {
log.info("[ExecutorRedisson]获取锁失败");
}
return "===================================";
}

测试结果

没有问题 ,不会超卖!

3、注解锁

/**
* 模拟这个是商品库存
*/
public static volatile Integer TOTAL = 10; @GetMapping("annotatin-lock-decrease-stock")
@DistributedLock(value="goods", leaseTime=5)
public String lockDecreaseStock() throws InterruptedException {
if (TOTAL > 0) {
TOTAL--;
}
log.info("===注解模式=== 减完库存后,当前库存===" + TOTAL);
return "=================================";
}

测试结果

没有问题 ,不会超卖!

通过实验可以看出,通过这三种模式都可以实现分布式锁,然后呢?哪个最优。

三、三种锁的锁选择

观点 最完美的就是lock锁,因为

1、tryLock锁是可能会跳过减库存的操作,因为当过了等待时间还没有获取锁,就会返回false,这显然很致命!

2、注解锁只能用于方法上,颗粒度太大,满足不了方法内加锁。

1、lock PK tryLock 性能的比较

模拟5秒内1000个线程分别去压测这两个接口,看报告结果!

1)lock锁

压测结果 1000个线程平均响应时间为31324。吞吐量 14.7/sec

2)tryLock锁

压测结果 1000个线程平均响应时间为28628。吞吐量 16.1/sec

这里只是单次测试,有很大的随机性。从当前环境单次测试来看,tryLock稍微高点。

2、常见异常 attempt to unlock lock, not ······

在使用RedissonLock锁时,很容易报这类异常,比如如下操作

       //设置锁1秒过去
redissonLock.lock("redisson", 1);
/**
* 业务逻辑需要咨询2秒
*/
redissonLock.release("redisson");

上面在并发情况下就会这样

造成异常原因:

线程1 进来获得锁后,但它的业务逻辑需要执行2秒,在 线程1 执行1秒后,这个锁就自动过期了,那么这个时候
线程2 进来了获得了锁。在线程1去解锁就会抛上面这个异常(因为解锁和当前锁已经不是同一线程了)

所以我们需要注意,设置锁的过期时间不能设置太小,一定要合理,宁愿设置大点。

正对上面的异常,可以通过isHeldByCurrentThread()方法,

  //如果为false就说明该线程的锁已经自动释放,无需解锁
if (redissonLock.isHeldByCurrentThread("lock")) {
redissonLock.unlock("lock");
}

好了,这篇博客就到这了!

至于完整的项目地址见GitHub。

如果对您能有帮助,就给个星星吧,哈哈!

GitHub地址 https://github.com/yudiandemingzi/spring-boot-distributed-redisson

参考

1、自己写分布式锁--基于redission

2、Redisson实现Redis分布式锁的N种姿势

3、利用Redisson实现分布式锁及其底层原理解析

只要自己变优秀了,其他的事情才会跟着好起来(中将7)

Redisson实现分布式锁(3)—项目落地实现的更多相关文章

  1. Redisson实现分布式锁(1)---原理

    Redisson实现分布式锁(1)---原理 有关Redisson作为实现分布式锁,总的分3大模块来讲. 1.Redisson实现分布式锁原理 2.Redisson实现分布式锁的源码解析 3.Redi ...

  2. Redisson 实现分布式锁的原理分析

    写在前面 在了解分布式锁具体实现方案之前,我们应该先思考一下使用分布式锁必须要考虑的一些问题.​ 互斥性:在任意时刻,只能有一个进程持有锁. 防死锁:即使有一个进程在持有锁的期间崩溃而未能主动释放锁, ...

  3. spring boot:用redis+redisson实现分布式锁(redisson3.11.1/spring boot 2.2)

    一,为什么要使用分布式锁? 如果在并发时锁定代码的执行,java中用synchronized锁保证了线程的原子性和可见性 但java锁只在单机上有效,如果是多台服务器上的并发访问,则需要使用分布式锁, ...

  4. 冷饭新炒:理解Redisson中分布式锁的实现

    前提 在很早很早之前,写过一篇文章介绍过Redis中的red lock的实现,但是在生产环境中,笔者所负责的项目使用的分布式锁组件一直是Redisson.Redisson是具备多种内存数据网格特性的基 ...

  5. Redisson 实现分布式锁原理分析

    Redisson 实现分布式锁原理分析   写在前面 在了解分布式锁具体实现方案之前,我们应该先思考一下使用分布式锁必须要考虑的一些问题.​ 互斥性:在任意时刻,只能有一个进程持有锁. 防死锁:即使有 ...

  6. Redisson实现分布式锁

    转: Redisson实现分布式锁 Redisson文档参考:https://github.com/redisson/redisson/wiki/%E7%9B%AE%E5%BD%95 redis是实现 ...

  7. 使用Redisson实现分布式锁,Spring AOP简化之

    源码 Redisson概述 Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid).它不仅提供了一系列的分布式的Java常用对象,还提供了许多 ...

  8. Redisson实现分布式锁(2)—RedissonLock

    Redisson实现分布式锁(2)-RedissonLock 有关Redisson实现分布式锁上一篇博客讲了分布式的锁原理:Redisson实现分布式锁---原理 这篇主要讲RedissonLock和 ...

  9. 利用Redisson实现分布式锁及其底层原理解析

    Redis介绍 参考地址:https://blog.csdn.net/turbo_zone/article/details/83422215 redis是一个key-value存储系统.和Memcac ...

随机推荐

  1. 如何使redis中存放的都是热点数据?

    当redis使用的内存超过设置的最大内存时,会触发redis的key淘汰机制,在redis3.0中的6中淘汰策略如下: (1)noeviction :不删除策略.当达到最大内存限制时,如果需要使用更多 ...

  2. 阿里面试官:字符串在JVM中如何存放?90%的人就真的只回答在哪里存放

    目录: 一道面试题的引出 案例分析 intern 源码分析 总结 1. 一道面试题的引出 在面试BAT这种一线大厂时,如果面试官问道:字符串在 JVM 中如何存放?大多数人能顺利的给出如下答案: 字符 ...

  3. TypeScript 装饰器的执行原理

    装饰器本质上提供了对被装饰对象 Property​ Descriptor 的操作,在运行时被调用. 因为对于同一对象来说,可同时运用多个装饰器,然后装饰器中又可对被装饰对象进行任意的修改甚至是替换掉实 ...

  4. fiddler抓包syntaxview窗口乱码

    只需再fiddler界面GO往右第二个Decode点击一下,让他出现蓝色边框即可 注意:抓取前的他不会进行自动解码,要再重新刷新页面才能获取界面后的内容

  5. Prism_Commanding(2)

    Commanding 除了提供对要在视图中显示或编辑的数据的访问之外,ViewModel还可能定义可由用户执行的一个或多个动作或操作.用户可以通过UI执行的动作或操作通常被定义为命令.命令提供了一种方 ...

  6. 一则sql优化实现接口耗时降低30倍的优化案例

    业务场景: 也测的业务,如上图,通过捕获业务的涉及的接口如下: 查询接口耗时大于7s,已经是非常的慢 经验提示: 一般接口响应时间慢的问题,最简单的方式就是监控接口相关的sql是否存在问题 开启mys ...

  7. ArcGIS api for JavaScript 3.27 在线浏览的一些小部件

    var navOption; var navToolbar;// 当前选择的操作 require( [ "esri/toolbars/navigation", "esri ...

  8. Servlet 使用介绍(2)

    说明 本篇由于介绍Servlet容器回传请求方法service(ServletRequest req, ServletResponse res);传入参数用户请求参数request和请求返回参数res ...

  9. Thymeleaf常用语法:使用星号表达式

    在处理模板时,一般情况都是使用变量表达式 ${...} 来显示变量,还可以使用选定对象表达式 *{...},它也称为星号表达式.如果在模板中先选定了对象,则需要使用星号表达式.Thymeleaf的内置 ...

  10. STL--标准模板库--简要概述

    STL--标准模板库 #include <vector>     //头文件 STL(Standared Template Library)即标准模板库,惠普实验室开发的一系列软件的统称. ...