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

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

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

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

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

一、项目概述

1、技术架构

项目总体技术选型

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

2、加锁方式

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

自定义注解加锁

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

常规加锁

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

3、Redis部署方式

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

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

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

4、项目整体结构

  1. redis-distributed-lock-core # 核心实现
  2. |
  3. ---src
  4. |
  5. ---com.jincou.redisson
  6. |# 通过注解方式 实现分布式锁
  7. ---annotation
  8. |# 配置类实例化RedissonLock
  9. ---config
  10. |# 放置常量信息
  11. ---constant
  12. |# 读取application.properties信息后,封装到实体
  13. ---entity
  14. |# 支持单机、集群、主从、哨兵 代码实现
  15. ---strategy
  16. redis-distributed-lock-web-test # 针对上面实现类的测试类
  17. |
  18. ---src
  19. |
  20. ---java
  21. |
  22. ---com.jincou.controller
  23. |# 测试 基于注解方式实现分布式锁
  24. ---AnnotatinLockController.java
  25. |# 测试 基于常规方式实现分布式锁
  26. ---LockController.java
  27. ---resources
  28. | # 配置端口号 连接redis信息(如果确定部署类型,那么将连接信息放到core项目中)
  29. ---application.properties

二、测试

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

1、lock锁

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

压测结果

没问题,不会超卖!

2、tryLock锁

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

测试结果

没有问题 ,不会超卖!

3、注解锁

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

测试结果

没有问题 ,不会超卖!

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

三、三种锁的锁选择

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

  1. 1tryLock锁是可能会跳过减库存的操作,因为当过了等待时间还没有获取锁,就会返回false,这显然很致命!
  2. 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. //设置锁1秒过去
  2. redissonLock.lock("redisson", 1);
  3. /**
  4. * 业务逻辑需要咨询2秒
  5. */
  6. redissonLock.release("redisson");

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

造成异常原因:

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

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

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

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

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

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

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

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

参考

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

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

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

  1. 只要自己变优秀了,其他的事情才会跟着好起来(中将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. Win10 中 Git clone github上内容保存到指定文件夹

    在要存储的右键→Git Bash Here 弹出命令窗口 输入 git clone 链接 很快就下载完成

  2. 【Web开发】到底什么是短链接

    目录 什么是短链接 为什么使用短链接 节省发送的内容 提升用户体验 便于链接追踪,分析点击来源 一定程度上保护原始网站链接 短链接生成平台 短链接生成原理 参考 今天无意中看到一个名词--" ...

  3. oracle相邻表记录交换(单双两两交换)

    在博客园的博问中看到了一个这样的提问:一个表中有id和name两个字段,id是连续非空不重复的,想要交换相邻表记录的name值(单双两两交换). 另外,如果最后的单独行没有对应的下一行匹配记录的话,就 ...

  4. Cypress 之 环境配置

    将项目添加到cypress时,cypress.json会在项目中创建一个文件,此文件用于存储projectId,以及你提供的任何配置项. { "projectId": " ...

  5. maven工程指定web资源包,创建jsp文件

    进入项目的project structure. 选择web. 在右侧增加web资源包路径

  6. 前端之HTML介绍及使用

    一.HTML介绍 1.1 web本质 在pycharm写入一下代码,然后在浏览器地址栏输入地址和端口127.0.0.1:8080,回车,回来运行代码,直接访问客户端发送的内容conn.send(b'& ...

  7. .net core 2.1 Swagger 配置

    1.先创建 .net core Web 应用程序,选择API 2.安装 Nuget 包:Swashbuckle.AspNetCore Install-Package Swashbuckle.AspNe ...

  8. 我的计划任务 --- 实现市电停电安全关闭群辉,Windows, Linux等设备

    有一次突然停电,我的群辉DS218+ 的一块硬盘出现故障了,让我担心我的数据安全,其实我是有UPS, 不是在线式的,然后就想如何实现停电自动关机呢? 经过半天的了解,其实群辉支持telnet协议,于是 ...

  9. 墙上你APP设计与实现

    墙上你APP,听名字你们就应该可以想到是一个类似于表白墙吧,其实在做这个项目的时候我就是看到我们学校有一个专门工程墙,但是你找它发布消息表白或者找寻自己在学校掉了的饭卡或者身份证的时候,需要等它们同意 ...

  10. HTML中特殊符号编码对照表,html特殊符号编码都有哪些?

    HTML中一些无法打出来的符号可以用相应的代码进行代替显示,本文提供了一些HTML特殊符号相应的代码供开发者参考. 特殊符号 命名实体 十进制编码 特殊符号 命名实体 十进制编码 特殊符号 命名实体 ...