成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Java 從零實現屬于你的 Redis 分布式鎖

開發 前端 新聞 分布式 Redis
我們想要解決分布式系統中的并發問題,就需要引入分布式鎖的概念。

[[347022]]

 

 java 從零實現屬于你的 redis 分布式鎖

redis分布式鎖

為什么需要分布式鎖

在 jdk 中為我們提供了加鎖的方式:

(1)synchronized 關鍵字

(2)volatile + CAS 實現的樂觀鎖

(3)ReadWriteLock 讀寫鎖

(4)ReenTrantLock 可重入鎖

等等,這些鎖為我們變成提供極大的便利性,保證在多線程的情況下,保證線程安全。

但是在分布式系統中,上面的鎖就統統沒用了。

我們想要解決分布式系統中的并發問題,就需要引入分布式鎖的概念。

java 代碼實現

創作動機

首先是對鎖實現原理的一個實現,理論指導實踐,實踐完善理論。

晚上關于 redis 分布式鎖的文章一大堆,但是也都稂莠不齊。

redis 分布式鎖工具有時候中間件團隊不見得會提供,提供了也不見得經常維護,不如自己實現一個,知道原理,也方便修改。

接口定義

為了便于和 JDK 復用,我們讓接口繼承自 jdk 的 Lock 接口。

  1. package com.github.houbb.lock.api.core; 
  2.  
  3. import java.util.concurrent.TimeUnit; 
  4. import java.util.concurrent.locks.Lock; 
  5.  
  6. /** 
  7.  * 鎖定義 
  8.  * @author binbin.hou 
  9.  * @since 0.0.1 
  10.  */ 
  11. public interface ILock extends Lock { 
  12.  
  13.     /** 
  14.      * 嘗試加鎖 
  15.      * @param time 時間 
  16.      * @param unit 當為 
  17.      * @param key key 
  18.      * @return 返回 
  19.      * @throws InterruptedException 異常 
  20.      * @since 0.0.1 
  21.      */ 
  22.     boolean tryLock(long time, TimeUnit unit, 
  23.                     String key) throws InterruptedException; 
  24.  
  25.     /** 
  26.      * 嘗試加鎖 
  27.      * @param key key 
  28.      * @return 返回 
  29.      * @since 0.0.1 
  30.      */ 
  31.     boolean tryLock(String key); 
  32.  
  33.     /** 
  34.      * 解鎖 
  35.      * @param key key 
  36.      * @since 0.0.1 
  37.      */ 
  38.     void unlock(String key); 
  39.  

方法我們只添加了三個比較常用的核心方法,作為第一個版本,簡單點。

后續陸續添加即可。

抽象實現

為了便于后期添加更多的所實現,這里首先實現了一個公用的抽象父類。

  1. package com.github.houbb.lock.redis.core; 
  2.  
  3. import com.github.houbb.lock.api.core.ILock; 
  4. import com.github.houbb.lock.redis.constant.LockRedisConst; 
  5. import com.github.houbb.wait.api.IWait; 
  6.  
  7. import java.util.concurrent.TimeUnit; 
  8. import java.util.concurrent.locks.Condition; 
  9.  
  10. /** 
  11.  * 抽象實現 
  12.  * @author binbin.hou 
  13.  * @since 0.0.1 
  14.  */ 
  15. public abstract class AbstractLockRedis implements ILock { 
  16.  
  17.     /** 
  18.      * 鎖等待 
  19.      * @since 0.0.1 
  20.      */ 
  21.     private final IWait wait; 
  22.  
  23.     protected AbstractLockRedis(IWait wait) { 
  24.         this.wait = wait; 
  25.     } 
  26.  
  27.     @Override 
  28.     public void lock() { 
  29.         throw new UnsupportedOperationException(); 
  30.     } 
  31.  
  32.     @Override 
  33.     public void lockInterruptibly() throws InterruptedException { 
  34.         throw new UnsupportedOperationException(); 
  35.     } 
  36.  
  37.     @Override 
  38.     public boolean tryLock() { 
  39.         return tryLock(LockRedisConst.DEFAULT_KEY); 
  40.     } 
  41.  
  42.     @Override 
  43.     public void unlock() { 
  44.         unlock(LockRedisConst.DEFAULT_KEY); 
  45.     } 
  46.  
  47.     @Override 
  48.     public boolean tryLock(long time, TimeUnit unit, String key) throws InterruptedException { 
  49.         long startTimeMills = System.currentTimeMillis(); 
  50.  
  51.         // 一次獲取,直接成功 
  52.         boolean result = this.tryLock(key); 
  53.         if(result) { 
  54.             return true
  55.         } 
  56.  
  57.         // 時間判斷 
  58.         if(time <= 0) { 
  59.             return false
  60.         } 
  61.         long durationMills = unit.toMillis(time); 
  62.         long endMills = startTimeMills + durationMills; 
  63.  
  64.         // 循環等待 
  65.         while (System.currentTimeMillis() < endMills) { 
  66.             result = tryLock(key); 
  67.             if(result) { 
  68.                 return true
  69.             } 
  70.  
  71.             // 等待 10ms 
  72.             wait.wait(TimeUnit.MILLISECONDS, 10); 
  73.         } 
  74.         return false
  75.     } 
  76.  
  77.     @Override 
  78.     public synchronized boolean tryLock(long time, TimeUnit unit) throws InterruptedException { 
  79.         return tryLock(time, unit, LockRedisConst.DEFAULT_KEY); 
  80.     } 
  81.  
  82.     @Override 
  83.     public Condition newCondition() { 
  84.         throw new UnsupportedOperationException(); 
  85.     } 
  86.  

最核心的實際上是 public boolean tryLock(long time, TimeUnit unit, String key) throws InterruptedException 方法。

這個方法會調用 this.tryLock(key) 獲取鎖,如果成功,直接返回;如果不成功,則循環等待。

這里設置了超時時間,如果超時,則直接返回 true。

redis 鎖實現

我們實現的 redis 分布鎖,繼承自上面的抽象類。

  1. package com.github.houbb.lock.redis.core; 
  2.  
  3. import com.github.houbb.heaven.util.lang.StringUtil; 
  4. import com.github.houbb.id.api.Id; 
  5. import com.github.houbb.id.core.util.IdThreadLocalHelper; 
  6. import com.github.houbb.lock.redis.constant.LockRedisConst; 
  7. import com.github.houbb.lock.redis.exception.LockRedisException; 
  8. import com.github.houbb.lock.redis.support.operator.IOperator; 
  9. import com.github.houbb.wait.api.IWait; 
  10.  
  11. /** 
  12.  * 這里是基于 redis 實現 
  13.  * 
  14.  * 實際上也可以基于 zk/數據庫等實現。 
  15.  * 
  16.  * @author binbin.hou 
  17.  * @since 0.0.1 
  18.  */ 
  19. public class LockRedis extends AbstractLockRedis { 
  20.  
  21.     /** 
  22.      * redis 操作實現 
  23.      * @since 0.0.1 
  24.      */ 
  25.     private final IOperator redisOperator; 
  26.  
  27.     /** 
  28.      * 主鍵標識 
  29.      * @since 0.0.1 
  30.      */ 
  31.     private final Id id; 
  32.  
  33.     public LockRedis(IWait wait, IOperator redisOperator, Id id) { 
  34.         super(wait); 
  35.         this.redisOperator = redisOperator; 
  36.         this.id = id; 
  37.     } 
  38.  
  39.     @Override 
  40.     public boolean tryLock(String key) { 
  41.         final String requestId = id.id(); 
  42.         IdThreadLocalHelper.put(requestId); 
  43.  
  44.         return redisOperator.lock(key, requestId, LockRedisConst.DEFAULT_EXPIRE_MILLS); 
  45.     } 
  46.  
  47.     @Override 
  48.     public void unlock(String key) { 
  49.         final String requestId = IdThreadLocalHelper.get(); 
  50.         if(StringUtil.isEmpty(requestId)) { 
  51.             String threadName = Thread.currentThread().getName(); 
  52.             throw new LockRedisException("Thread " + threadName +" not contains requestId"); 
  53.         } 
  54.  
  55.         boolean unlock = redisOperator.unlock(key, requestId); 
  56.         if(!unlock) { 
  57.             throw new LockRedisException("Unlock key " + key + " result is failed!"); 
  58.         } 
  59.     } 

這里就是 redis 鎖的核心實現了,如果不太理解,建議回顧一下原理篇:

redis 分布式鎖原理詳解

加鎖

加鎖部分,這里會生成一個 id 標識,用于區分當前操作者。

為了安全也設置了默認的超時時間。

當然這里是為了簡化調用者的使用成本,開發在使用的時候只需要關心自己要加鎖的 key 即可。

當然,甚至連加鎖的 key 都可以進一步抽象掉,比如封裝 @DistributedLock 放在方法上,即可實現分布式鎖。這個后續有時間可以拓展,原理也不難。

解鎖

解鎖的時候,就會獲取當前進程的持有標識。

憑借當前線程持有的 id 標識,去解鎖。

IOperator

我們對 redis 的操作進行了抽象,為什么抽象呢?

因為 redis 服務種類實際很多,可以是 redis 單點,集群,主從,哨兵。

連接的客戶端也可以很多,jedis,spring redisTemplate, codis, redisson 等等。

這里為了后期拓展方便,就對操作進行了抽象。

接口

定義接口如下:

  1. package com.github.houbb.lock.redis.support.operator; 
  2.  
  3. /** 
  4.  * Redis 客戶端 
  5.  * @author binbin.hou 
  6.  * @since 0.0.1 
  7.  */ 
  8. public interface IOperator { 
  9.  
  10.     /** 
  11.      * 嘗試獲取分布式鎖 
  12.      * 
  13.      * @param lockKey    鎖 
  14.      * @param requestId  請求標識 
  15.      * @param expireTimeMills 超期時間 
  16.      * @return 是否獲取成功 
  17.      * @since 0.0.1 
  18.      */ 
  19.     boolean lock(String lockKey, String requestId, int expireTimeMills); 
  20.  
  21.     /** 
  22.      * 解鎖 
  23.      * @param lockKey 鎖 key 
  24.      * @param requestId 請求標識 
  25.      * @return 結果 
  26.      * @since 0.0.1 
  27.      */ 
  28.     boolean unlock(String lockKey, String requestId); 
  29.  

jedis 實現

我們實現一個 jedis 單點版本的:

  1. package com.github.houbb.lock.redis.support.operator.impl; 
  2.  
  3. import com.github.houbb.lock.redis.constant.LockRedisConst; 
  4. import com.github.houbb.lock.redis.support.operator.IOperator; 
  5. import redis.clients.jedis.Jedis; 
  6.  
  7. import java.util.Collections; 
  8.  
  9. /** 
  10.  * Redis 客戶端 
  11.  * @author binbin.hou 
  12.  * @since 0.0.1 
  13.  */ 
  14. public class JedisOperator implements IOperator { 
  15.  
  16.     /** 
  17.      * jedis 客戶端 
  18.      * @since 0.0.1 
  19.      */ 
  20.     private final Jedis jedis; 
  21.  
  22.     public JedisOperator(Jedis jedis) { 
  23.         this.jedis = jedis; 
  24.     } 
  25.  
  26.     /** 
  27.      * 嘗試獲取分布式鎖 
  28.      * 
  29.      * expireTimeMills 保證當前進程掛掉,也能釋放鎖 
  30.      * 
  31.      * requestId 保證解鎖的是當前進程(鎖的持有者) 
  32.      * 
  33.      * @param lockKey         鎖 
  34.      * @param requestId       請求標識 
  35.      * @param expireTimeMills 超期時間 
  36.      * @return 是否獲取成功 
  37.      * @since 0.0.1 
  38.      */ 
  39.     @Override 
  40.     public boolean lock(String lockKey, String requestId, int expireTimeMills) { 
  41.         String result = jedis.set(lockKey, requestId, LockRedisConst.SET_IF_NOT_EXIST, LockRedisConst.SET_WITH_EXPIRE_TIME, expireTimeMills); 
  42.         return LockRedisConst.LOCK_SUCCESS.equals(result); 
  43.     } 
  44.  
  45.     /** 
  46.      * 解鎖 
  47.      * 
  48.      * (1)使用 requestId,保證為當前鎖的持有者 
  49.      * (2)使用 lua 腳本,保證執行的原子性。 
  50.      * 
  51.      * @param lockKey   鎖 key 
  52.      * @param requestId 請求標識 
  53.      * @return 結果 
  54.      * @since 0.0.1 
  55.      */ 
  56.     @Override 
  57.     public boolean unlock(String lockKey, String requestId) { 
  58.         String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"
  59.         Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId)); 
  60.         return LockRedisConst.RELEASE_SUCCESS.equals(result); 
  61.     } 
  62.  

這里時最核心的部分。

別看簡單幾行代碼,需要注意的點還是很多的。

加鎖

加鎖時附帶 requestId,用來標識自己為鎖的持有者。

SETNX 當 key 不存在時才進行加鎖。

設置加鎖的過期時間,避免因異常等原因未釋放鎖,導致鎖的長時間占用。

解鎖

使用 lua 腳本,保證操作的原子性。

為了證明為鎖的持有者,傳入 requestId。

測試驗證

maven 引入

  1. <dependency> 
  2.     <groupId>com.github.houbb</groupId> 
  3.     <artifactId>lock-core</artifactId> 
  4.     <version>0.0.1</version> 
  5. </dependency> 

測試代碼

  1. Jedis jedis = new Jedis("127.0.0.1"6379); 
  2. IOperator operator = new JedisOperator(jedis); 
  3.  
  4. // 獲取鎖 
  5. ILock lock = LockRedisBs.newInstance().operator(operator).lock(); 
  6.  
  7. try { 
  8.     boolean lockResult = lock.tryLock(); 
  9.     System.out.println(lockResult); 
  10.     // 業務處理 
  11. catch (Exception e) { 
  12.     e.printStackTrace(); 
  13. finally { 
  14.     lock.unlock(); 

小結

到這里,一個簡單版本的 redis 分布式鎖就實現完成了。

當然還有很多可以改進的地方:

(1)比如引入遞增的 sequence,避免分布式鎖中的 GC 導致的問題

(2)對于更多 redis 服務端+客戶端的支持

(3)對于注解式 redis 分布式鎖的支持

責任編輯:張燕妮 來源: 今日頭條
相關推薦

2022-01-06 10:58:07

Redis數據分布式鎖

2023-08-21 19:10:34

Redis分布式

2019-06-19 15:40:06

分布式鎖RedisJava

2021-11-26 06:43:19

Java分布式

2019-02-26 09:51:52

分布式鎖RedisZookeeper

2024-10-07 10:07:31

2023-03-01 08:07:51

2024-04-01 05:10:00

Redis數據庫分布式鎖

2022-05-18 10:38:51

Redis分布式鎖數據

2023-10-11 09:37:54

Redis分布式系統

2019-12-25 14:35:33

分布式架構系統

2020-07-30 09:35:09

Redis分布式鎖數據庫

2020-07-15 16:50:57

Spring BootRedisJava

2024-11-28 15:11:28

2023-01-13 07:39:07

2021-10-09 11:34:59

MySQL分布式鎖庫存

2022-06-16 08:01:24

redis分布式鎖

2022-03-04 09:54:04

Redis分布式鎖腳本

2021-02-28 07:49:28

Zookeeper分布式

2022-01-14 08:35:58

Curator分布式鎖Zookeeper
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 91在线免费视频 | 金莲网| 久久久久国产精品午夜一区 | 日韩av免费看 | 欧美国产免费 | 一区二区三区高清 | 一级一片在线观看 | av网站推荐| 亚洲国产精品一区二区www | 欧美精品一区二区在线观看 | 成人精品国产免费网站 | 国产精品久久久av | 在线精品亚洲欧美日韩国产 | 精品欧美黑人一区二区三区 | 免费一级片 | 亚洲欧美国产视频 | 99在线免费观看视频 | 中文字幕一区二区三区在线观看 | 精品一区二区三区91 | 做a视频在线观看 | 国产成人一区二区三区久久久 | 国产一区二区麻豆 | 欧美日韩久久久 | 日韩av一区在线观看 | 成人一区二| 欧美八区| 91视频正在播放 | 美女二区 | av黄色在线观看 | 精品亚洲一区二区三区 | 精品亚洲一区二区三区 | 老牛影视av一区二区在线观看 | 日韩高清国产一区在线 | 国产精品99视频 | 成人欧美一区二区三区在线观看 | 国产午夜精品久久久 | 日操夜操 | 91xxx在线观看 | 日本在线你懂的 | 日韩精品一区二区三区老鸭窝 | 成年人国产在线观看 |