|
| 1 | +## RedLock 简介 |
| 2 | +在不同进程需要互斥地访问共享资源时,分布式锁是一种非常有用的技术手段。实现高效的分布式锁有三个属性需要考虑: |
| 3 | +``` |
| 4 | +安全属性:互斥,不管什么时候,只有一个客户端持有锁 |
| 5 | +效率属性A:不会死锁 |
| 6 | +效率属性B:容错,只要大多数redis节点能够正常工作,客户端端都能获取和释放锁。 |
| 7 | +解铃还须系铃人。加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。 |
| 8 | +``` |
| 9 | +Redlock是redis官方提出的实现分布式锁管理器的算法。这个算法会比一般的普通方法更加安全可靠。 |
| 10 | +Redisson提供了很多种类型的分布式锁和分布式同步器,如下: |
| 11 | +```text |
| 12 | +- 8.1. 可重入锁(Reentrant Lock) --例子以实现 |
| 13 | +- 8.2. 公平锁(Fair Lock) --例子以实现 |
| 14 | +- 8.3. 联锁(MultiLock) |
| 15 | +- 8.4. 红锁(RedLock) |
| 16 | +- 8.5. 读写锁(ReadWriteLock) |
| 17 | +- 8.6. 信号量(Semaphore) |
| 18 | +- 8.7. 可过期性信号量(PermitExpirableSemaphore) |
| 19 | +- 8.8. 闭锁(CountDownLatch) |
| 20 | +``` |
| 21 | +详情可查看下面的参考链接 |
| 22 | +### 分布式锁实现 |
| 23 | +```java |
| 24 | +@Component |
| 25 | +public class RedisLocker implements DistributedLocker { |
| 26 | + |
| 27 | + private final static String LOCKER_PREFIX = "lock:"; |
| 28 | + |
| 29 | + /** |
| 30 | + * The Redisson client. |
| 31 | + */ |
| 32 | + @Autowired |
| 33 | + RedissonClient redissonClient; |
| 34 | + |
| 35 | + @Override |
| 36 | + public <T> T lock(String resourceName, AcquiredLockWorker<T> worker) throws Exception { |
| 37 | + return fairLock(resourceName, worker, 100); |
| 38 | + } |
| 39 | + |
| 40 | + @Override |
| 41 | + public <T> T tryLock(String resourceName, AcquiredLockWorker<T> worker, int lockTime) throws Exception { |
| 42 | + RLock lock = redissonClient.getLock(LOCKER_PREFIX + resourceName); |
| 43 | + // (可重入锁)最多等待100秒,锁定后经过lockTime秒后自动解锁 |
| 44 | + boolean success = lock.tryLock(100, lockTime, TimeUnit.SECONDS); |
| 45 | + if (success) { |
| 46 | + try { |
| 47 | + return worker.invokeAfterLockAcquire(); |
| 48 | + } finally { |
| 49 | + lock.unlock(); |
| 50 | + } |
| 51 | + } |
| 52 | + throw new UnableToAcquireLockException(); |
| 53 | + } |
| 54 | + |
| 55 | + @Override |
| 56 | + public <T> T fairLock(String resourceName, AcquiredLockWorker<T> worker, int lockTime) throws Exception { |
| 57 | + RLock lock = redissonClient.getFairLock(LOCKER_PREFIX + resourceName); |
| 58 | + // (公平锁)最多等待100秒,锁定后经过lockTime秒后自动解锁 |
| 59 | + boolean success = lock.tryLock(100, lockTime, TimeUnit.SECONDS); |
| 60 | + if (success) { |
| 61 | + try { |
| 62 | + return worker.invokeAfterLockAcquire(); |
| 63 | + } finally { |
| 64 | + lock.unlock(); |
| 65 | + } |
| 66 | + } |
| 67 | + throw new UnableToAcquireLockException(); |
| 68 | + } |
| 69 | + |
| 70 | +} |
| 71 | +``` |
| 72 | +**配置客户端** |
| 73 | +```java |
| 74 | +@Configuration |
| 75 | +public class RedissonConfiguration { |
| 76 | + |
| 77 | + /** |
| 78 | + * Gets client. |
| 79 | + * |
| 80 | + * @return the client |
| 81 | + */ |
| 82 | + @Bean |
| 83 | + public RedissonClient redissonClient() { |
| 84 | + //如果是默认本地6379,则可不必配置,否则参照 |
| 85 | + // https://github.com/redisson/redisson/wiki/2.-%E9%85%8D%E7%BD%AE%E6%96%B9%E6%B3%95 配置 |
| 86 | + return Redisson.create(); |
| 87 | + } |
| 88 | +} |
| 89 | +``` |
| 90 | +**获取锁后的业务处理** |
| 91 | +```java |
| 92 | +/** |
| 93 | + * 获取锁后需要做的逻辑 |
| 94 | + * |
| 95 | + * @param <T> the type parameter |
| 96 | + * @author Liaozihong |
| 97 | + */ |
| 98 | +public interface AcquiredLockWorker<T> { |
| 99 | + /** |
| 100 | + * Invoke after lock aquire t. |
| 101 | + * |
| 102 | + * @return the t |
| 103 | + * @throws Exception the exception |
| 104 | + */ |
| 105 | + T invokeAfterLockAcquire() throws Exception; |
| 106 | +} |
| 107 | +``` |
| 108 | +**测试类** |
| 109 | +```java |
| 110 | +@RestController |
| 111 | +@Slf4j |
| 112 | +public class RedissonLockTestApi { |
| 113 | + /** |
| 114 | + * The Distributed locker. |
| 115 | + */ |
| 116 | + @Autowired |
| 117 | + RedisLocker distributedLocker; |
| 118 | + |
| 119 | + /** |
| 120 | + * Test redlock string. |
| 121 | + * 并发下分布式锁测试API |
| 122 | + * |
| 123 | + * @return the string |
| 124 | + * @throws Exception the exception |
| 125 | + */ |
| 126 | + @RequestMapping(value = "/redlock") |
| 127 | + public String testRedlock() throws Exception { |
| 128 | + CountDownLatch startSignal = new CountDownLatch(1); |
| 129 | + CountDownLatch doneSignal = new CountDownLatch(5); |
| 130 | + // 测试5个并发 |
| 131 | + for (int i = 0; i < 5; ++i) { |
| 132 | + new Thread(new Worker(startSignal, doneSignal)).start(); |
| 133 | + } |
| 134 | + startSignal.countDown(); // let all threads proceed |
| 135 | + doneSignal.await(); |
| 136 | + System.out.println("All processors done. Shutdown connection"); |
| 137 | + return "redlock"; |
| 138 | + } |
| 139 | + |
| 140 | + /** |
| 141 | + * Worker |
| 142 | + * <p/> |
| 143 | + * Created in 2018.12.05 |
| 144 | + * <p/> |
| 145 | + * |
| 146 | + * @author Liaozihong |
| 147 | + */ |
| 148 | + class Worker implements Runnable { |
| 149 | + private final CountDownLatch startSignal; |
| 150 | + private final CountDownLatch doneSignal; |
| 151 | + |
| 152 | + /** |
| 153 | + * Instantiates a new Worker. |
| 154 | + * |
| 155 | + * @param startSignal the start signal |
| 156 | + * @param doneSignal the done signal |
| 157 | + */ |
| 158 | + Worker(CountDownLatch startSignal, CountDownLatch doneSignal) { |
| 159 | + this.startSignal = startSignal; |
| 160 | + this.doneSignal = doneSignal; |
| 161 | + } |
| 162 | + |
| 163 | + @Override |
| 164 | + public void run() { |
| 165 | + try { |
| 166 | + startSignal.await(); |
| 167 | + //尝试加锁 |
| 168 | + distributedLocker.lock("test", new AcquiredLockWorker<Object>() { |
| 169 | + |
| 170 | + @Override |
| 171 | + public Object invokeAfterLockAcquire() { |
| 172 | + doTask(); |
| 173 | + return "success"; |
| 174 | + } |
| 175 | + |
| 176 | + }); |
| 177 | + } catch (Exception e) { |
| 178 | + log.warn("获取锁出现异常", e); |
| 179 | + } |
| 180 | + } |
| 181 | + |
| 182 | + /** |
| 183 | + * Do task. |
| 184 | + */ |
| 185 | + void doTask() { |
| 186 | + System.out.println(Thread.currentThread().getName() + " 抢到锁!"); |
| 187 | + Random random = new Random(); |
| 188 | + int _int = random.nextInt(200); |
| 189 | + System.out.println(Thread.currentThread().getName() + " sleep " + _int + "millis"); |
| 190 | + try { |
| 191 | + Thread.sleep(_int); |
| 192 | + } catch (InterruptedException e) { |
| 193 | + e.printStackTrace(); |
| 194 | + } |
| 195 | + System.out.println(Thread.currentThread().getName() + " 释放锁!"); |
| 196 | + doneSignal.countDown(); |
| 197 | + } |
| 198 | + } |
| 199 | +} |
| 200 | +``` |
| 201 | +运行结果: |
| 202 | +```text |
| 203 | +Thread-26 抢到锁! |
| 204 | +Thread-26 sleep 181millis |
| 205 | +Thread-26 释放锁! |
| 206 | +Thread-25 抢到锁! |
| 207 | +Thread-25 sleep 189millis |
| 208 | +Thread-25 释放锁! |
| 209 | +Thread-27 抢到锁! |
| 210 | +Thread-27 sleep 42millis |
| 211 | +Thread-27 释放锁! |
| 212 | +Thread-29 抢到锁! |
| 213 | +Thread-29 sleep 97millis |
| 214 | +Thread-29 释放锁! |
| 215 | +Thread-28 抢到锁! |
| 216 | +Thread-28 sleep 45millis |
| 217 | +Thread-28 释放锁! |
| 218 | +All processors done. Shutdown connection |
| 219 | +``` |
| 220 | +源码 GitHub:https://github.com/liaozihong/SpringBoot-Learning/tree/master/SpringBoot-Redis-Distributed-Redlock |
| 221 | +参考链接: |
| 222 | +[如何用Redlock实现分布式锁](https://blog.csdn.net/forezp/article/details/70305336) |
| 223 | +[分布式锁和同步器](https://github.com/redisson/redisson/wiki/8.-%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81%E5%92%8C%E5%90%8C%E6%AD%A5%E5%99%A8) |
0 commit comments