一文掌握Redisson分布式锁
简介
Redisson 说白了就是redis的儿子,本质上还是对redis进行加锁,不过是对redis进行了很多封装,他不仅仅提供了一系列的分布式的Java常用对象,还提供了许多的分布式服务。
与其他产品的比较
(redis审)
- Redisson 与 Jedis 、 Lettuce 有什么区别。
- Redisson和他俩的区别就像一个用鼠标操作图形化界面 , 一个用命令行操作文件夹,Redisson是更高层的抽象,Jedis和Lettuce是Redis命令的封装。
- Jedis是Redis官方推出的用于通过Java连接Redis客户端的一个工具包,提供了Redis的各种命令支持
- Lettuce是一种可扩展的线程安全的 Redis 客户端,通讯框架基于Netty,支持高级的 Redis 特性,比如哨兵,集群,管道,自动重新连接和Redis数据模型。Spring Boot 2.x 开始 Lettuce 已取代 Jedis 成为首选 Redis 的客户端。
- Redisson是架设在Redis基础上,通讯基于Netty的综合的、新型的中间件,企业级开发中使用Redis的最佳范本
- Jedis把Redis命令封装好,Lettuce则进一步有了更丰富的Api,也支持集群等模式。但是两者只给了你操作Redis数据库的脚手架,而Redisson则是基于Redis、Lua和Netty建立起了成熟的分布式解决方案,甚至redis官方都推荐的一种工具集
Redisson 操作使用
引入pom.xml依赖
在引入 Redisson 的依赖后 , 就可以直接调用了.
<!-- 原生 -->
<dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.13.4</version>
</dependency>
项目中,增加配置文件
package com.example.spring_boot_family_meals.Redisson;import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.codec.JsonJacksonCodec;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** @Date 2023/8/24 10:38*/
@Configuration
public class RedissonConfig {@Value("${spring.redis.host}")private String redisHost;@Value("${spring.redis.password}")private String password;private int port = 6379;@Beanpublic RedissonClient getRedisson() {System.out.println("初始化redisson :" + redisHost);Config config = new Config();config.useSingleServer().setAddress("redis://" + redisHost + ":" + port).setPassword(password);config.setCodec(new JsonJacksonCodec());return Redisson.create(config);}
}
启动分布式锁 (项目示例)
package com.example.spring_boot_family_meals.Redisson;import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;@RestController
@RequestMapping("/redisson")
public class RedissonController {@Resourceprivate RedissonClient redissonClient;@GetMapping("/test")public String test() {
// 1 : 创建线程 : 执行业务 1new Thread(() -> business_1()).start();
// 2 : 创建线程 : 执行业务 2new Thread(() -> business_2()).start();
// 3 : 创建线程 : 执行业务 3new Thread(() -> business_3()).start();return "ok";}/*** 业务一 : 对缓存进行加锁,锁过期10分钟 :业务睡眠10秒*/public void business_1() {
// 1 : 获取锁RLock rLock = redissonClient.getLock("business");System.out.println(getDate() + " : business_1 : 锁获取成功");try {
// 1.1 : 加锁,指定锁过期的时间 1分钟boolean isLocked = rLock.tryLock(10, TimeUnit.MINUTES);System.out.println(getDate() + " : business_1 : 加锁成功");if (isLocked) {
// 1.2 : 开始执行逻辑(我们这里睡眠)System.out.println(getDate() + " : business_1 : 开始执行业务逻辑");Thread.sleep(10000); //睡眠十秒System.out.println(getDate() + " : business_1 : 结束执行业务逻辑");}} catch (Exception e) {e.printStackTrace();} finally {System.out.println(getDate() + " : business_1 : 释放锁");rLock.unlock();}}/*** 业务二 : 对缓存进行加锁,锁过期 1 分钟 : 业务睡眠一百秒*/public void business_2() {
// 1 : 获取锁RLock rLock = redissonClient.getLock("business");System.out.println(getDate() + " : business_2 : 锁获取成功");try {
// 1.1 : 加锁,指定锁过期的时间 1分钟boolean isLocked = rLock.tryLock(1, TimeUnit.MINUTES);System.out.println(getDate() + " : business_2 : 加锁成功");if (isLocked) {
// 1.2 : 开始执行逻辑(我们这里睡眠)System.out.println(getDate() + " : business_2 : 开始执行业务逻辑");Thread.sleep(100000); // 睡眠100秒System.out.println(getDate() + " : business_2 : 结束执行业务逻辑");}} catch (Exception e) {e.printStackTrace();} finally {System.out.println(getDate() + " : business_2 : 释放锁");rLock.unlock();}}/*** 业务三 : 对缓存进行加锁,锁过期 1 分钟 : 业务睡眠 10 百秒*/public void business_3() {
// 1 : 获取锁RLock rLock = redissonClient.getLock("business");System.out.println(getDate() + " : business_3 : 锁获取成功");try {
// 1.1 : 加锁,指定锁过期的时间 1分钟boolean isLocked = rLock.tryLock(1, TimeUnit.MINUTES);System.out.println(getDate() + " : business_3 : 加锁成功");if (isLocked) {
// 1.2 : 开始执行逻辑(我们这里睡眠)System.out.println(getDate() + " : business_3 : 开始执行业务逻辑");Thread.sleep(10000); // 睡眠十秒System.out.println(getDate() + " : business_3 : 结束执行业务逻辑");}} catch (Exception e) {e.printStackTrace();} finally {System.out.println(getDate() + " : business_3 : 释放锁");rLock.unlock();}}/*** 获取到当前时分秒*/public static String getDate() {SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String format = dateFormat.format(new Date());return format;}}
- 执行结果 :
# Rediss项目启动,初始化成功
初始化redisson :127.0.0.1
2023-08-24 11:19:23.945 INFO 10100 --- [ main] org.redisson.Version : Redisson 3.15.5
2023-08-24 11:19:24.759 INFO 10100 --- [sson-netty-2-13] o.r.c.pool.MasterPubSubConnectionPool : 1 connections initialized for /127.0.0.1:6379
2023-08-24 11:19:25.201 INFO 10100 --- [sson-netty-2-19] o.r.c.pool.MasterConnectionPool : 24 connections initialized for /127.0.0.1:6379
2023-08-24 11:19:25.471 INFO 10100 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2023-08-24 11:19:25.479 INFO 10100 --- [ main] c.e.springtest.SpringTestApplication : Started SpringTestApplication in 2.611 seconds (JVM running for 3.276)
2023-08-24 11:19:33.530 INFO 10100 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2023-08-24 11:19:33.530 INFO 10100 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2023-08-24 11:19:33.534 INFO 10100 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 4 ms
# 3个线程同时争夺一把锁
2023-08-24 11:19:33 : business_2 : 锁获取成功
2023-08-24 11:19:33 : business_3 : 锁获取成功
2023-08-24 11:19:33 : business_1 : 锁获取成功
# 业务3 抢锁成功
2023-08-24 11:19:41 : business_3 : 加锁成功
2023-08-24 11:19:41 : business_3 : 开始执行业务逻辑
2023-08-24 11:19:51 : business_3 : 结束执行业务逻辑
2023-08-24 11:19:51 : business_3 : 释放锁
# 业务3 释放锁 ,业务1抢锁
2023-08-24 11:19:51 : business_1 : 加锁成功
2023-08-24 11:19:51 : business_1 : 开始执行业务逻辑
2023-08-24 11:20:01 : business_1 : 结束执行业务逻辑
2023-08-24 11:20:01 : business_1 : 释放锁
# 业务1 释放所 , 业务2抢锁
2023-08-24 11:20:02 : business_2 : 加锁成功
2023-08-24 11:20:02 : business_2 : 开始执行业务逻辑
2023-08-24 11:21:42 : business_2 : 结束执行业务逻辑
2023-08-24 11:21:42 : business_2 : 释放锁
下面是缓存中存放的内容
Redisson原理讲解
以上的代码简单明了 , 三个线程模拟三个服务来争抢一把锁, 我们只需要一个 Rlock ,下面我们来看看Redisson是怎么实现的
Redisson与使用JDK的ReentrantLock差不多 , 并且也支持并且也支持 ReadWriteLock(读写锁)、Reentrant Lock(可重入锁)、Fair Lock(公平锁)、RedLock(红锁)等各种锁,详细可以参照redisson官方文档来查看。
那么 Redisson到底有哪些优势呢? 锁的自动续期 (默认都是30秒) , 如果业务超长,运行期间会自动给锁续上新的30秒,不用担心业务执行较长而锁被自动删掉.
加锁的业务只要运行完成 , 就不会给当前续期 , 即便不手动解锁,锁默认在30秒后自动删除,不会照成死锁的原因.
前面也提到了锁的自动续期,那么我们来看看Redisson是怎么实现的
The end.