Redisson

Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务。

Redisson整合

引入依赖

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/org.redisson/redisson -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.12.2</version>
</dependency>

配置redisson

redisson官方提供了多种方式来配置redisson

程序化配置方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Configuration
public class MyRedissonConfig {

/**
* 所有对Redisson的使用都是通过RedissonClient对象
* @return
* @throws IOException
*/
@Bean(destroyMethod="shutdown")
RedissonClient redisson() throws IOException {
// 1、创建配置
Config config = new Config();
config.useSingleServer().setAddress("redis://192.168.111.100:6379");

// 2、根据config创建出RedissonClient实例
RedissonClient redissonClient = Redisson.create(config);
return redissonClient;
}
}

一些小例子

自动续期锁

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
@ResponseBody
@GetMapping("/hello")
public String hello() {
// 1、获取一把锁,只要锁的名字一样,就是同一把锁
RLock lock = redisson.getLock("my-lock");

// 2、加锁
// 阻塞式等待。默认加的锁都是30s时间
// 1)、锁的自动续期,如果业务超长,运行期间自动给锁续上新的30s。不用担心业务时间长,锁自动过期被删掉
// 2)、加锁的业务只要运行完成,就不会给当前锁续期,即时不手动解锁,锁默认在30s以后自动删除
// lock.lock();

lock.lock(10, TimeUnit.SECONDS);// 10秒自动解锁,自动解锁时间一定要大于业务的执行时间。

try {
System.out.println("加锁成功,执行业务。。。。" + Thread.currentThread().getId());
Thread.sleep(30000);
} catch (Exception e) {

} finally {
// 3、解锁
System.out.println("释放锁。。。。" + Thread.currentThread().getId());
lock.unlock();
}
return "hello";
}
  • lock.lock(10, TimeUnit.SECONDS) 在锁时间到了以后。不会自动续期。

  • 如果我们传递了锁的超时时间,就发送给redis执行脚本,进行占锁,默认超时就是我们指定的时间

  • 如果我们未指定锁的超时时间,就使用30 * 1000;【lockWatchdogTimeout看门狗的默认时间】
  • 只要占锁成功,就会启动一个定时任务【重新给锁设置过期时间,新的过期时间就是看门狗的默认时间】,每隔10s都会自动再次续期,续成30s
  • internalLockLeaseTime【看门狗时间】 /3 ,10s

  • lock.lock(30, TimeUnit.SECONDS); 省掉了整个续期操作。手动解锁

读写锁

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
@GetMapping("/write")
@ResponseBody
public String writeValue(){
RReadWriteLock lock = redisson.getReadWriteLock("rw-lock");
String s ="";
RLock rLock = lock.writeLock();
try {
// 1、改数据加写锁,读数据加读锁
rLock.lock();
s = UUID.randomUUID().toString();
Thread.sleep(30000);
redisTemplate.opsForValue().set("writeValue",s);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
rLock.unlock();
}

return s;
}

@GetMapping("/read")
@ResponseBody
public String readValue(){
RReadWriteLock lock = redisson.getReadWriteLock("rw-lock");
String s ="";
// 加读锁
RLock rLock = lock.readLock();
rLock.lock();
try {
s = redisTemplate.opsForValue().get("writeValue");
} catch (Exception e) {
e.printStackTrace();
}finally {
rLock.unlock();
}

return s;
}
  • 读写锁,保证一定能读到最新数据,修改期间,写锁是一个排他锁(互斥锁)。读锁是一个共享锁
  • 写锁没释放 读就必须等待
  • 读锁 + 读锁:相当于无锁,并发读,只会在redis中记录好,所有当前的读锁。他们都会同时加锁成功
  • 写锁 + 读锁:等待写锁释放
  • 写锁 + 写锁:阻塞方式
  • 读锁 + 写锁:有读锁。写也需要等待
  • 总结:只要有写锁的存在,都必须等待

信号锁

基于Redis的Redisson的分布式信号量(Semaphore)Java对象RSemaphore采用了与java.util.concurrent.Semaphore相似的接口和用法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 车库停车,
* 3车位
* @return
* @throws InterruptedException
*/
@GetMapping("/park")
@ResponseBody
public String park() throws InterruptedException {
RSemaphore park = redisson.getSemaphore("park");
// 获取一个信号,获取一个值,占一个车位
park.acquire();

return "ok";
}

@GetMapping("/go")
@ResponseBody
public String go() throws InterruptedException {
RSemaphore park = redisson.getSemaphore("park");
park.release();

return "ok";
}

缓存数据一致性

双写模式

失效模式

Canal

解决方案