如何用Redlock实现分布式锁
轉:https://blog.csdn.net/forezp/article/details/70305336
之前寫過一篇文章《如何在springcloud分布式系統中實現分布式鎖?》(https://blog.csdn.net/forezp/article/details/68957681),由于自己僅僅是閱讀了相關的書籍,和查閱了相關的資料,就認為那樣的是可行的。那篇文章實現的大概思路是用setNx命令和setEx配合使用。 setNx是一個耗時操作,因為它需要查詢這個鍵是否存在,就算redis的百萬的qps,在高并發的場景下,這種操作也是有問題的。關于redis實現分布式鎖,redis官方推薦使用redlock。
一、redlock簡介
在不同進程需要互斥地訪問共享資源時,分布式鎖是一種非常有用的技術手段。實現高效的分布式鎖有三個屬性需要考慮:
1、安全屬性:互斥,不管什么時候,只有一個客戶端持有鎖 2、效率屬性A:不會死鎖 3、效率屬性B:容錯,只要大多數redis節點能夠正常工作,客戶端端都能獲取和釋放鎖。Redlock是redis官方提出的實現分布式鎖管理器的算法。這個算法會比一般的普通方法更加安全可靠。關于這個算法的討論可以看下官方文檔(https://github.com/antirez/redis-doc/blob/master/topics/distlock.md)。
二、怎么用java使用 redlock
在pom文件引入redis和redisson依賴:
<!-- redis--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- redisson--> <dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.3.2</version> </dependency>AquiredLockWorker接口類,,主要是用于獲取鎖后需要處理的邏輯:
/*** Created by fangzhipeng on 2017/4/5.* 獲取鎖后需要處理的邏輯*/ public interface AquiredLockWorker<T> {T invokeAfterLockAquire() throws Exception; }DistributedLocker 獲取鎖管理類:
/*** Created by fangzhipeng on 2017/4/5.* 獲取鎖管理類*/ public interface DistributedLocker {/*** 獲取鎖* @param resourceName 鎖的名稱* @param worker 獲取鎖后的處理類* @param <T>* @return 處理完具體的業務邏輯要返回的數據* @throws UnableToAquireLockException* @throws Exception*/<T> T lock(String resourceName, AquiredLockWorker<T> worker) throws UnableToAquireLockException, Exception;<T> T lock(String resourceName, AquiredLockWorker<T> worker, int lockTime) throws UnableToAquireLockException, Exception;}UnableToAquireLockException ,不能獲取鎖的異常類:
/*** Created by fangzhipeng on 2017/4/5.* 異常類*/ public class UnableToAquireLockException extends RuntimeException {public UnableToAquireLockException() {}public UnableToAquireLockException(String message) {super(message);}public UnableToAquireLockException(String message, Throwable cause) {super(message, cause);} }RedissonConnector 連接類:
/*** Created by fangzhipeng on 2017/4/5.* 獲取RedissonClient連接類*/ @Component public class RedissonConnector {RedissonClient redisson;@PostConstructpublic void init(){redisson = Redisson.create();}public RedissonClient getClient(){return redisson;} }RedisLocker 類,實現了DistributedLocker:
import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.concurrent.TimeUnit;/*** Created by fangzhipeng on 2017/4/5.*/ @Component public class RedisLocker implements DistributedLocker{private final static String LOCKER_PREFIX = "lock:";@AutowiredRedissonConnector redissonConnector;@Overridepublic <T> T lock(String resourceName, AquiredLockWorker<T> worker) throws InterruptedException, UnableToAquireLockException, Exception {return lock(resourceName, worker, 100);}@Overridepublic <T> T lock(String resourceName, AquiredLockWorker<T> worker, int lockTime) throws UnableToAquireLockException, Exception {RedissonClient redisson= redissonConnector.getClient();RLock lock = redisson.getLock(LOCKER_PREFIX + resourceName);// Wait for 100 seconds seconds and automatically unlock it after lockTime secondsboolean success = lock.tryLock(100, lockTime, TimeUnit.SECONDS);if (success) {try {return worker.invokeAfterLockAquire();} finally {lock.unlock();}}throw new UnableToAquireLockException();} }測試類:
@AutowiredRedisLocker distributedLocker;@RequestMapping(value = "/redlock")public String testRedlock() throws Exception{CountDownLatch startSignal = new CountDownLatch(1);CountDownLatch doneSignal = new CountDownLatch(5);for (int i = 0; i < 5; ++i) { // create and start threadsnew Thread(new Worker(startSignal, doneSignal)).start();}startSignal.countDown(); // let all threads proceeddoneSignal.await();System.out.println("All processors done. Shutdown connection");return "redlock";}class Worker implements Runnable {private final CountDownLatch startSignal;private final CountDownLatch doneSignal;Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {this.startSignal = startSignal;this.doneSignal = doneSignal;}public void run() {try {startSignal.await();distributedLocker.lock("test",new AquiredLockWorker<Object>() {@Overridepublic Object invokeAfterLockAquire() {doTask();return null;}});}catch (Exception e){}}void doTask() {System.out.println(Thread.currentThread().getName() + " start");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() + " end");doneSignal.countDown();}}運行測試類:
Thread-48 start Thread-48 sleep 99millis Thread-48 end Thread-49 start Thread-49 sleep 118millis Thread-49 end Thread-52 start Thread-52 sleep 141millis Thread-52 end Thread-50 start Thread-50 sleep 28millis Thread-50 end Thread-51 start Thread-51 sleep 145millis Thread-51 end從運行結果上看,在異步任務的情況下,確實是獲取鎖之后才能運行線程。不管怎么樣,這是redis官方推薦的一種方案,可靠性比較高。有什么問題歡迎留言。
三、參考資料
https://github.com/redisson/redisson
《Redis官方文檔》用Redis構建分布式鎖 : http://ifeve.com/redis-lock/
A Look at the Java Distributed In-Memory Data Model (Powered by Redis) : https://dzone.com/articles/java-distributed-in-memory-data-model-powered-by-r
總結
以上是生活随笔為你收集整理的如何用Redlock实现分布式锁的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 史上最简单的 SpringCloud 教
- 下一篇: 实战docker+jenkins+git