RedLock 簡介
在不同進程需要互斥地訪問共享資源時,分佈式鎖是一種非常有用的技術手段。實現高效的分佈式鎖有三個屬性需要考慮:
安全屬性:互斥,不管什麼時候,只有一個客戶端持有鎖
效率屬性A:不會死鎖
效率屬性B:容錯,只要大多數redis節點能夠正常工作,客戶端端都能獲取和釋放鎖。
解鈴還須繫鈴人。加鎖和解鎖必須是同一個客戶端,客戶端自己不能把別人加的鎖給解了。
Redlock是redis官方提出的實現分佈式鎖管理器的算法。這個算法會比一般的普通方法更加安全可靠。
Redisson提供了很多種類型的分佈式鎖和分佈式同步器,如下:
- 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)
詳情可查看下面的參考鏈接
分佈式鎖實現
@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();
}
}
配置客戶端
@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();
}
}
獲取鎖後的業務處理
/**
* 獲取鎖後需要做的邏輯
*
* @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;
}
測試類
@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();
}
}
}
運行結果:
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實現分佈式鎖
分佈式鎖和同步器