之前看很多人手写分布式锁,其实 Spring Boot 现在已经做的足够好了,开箱即用,支持主流的 Redis、Zookeeper 中间件,另外还支持 JDBC。

本篇栈长以 Redis 为例(这也是用得最多的方案),教大家如何利用 Spring Boot 集成 Redis 实现缓存,如何简单、快速实现 Redis 分布式锁。

分布式锁介绍

Spring Boot 实现 Redis 分布式锁在 spring-integration 这个项目中,参考:

https://docs.spring.io/spring-integration/docs/5.3.1.RELEASE/reference/html/redis.html#redis-lock-registry

首先来看下 LockRegistry 所注册接口的所有实现类结构图:

DefaultLockRegistry 就是纯单机的可重入锁,PassThruLockRegistry 是一个空实现类,也都没有什么利用价值。

Spring Integration 4.0 引入了基于 Redis 的分布式锁:RedisLockRegistry,并且从 5.0 开始实现了 ExpirableLockRegistry 接口,用来移除超时且没有用的锁。

分布式锁实战

添加依赖

上面提到 Spring Boot 实现 Redis 分布式锁在 spring-integration 这个项目中,所以需要这三个依赖:

  • spring-boot-starter-data-redis
  • spring-boot-starter-integration
  • spring-integration-redis
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency><groupId>org.springframework.integration</groupId><artifactId>spring-integration-redis</artifactId>
</dependency>

Spring Boot 基础知识就不介绍了。

配置分布式锁

@Bean(destroyMethod = "destroy")
public RedisLockRegistry redisLockRegistry(RedisConnectionFactory redisConnectionFactory) {return new RedisLockRegistry(redisConnectionFactory, "lock");
}

使用示例

