Redisson 实现分布式锁

当我们的项目部署在多个服务器下时,有时会遇到一段代码只需要在一段时间内只需要执行一次的情况。此时就需要使用来实现,而java中的synchronized锁只是针对单个JVM实例的锁,在分布式的环境下无法无能为力。

实现分布式锁的方式有多种:

  • 基于数据库(唯一索引)
  • 基于缓存(Redis,memcached,tair)
  • 基于Zookeeper

这里主要学习使用Redis来实现分布式锁

1.1 导入依赖

官方文档:

GitHub - redisson/redisson: Redisson - Easy Redis Java client with features of In-Memory Data Grid….

redisson/redisson-spring-boot-starter at master · redisson/redisson · GitHub

1.1.1 使用自定义redisson客户端

导入redisson依赖

1
2
3
4
5
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.17.5</version>
</dependency>

RedissonConfig.class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package com.me.yiqi.config;

import lombok.Data;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
* Redisson配置类
*/
@Configuration
@ConfigurationProperties(prefix = "spring.redis")//引用application配置文件
@Data
public class RedissonConfig {
//来自配置文件
private String host;
private Integer port;
private String password;
private Integer database;

@Bean
public RedissonClient redissonClient(){
// 1. 创建配置
Config config = new Config();
String redisAddress = "redis://"+host+":"+port;

//config.useClusterServers() //userClusterServers()是集群模式
config.useSingleServer()
.setAddress(redisAddress)
.setPassword(password)
.setDatabase(database);

// 2. 创建实例
RedissonClient redisson = Redisson.create(config);
return redisson;
}
}

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@SpringBootTest
public class RedisTestClass {

@Resource
RedissonClient redissonClient;

@Test
public void testRedisson() {
RList<Object> list = redissonClient.getList("test-list");
list.add("123");
list.add("abcd");
list.add("甲乙丙丁");
System.out.println("rlist:"+list);
}
}

1.1.2 使用 redisson-spring-boot-starter

不太推荐这种方式,因为redisson迭代速度较快,需要注意导入的版本号

导入redisson-spring-boot-starter依赖

1
2
3
4
5
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.27.0</version>
</dependency>

但需要注意springboot的版本和这个依赖的版本对应关系

配置application文件

Using common Spring Boot 3.x+ settings:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
spring:
data:
redis:
database:
host:
port:
password:
ssl:
timeout:
connectTimeout:
clientName:
cluster:
nodes:
sentinel:
master:
nodes:

Using common Spring Boot up to 2.7.x settings:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
spring:
redis:
database:
host:
port:
password:
ssl:
timeout:
connectTimeout:
clientName:
cluster:
nodes:
sentinel:
master:
nodes:

导入后即可使用以下Bean:

  • RedissonClient
  • RedissonRxClient
  • RedissonReactiveClient
  • RedisTemplate
  • ReactiveRedisTemplate

1.2 使用

下面是一个缓存预热的定时任务案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package com.me.yiqi.job;

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
@Component
public class PreCacheJob {

@Resource
private RedissonClient redissonClient;

@Scheduled(cron = "0 1 22 * * *")
public void preCacheJob() {
System.out.println("预热缓存");

//获取锁
RLock lock = redissonClient.getLock("docache:usercommend:lock");


try {
//只有一个线程可以获取锁
//waitTime设置为0表示,不设置超时时间,获取不到就直接放弃获取锁
//leaseTime设置为-1表示
//redisson根据其看门狗机制自动释放锁,若进程未结束,会自动延长过期时间
if (lock.tryLock(0, -1, java.util.concurrent.TimeUnit.SECONDS)) {
//获取锁成功
System.out.println("获取锁成功");
//预热缓存
System.out.println("缓存写入中");
Thread.sleep(100000);
System.out.println("缓存写入完成");
}
} catch (Exception e) {
System.out.println(e.getMessage());
} finally {
//释放锁,且只能释放自己的锁
if(lock.isHeldByCurrentThread()){
lock.unlock();
}
}
}

}