基于官方推荐的Redisson实现Redis分布式锁

  |     |  

RedLock 简介

在不同进程需要互斥地访问共享资源时,分布式锁是一种非常有用的技术手段。实现高效的分布式锁有三个属性需要考虑:

1
2
3
4
安全属性:互斥,不管什么时候,只有一个客户端持有锁
效率属性A:不会死锁
效率属性B:容错,只要大多数redis节点能够正常工作,客户端端都能获取和释放锁。
解铃还须系铃人。加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。

Redlock是redis官方提出的实现分布式锁管理器的算法。这个算法会比一般的普通方法更加安全可靠。

Redisson提供了很多种类型的分布式锁和分布式同步器,如下:

1
2
3
4
5
6
7
8
- 8.1. 可重入锁(Reentrant Lock) --例子以实现
- 8.2. 公平锁(Fair Lock) --例子以实现
- 8.3. 联锁(MultiLock)
- 8.4. 红锁(RedLock)
- 8.5. 读写锁(ReadWriteLock)
- 8.6. 信号量(Semaphore)
- 8.7. 可过期性信号量(PermitExpirableSemaphore)
- 8.8. 闭锁(CountDownLatch)

详情可查看下面的参考链接

分布式锁实现

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
47
@Component
public class RedisLocker implements DistributedLocker {

private final static String LOCKER_PREFIX = "lock:";

/**
* The Redisson client.
*/
@Autowired
RedissonClient redissonClient;

@Override
public <T> T lock(String resourceName, AcquiredLockWorker<T> worker) throws Exception {
return fairLock(resourceName, worker, 100);
}

@Override
public <T> T tryLock(String resourceName, AcquiredLockWorker<T> worker, int lockTime) throws Exception {
RLock lock = redissonClient.getLock(LOCKER_PREFIX + resourceName);
// (可重入锁)最多等待100秒,锁定后经过lockTime秒后自动解锁
boolean success = lock.tryLock(100, lockTime, TimeUnit.SECONDS);
if (success) {
try {
return worker.invokeAfterLockAcquire();
} finally {
lock.unlock();
}
}
throw new UnableToAcquireLockException();
}

@Override
public <T> T fairLock(String resourceName, AcquiredLockWorker<T> worker, int lockTime) throws Exception {
RLock lock = redissonClient.getFairLock(LOCKER_PREFIX + resourceName);
// (公平锁)最多等待100秒,锁定后经过lockTime秒后自动解锁
boolean success = lock.tryLock(100, lockTime, TimeUnit.SECONDS);
if (success) {
try {
return worker.invokeAfterLockAcquire();
} finally {
lock.unlock();
}
}
throw new UnableToAcquireLockException();
}

}

配置客户端

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

/**
* Gets client.
*
* @return the client
*/
@Bean
public RedissonClient redissonClient() {
//如果是默认本地6379,则可不必配置,否则参照
// https://github.com/redisson/redisson/wiki/2.-%E9%85%8D%E7%BD%AE%E6%96%B9%E6%B3%95 配置
return Redisson.create();
}
}

获取锁后的业务处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 获取锁后需要做的逻辑
*
* @param <T> the type parameter
* @author Liaozihong
*/
public interface AcquiredLockWorker<T> {
/**
* Invoke after lock aquire t.
*
* @return the t
* @throws Exception the exception
*/
T invokeAfterLockAcquire() throws Exception;
}

测试类

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
@RestController
@Slf4j
public class RedissonLockTestApi {
/**
* The Distributed locker.
*/
@Autowired
RedisLocker distributedLocker;

/**
* Test redlock string.
* 并发下分布式锁测试API
*
* @return the string
* @throws Exception the exception
*/
@RequestMapping(value = "/redlock")
public String testRedlock() throws Exception {
CountDownLatch startSignal = new CountDownLatch(1);
CountDownLatch doneSignal = new CountDownLatch(5);
// 测试5个并发
for (int i = 0; i < 5; ++i) {
new Thread(new Worker(startSignal, doneSignal)).start();
}
startSignal.countDown(); // let all threads proceed
doneSignal.await();
System.out.println("All processors done. Shutdown connection");
return "redlock";
}

/**
* Worker
* <p/>
* Created in 2018.12.05
* <p/>
*
* @author Liaozihong
*/
class Worker implements Runnable {
private final CountDownLatch startSignal;
private final CountDownLatch doneSignal;

/**
* Instantiates a new Worker.
*
* @param startSignal the start signal
* @param doneSignal the done signal
*/
Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
this.startSignal = startSignal;
this.doneSignal = doneSignal;
}

@Override
public void run() {
try {
startSignal.await();
//尝试加锁
distributedLocker.lock("test", new AcquiredLockWorker<Object>() {

@Override
public Object invokeAfterLockAcquire() {
doTask();
return "success";
}

});
} catch (Exception e) {
log.warn("获取锁出现异常", e);
}
}

/**
* Do task.
*/
void doTask() {
System.out.println(Thread.currentThread().getName() + " 抢到锁!");
Random random = new Random();
int _int = random.nextInt(200);
System.out.println(Thread.currentThread().getName() + " sleep " + _int + "millis");
try {
Thread.sleep(_int);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 释放锁!");
doneSignal.countDown();
}
}
}

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Thread-26 抢到锁!
Thread-26 sleep 181millis
Thread-26 释放锁!
Thread-25 抢到锁!
Thread-25 sleep 189millis
Thread-25 释放锁!
Thread-27 抢到锁!
Thread-27 sleep 42millis
Thread-27 释放锁!
Thread-29 抢到锁!
Thread-29 sleep 97millis
Thread-29 释放锁!
Thread-28 抢到锁!
Thread-28 sleep 45millis
Thread-28 释放锁!
All processors done. Shutdown connection

源码 GitHub:https://github.com/liaozihong/SpringBoot-Learning/tree/master/SpringBoot-Redis-Distributed-Redlock
参考链接:
如何用Redlock实现分布式锁
分布式锁和同步器

文章目录
  1. 1. RedLock 简介
    1. 1.1. 分布式锁实现
作者共写了53.1k个字 本站总访问量  |   您是访问本站的第个小伙伴