@GetMapping("/redis/lock")
public String lock(@RequestParam("key") String key) {for (int i = 0; i < 10; i++) {new Thread(() -> {redisLockService.lock(key);try {Thread.sleep(3000L);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss"));redisLockService.unlock(key);}).start();}return "OK";
}

RedisLockService 是我封装了的一个 Redis 锁服务,代码有点多,这里就不贴了,完整的代码示例在 Github 上,大家可以 Star 一下:

https://github.com/javastacks/spring-boot-best-practice

测试:

http://localhost:8080/redis/lock?key=yeah

输出:

2020-06-23 11:15:34
2020-06-23 11:15:37
2020-06-23 11:15:40
2020-06-23 11:15:43
2020-06-23 11:15:46
2020-06-23 11:15:49
2020-06-23 11:15:52
2020-06-23 11:15:55
2020-06-23 11:15:58
2020-06-23 11:16:01

可以看到每个线程需要等上一个线程休眠 3 秒后才能获取到锁。

源码分析

集成完了,会使用了,还得研究下 RedisLockRegistry 的源码,不然遇到什么坑还得再踩一遍。

RedisLockRegistry 有两个类构造器:

  • connectionFactory:Redis 连接工厂
  • registryKey:锁前缀
  • expireAfter:失效时间(非必须项,默认60秒)

所以,我们要注册 expireAfter 这个选项,默认 60 秒是否满足业务需要,如果超过默认的 60 少时间,否则将导致锁失效。

还有两个和 RedisLockRegistry 相关且很重要的成员变量:

private final String clientId = UUID.randomUUID().toString();private final Map<String, RedisLock> locks = new ConcurrentHashMap<>();
  • clientId

首先用来标识当前 RedisLockRegistry 实例ID,并且在设置、移除锁的时候都会要用到,用来判断是不是当前的锁注册实例。

  • locks

用来在内存中缓存当前锁注册实例所有锁对象。

获取锁对象

如下面获取锁对象源码所示:

每个 key 在内存中(ConcurrentHashMap)都对应一个锁对象,锁对象有生成过就直接返回,没有就生成再返回,有了这个锁对象才能进行上锁和解锁操作。

这个锁对象(RedisLock)其实也是实现了 Java 中的 
java.util.concurrent.locks.Lock 锁接口:

锁对象(RedisLock)也有两个很重要的成员变量:

private final ReentrantLock localLock = new ReentrantLock();private volatile long lockedAt;
  • localLock

localLock 是一个本地内存可重入锁,每次去 Redis 上锁前,都要用本地 localLock 上锁先,这样能做到尽可能的少往 Redis 上锁,也能从一方面提升锁的性能。

  • lockedAt

lockedAt 上锁时间,移除过时锁会用到。

阻塞上锁

RedisLock#lock():

每隔 100 毫秒尝试获取一次锁,直到获取锁成功为止,不接受打断异常,遇到其他异常会释放本地锁返回异常并结束。

主要看下设置 Redis 锁的 Lua 脚本:

根据 key 查询其对应的值:clientId,如果和当前 clientId 一致则延长失效时间,如果 clientId 不存在就直接上锁,以上都不成立返回 false。

这样做的好处是,可以将整个 Redis Lua 脚本作为一个原子执行,而不用考虑并发及事务影响。

好了,核心的源码分析完了,其实也很简单,大家还不懂的或者有兴趣的可以再研究下。

Spring Boot Redis 实现分布式锁,真香相关推荐

  1. Spring Boot Redis 实现分布式锁,真香!!

    之前看很多人手写分布式锁,其实 Spring Boot 现在已经做的足够好了,开箱即用,支持主流的 Redis.Zookeeper 中间件,另外还支持 JDBC. 本篇栈长以 Redis 为例(这也是 ...

  2. Spring Boot + Redis 实现分布式锁,还有谁不会??

    点击关注公众号:互联网架构师,后台回复 2T获取2TB学习资源! 上一篇:Alibaba开源内网高并发编程手册.pdf 一.业务背景 有些业务请求,属于耗时操作,需要加锁,防止后续的并发操作,同时对数 ...

  3. Spring Boot Redis 实现分布式锁,真香,你掌握了多少?

    org.springframework.boot spring-boot-starter-integration org.springframework.integration spring-inte ...

  4. mybatis if test 判断参数_什么?你还在if判断参数?Spring Boot 注解进行参数校验真香...

    一.依赖 org.springframework.bootspring-boot-starter-validation2.3.3.RELEASE 二.实体类 @TableField("use ...

  5. redis setnx 分布式锁_Spring Boot 整合 Redis 正确的实现分布式锁

    前言 最近在做分块上传的业务,使用到了Redis来维护上传过程中的分块编号. 每上传完成一个分块就获取一下文件的分块集合,加入新上传的编号,手动接口测试下是没有问题的,前端通过并发上传调用就出现问题了 ...

  6. Spring Boot Redis 入门

    本文,我们基于 Spring Boot 2.X 版本. 1. 概述 在快速入门 Spring Boot 整合 Redis 之前,我们先来做个简单的了解.在 Spring 的生态中,我们使用 Sprin ...

  7. 基于 Redis 实现分布式锁思考

    以下文章来源方志朋的博客,回复"666"获面试宝典 来源:blog.csdn.net/xuan_lu/article/details/111600302 分布式锁 基于redis实 ...

  8. Redis实现分布式锁的深入探究

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 一.分布式锁简介 锁 是一种用来解决多个执行线程 访问共享资源 错 ...

  9. redis使用sysc超时_基于redis的分布式锁实现

    随着业务越来越复杂,应用服务都会朝着分布式.集群方向部署,而分布式CAP原则告诉我们,Consistency(一致性). Availability(可用性).Partition tolerance(分 ...

最新文章

  1. 惊了,AI已经学会刷LeetCode了!
  2. 达摩院副院长金榕:中国 AI 将向何处?热潮有回落,但不应沮丧
  3. 【Robot学院】一文读懂世界智能制造大趋势!
  4. 网站不同优化时段的优化方法介绍
  5. solr获取同义词 java_java操作solr实现查询功能的实例
  6. 《机器学习实战》笔记(04):基于概率论的分类方法 - 朴素贝叶斯分类
  7. XSL样式,分页方法
  8. C#开源项目一览表[转](包含国内和国外)
  9. 语音识别错误太多?高科技巨头们偏偏“不信邪”
  10. ad中电容用什么封装_电容器是怎么工作的?它在电路中究竟起什么作用?
  11. 什么是像素格式(色彩采样、色度抽样)RGB 4:4:4、(Limit)RGB 4:4:4、Ycbcr 4:4:4、Ycbcr 4:2:2、Ycbcr 4:2:0又是什么?
  12. 深度优先和广度优先算法
  13. TensorFlow1.x入门(2)——变量的定义及其操作
  14. 计算机导论课程知识总结,计算机导论课程论文
  15. Android启动页设置
  16. 注册中心Consul
  17. 苹果系统服务器状态在哪里,详细解读iPhone上的系统定位服务
  18. 国家著作权: DNA 计算公式, 肽展定理公式与 变嘧啶 推导.
  19. 数据科学与大数据技术专业 —— 云计算●虚拟化 课程 期末复习卷及其简答(1)
  20. 3dmax顶点动画导入unity_3DMAX点缓存的动画怎么导入unity呀?

热门文章

  1. 物联网体系的系统构架和用途
  2. 电脑调分辨率黑屏了怎么办_电脑调分辨率黑屏了怎么办
  3. ode45 求常微分非线性方程
  4. 正大国际期货:外盘黄金交易中如何用MACD指标捕捉波段
  5. 在 Kubernetes 上部署 Traefik Ingress
  6. 语音验证码接收平台接口调用文档
  7. 某程序员因准点下班没加班,被劝退!网友:还有没有天理?
  8. 天然“降脂药”,帮你“吃掉”血脂!
  9. Ethereum非同质化通证(NFT)的编写与部署
  10. vr企业视频展示如何制作