Spring Boot Redis 实现分布式锁,真香
之前看很多人手写分布式锁,其实 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 实现分布式锁,真香相关推荐
- Spring Boot Redis 实现分布式锁,真香!!
之前看很多人手写分布式锁,其实 Spring Boot 现在已经做的足够好了,开箱即用,支持主流的 Redis.Zookeeper 中间件,另外还支持 JDBC. 本篇栈长以 Redis 为例(这也是 ...
- Spring Boot + Redis 实现分布式锁,还有谁不会??
点击关注公众号:互联网架构师,后台回复 2T获取2TB学习资源! 上一篇:Alibaba开源内网高并发编程手册.pdf 一.业务背景 有些业务请求,属于耗时操作,需要加锁,防止后续的并发操作,同时对数 ...
- Spring Boot Redis 实现分布式锁,真香,你掌握了多少?
org.springframework.boot spring-boot-starter-integration org.springframework.integration spring-inte ...
- mybatis if test 判断参数_什么?你还在if判断参数?Spring Boot 注解进行参数校验真香...
一.依赖 org.springframework.bootspring-boot-starter-validation2.3.3.RELEASE 二.实体类 @TableField("use ...
- redis setnx 分布式锁_Spring Boot 整合 Redis 正确的实现分布式锁
前言 最近在做分块上传的业务,使用到了Redis来维护上传过程中的分块编号. 每上传完成一个分块就获取一下文件的分块集合,加入新上传的编号,手动接口测试下是没有问题的,前端通过并发上传调用就出现问题了 ...
- Spring Boot Redis 入门
本文,我们基于 Spring Boot 2.X 版本. 1. 概述 在快速入门 Spring Boot 整合 Redis 之前,我们先来做个简单的了解.在 Spring 的生态中,我们使用 Sprin ...
- 基于 Redis 实现分布式锁思考
以下文章来源方志朋的博客,回复"666"获面试宝典 来源:blog.csdn.net/xuan_lu/article/details/111600302 分布式锁 基于redis实 ...
- Redis实现分布式锁的深入探究
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 一.分布式锁简介 锁 是一种用来解决多个执行线程 访问共享资源 错 ...
- redis使用sysc超时_基于redis的分布式锁实现
随着业务越来越复杂,应用服务都会朝着分布式.集群方向部署,而分布式CAP原则告诉我们,Consistency(一致性). Availability(可用性).Partition tolerance(分 ...
最新文章
- 惊了,AI已经学会刷LeetCode了!
- 达摩院副院长金榕:中国 AI 将向何处?热潮有回落,但不应沮丧
- 【Robot学院】一文读懂世界智能制造大趋势!
- 网站不同优化时段的优化方法介绍
- solr获取同义词 java_java操作solr实现查询功能的实例
- 《机器学习实战》笔记(04):基于概率论的分类方法 - 朴素贝叶斯分类
- XSL样式,分页方法
- C#开源项目一览表[转](包含国内和国外)
- 语音识别错误太多?高科技巨头们偏偏“不信邪”
- ad中电容用什么封装_电容器是怎么工作的?它在电路中究竟起什么作用?
- 什么是像素格式(色彩采样、色度抽样)RGB 4:4:4、(Limit)RGB 4:4:4、Ycbcr 4:4:4、Ycbcr 4:2:2、Ycbcr 4:2:0又是什么?
- 深度优先和广度优先算法
- TensorFlow1.x入门(2)——变量的定义及其操作
- 计算机导论课程知识总结,计算机导论课程论文
- Android启动页设置
- 注册中心Consul
- 苹果系统服务器状态在哪里,详细解读iPhone上的系统定位服务
- 国家著作权: DNA 计算公式, 肽展定理公式与 变嘧啶 推导.
- 数据科学与大数据技术专业 —— 云计算●虚拟化 课程 期末复习卷及其简答(1)
- 3dmax顶点动画导入unity_3DMAX点缓存的动画怎么导入unity呀?