A. Redis的Setnx命令實現分布式鎖
首先,分布式鎖和我們平常講到的鎖原理基本一樣,目的就是確保在多個線程並發時,只有一個線程在同一刻操作這個業務或者說方法、變數。
在一個進程中,也就是一個jvm或者說應用中,我們很容易去處理控制,在 java.util 並發包中已經為我們提供了這些方法去加鎖,比如 synchronized 關鍵字或者 Lock 鎖,都可以處理。
但是如果在分布式環境下,要保證多個線程同時只有1個能訪問某個資源,就需要用到分布式鎖。這里我們將介紹用Redis的 setnx 命令來實現分布式鎖。
其實目前通常所說的 setnx 命令,並非單指redis的 setnx key value 這條命令,這條命令可能會在後期redis版本中刪除。
一般代指redis中對 set 命令加上 nx 參數進行使用, set 這個命令,目前已經支持這么多參數可選:
從 Redis 2.6.12 版本開始, SET 命令的行為可以通過一系列參數來修改:
注入bean
這里同時啟動5個線程並發往redis中存儲 lock 這個key(key可以自定義,但需要一致),同時設置10秒的過期時間。
setIfAbsent 這個函數實現的功能與 setnx 命令一樣,代表如果沒有這個key則set成功獲取到鎖,否則set失敗沒有獲取到鎖。
獲得鎖後進行資源的操作,最後釋放鎖。
執行效果 :
可以看到同時只有1個線程能夠獲取到鎖。
使用 setnx 命令方式雖然操作比較簡單方便,但是會有如下問題:
可以在叢肢再次獲取鎖時,如果鎖被佔用就get值,判斷值是否是當前線程存的隨機值,如果是則再次執行 set 命令重新上鎖;當然為了保證原子性這些操作都要用 lua 腳本來執行。
可以使用 while 循環重復執行 setnx 命令,並設置一個超時時間退出循環。
可以盡量把鎖自動過期的時間設的冗餘一些。但也不能絕橡徹底解決。
可以在刪除鎖的時候先get值,判斷值是否是當前線程存的隨機值,只有相同才執行滲宏世刪鎖的操作;當然也要使用 lua 腳本執行來保證原子性。
分布式鎖需要滿足的特性
綜上:使用 setnx 命令來實現分布式鎖並不是一個很嚴謹的方案,如果是Java技術棧,我們可以使用 Redisson 庫來解決以上問題,接下來的文章會介紹如何使用。
Redisson實現分布式鎖
Redlock實現分布式鎖
B. Springboot使用redis的setnx和getset實現並發鎖、分布式鎖
在日常開發中,很多業圓搏務場景必須保證原子性。舉幾個例子:
如果你只有一台伺服器,只運行一個Java程序,那麼可以使用Java語言自身的一些鎖來實現原子性。但如果我們有多台伺服器,甚至不同伺服器上跑的是不同的語言。那這時候,我們就需要一個跨平台、跨語言的加鎖方式。redis就是其中最方便的一種。
使用redis實現並發鎖,主要是靠兩個redis的命令:setnx和getset。
那我們的設計思路就是:
上面的代碼使用了一個RedisService的類薯升,裡面主要是簡單封裝了一數腔老下redis的操作,你可以替換為自己的service。代碼如下:
以上代碼有任何疑問,可以點擊右側邊欄聯系作者。收費5毛~交個朋友,歡迎來撩!
版權聲明:《Springboot使用redis的setnx和getset實現並發鎖、分布式鎖》為CoderBBB作者「ʘᴗʘ」的原創文章,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://www.coderbbb.com/articles/2
C. java 鏈接redis 怎麼加鎖
我介紹一下Redis分布式鎖吧:
一、定義redis實現分布式鎖的介面
[java]viewplainprint?
packagecom.iol.common.util.concurrent.locks;
importjava.io.Serializable;
/**
*Description:定義redis實現分布式鎖的演算法<br/>
*_SMALL_TAIL.<br/>
*ProgramName:IOL_SMALL_TAIL<br/>
*Date:2015年11月8日
*
*@author王鑫
*@version1.0
*/
{
/**
*加鎖演算法<br/>
*@paramkey
*@return
*/
publicbooleanlock(Stringkey);
/**
*解鎖演算法<br/>
*@paramkey
*@return
*/
publicbooleanunLock(Stringkey);
}
二、redis分布式鎖基礎演算法實現
[java]viewplainprint?
packagecom.iol.common.util.concurrent.locks.arithmetic;
importorg.slf4j.Logger;
importorg.slf4j.LoggerFactory;
importcom.iol.common.util.concurrent.locks.IRedisComponent;
importcom.iol.common.util.concurrent.locks.IRedisLockArithmetic;
/**
*Description:redis分布式鎖基礎演算法實現<br/>
*_SMALL_TAIL.<br/>
*ProgramName:IOL_SMALL_TAIL<br/>
*Date:2015年11月9日
*
*@author王鑫
*@version1.0
*/
{
/**
*serialVersionUID
*/
=-8333946071502606883L;
privateLoggerlogger=LoggerFactory.getLogger(RedisLockBaseArithmetic.class);
/**
*redis操作方法
*/
;
/**
*超時時間,以毫秒為單位<br/>
*默認為5分鍾
*/
privatelongovertime=5*60*1000L;
/**
*休眠時長,以毫秒為單位<br/>
*默認為100毫秒
*/
privatelongsleeptime=100L;
/**
*當前時間
*/
privatelongcurrentLockTime;
/**
*@
*/
publicvoidsetRedisComp(IRedisComponentredisComp){
this.redisComp=redisComp;
}
/**
*@paramovertimetheovertimetoset
*/
publicvoidsetOvertime(longovertime){
this.overtime=overtime;
}
/**
*@
*/
publicvoidsetSleeptime(longsleeptime){
this.sleeptime=sleeptime;
}
/*(non-Javadoc)
*@seecom.iol.common.util.concurrent.locks.IRedisLockArithmetic#lock(java.lang.String,java.lang.Long)
*/
@Override
publicbooleanlock(Stringkey){
while(true){
//當前加鎖時間
currentLockTime=System.currentTimeMillis();
if(redisComp.setIfAbsent(key,currentLockTime)){
//獲取鎖成功
logger.debug("直接獲取鎖{key:{},currentLockTime:{}}",key,currentLockTime);
returntrue;
}else{
//其他線程佔用了鎖
logger.debug("檢測到鎖被佔用{key:{},currentLockTime:{}}",key,currentLockTime);
LongotherLockTime=redisComp.get(key);
if(otherLockTime==null){
//其他系統釋放了鎖
//立刻重新嘗試加鎖
logger.debug("檢測到鎖被釋放{key:{},currentLockTime:{}}",key,currentLockTime);
continue;
}else{
if(currentLockTime-otherLockTime>=overtime){
//鎖超時
//嘗試更新鎖
logger.debug("檢測到鎖超時{key:{},currentLockTime:{},otherLockTime:{}}",key,currentLockTime,otherLockTime);
LongotherLockTime2=redisComp.getAndSet(key,currentLockTime);
if(otherLockTime2==null||otherLockTime.equals(otherLockTime2)){
logger.debug("獲取到超時鎖{key:{},currentLockTime:{},otherLockTime:{},otherLockTime2:{}}",key,currentLockTime,otherLockTime,otherLockTime2);
returntrue;
}else{
sleep();
//重新嘗試加鎖
logger.debug("重新嘗試加鎖{key:{},currentLockTime:{}}",key,currentLockTime);
continue;
}
}else{
//鎖未超時
sleep();
//重新嘗試加鎖
logger.debug("重新嘗試加鎖{key:{},currentLockTime:{}}",key,currentLockTime);
continue;
}
}
}
}
}
/*(non-Javadoc)
*@seecom.iol.common.util.concurrent.locks.IRedisLockArithmetic#unLock(java.lang.String)
*/
@Override
publicbooleanunLock(Stringkey){
logger.debug("解鎖{key:{}}",key);
redisComp.delete(key);
returntrue;
}
/**
*休眠<br/>
*@paramsleeptime
*/
privatevoidsleep(){
try{
Thread.sleep(sleeptime);
}catch(InterruptedExceptione){
thrownewLockException("線程異常中斷",e);
}
}
}
D. 北京java課程分享分布式限流的運行原理
分布式編程架構技術我們在前幾期的文章中已經給大家簡單分析過很多次了,今天我們就一起來了解一下API網關分布式限流的運正滑悔行原理都有哪些。
API網關中針對一個API、API分組、接入應用APPID,IP等進行限流。這些限流條件都將會產生一個限流使用的key,在後續的限流中都是對這個key進行限流。
限流演算法通常在API網關中可以採用令牌桶演算法實現。
必須說明一點的是分布式限流由於有網路的開銷,TPS的支持隔本地限流是有差距的,因此在對於TPS要求很高的場景,建議採用本地限流進行處理。
下面討論我們應該採用redis的哪一種分布式鎖的方案:
由於redis事務要得到鎖的效果需要在高TPS時會產生大量的無效的訪問請求,所以不建議在這種場景下使用。
SETNX/EX的鎖方案會產生在過期時間的問題,同時也有非同步復制master數據到slave的問題。相比lua方案會產生更多的不穩定性。
我建議採用lua的方案來實施分布式鎖,因為都是單進程單線程的執行,因此在TPS上和二種方案沒有大的區別,而且由於只是一個lua腳本在執行,甚至是可能純lua執行可能會有更高的TPS。當然是lua腳本中可能還是會去設置過期時間,讓悶但是應用server宕機並不會影響到redis中的鎖。當然master非同步復制的問題還是有,但是並不會造成問題,因為數據只會有1個lua腳本執行問題,下一個執行就正常了。
在實現方案的時候使用了Jedis庫,北京java課程認為有一些問題舉正在方案的實現層面我已經去做過驗證了,可能也會是讀者的疑問。
E. 使用Redisson實現分布式鎖
Redisson的分布式可重入鎖RLock Java對象實現了java.util.concurrent.locks.Lock介面,同時還支持自動過期解鎖。
Redisson同時還為分布式鎖提供了非同步執行的相關方法:
Redisson分布式可重入公平鎖也是實現了java.util.concurrent.locks.Lock介面的一種RLock對象。在提供了自動過期解鎖坦歷功能的同時,保證了當多個Redisson客戶端線程同時請求加鎖時,優先分配給先發出請求的線程。
Redisson同時還為分布式可重入公平鎖提供了非同步執行的相關方法:
Redisson的RedissonMultiLock對象可以將多個RLock對象關聯為一個聯鎖,每個RLock對象實例可以來自於不同的Redisson實例。
Redisson的RedissonRedLock對象實現了 Redlock 介紹的加鎖演算法。該對象也可以用來將多個RLock
對象關聯為一個紅鎖,每個RLock對象實例可以來自於不同的Redisson實例。
Redisson的分布式可重入讀寫鎖RReadWriteLock Java對象實現了java.util.concurrent.locks.ReadWriteLock介面。同時還支持自動過期解鎖。該對象允許同時有多個讀取鎖,但是最多隻能有一個寫入鎖。
Redisson的分布式信號量(Semaphore)Java對讓顫搜象RSemaphore採用了與java.util.concurrent.Semaphore相似的介面和用法。
Redisson的可過期性信號量(PermitExpirableSemaphore)實在RSemaphore對象的基礎上,為每個信號增加了一個過期時間。每個信號可以通過獨立洞禪的ID來辨識,釋放時只能通過提交這個ID才能釋放。
Redisson的分布式閉鎖(CountDownLatch)Java對象RCountDownLatch採用了與java.util.concurrent.CountDownLatch相似的介面和用法。
Redisson 分布式鎖和同步器
F. java怎麼實現redis分布式鎖
一基咐、使用分布式鎖要滿足的幾個條件:
系統是一個分布式系統(關鍵是分布式,單機的可以使用ReentrantLock或者synchronized代碼塊來實現)
共享資源(各個系統訪問同一個資源,資禪鋒槐源的載體可能是傳統關系型數據賀友庫或者NoSQL)
同步訪問(即有很多個進程同事訪問同一個共享資源。沒有同步訪問,誰管你資源競爭不競爭)
二、應用的場景例子
管理後台的部署架構(多台tomcat伺服器+redis【多台tomcat伺服器訪問一台redis】+mysql【多台tomcat伺服器訪問一台伺服器上的mysql】)就滿足使用分布式鎖的條件。多台伺服器要訪問redis全局緩存的資源,如果不使用分布式鎖就會出現問題。 看如下偽代碼:
long N=0L;
//N從redis獲取值
if(N<5){
N++;
//N寫回redis
}
復制代碼
G. Java 集群鎖如何實現
1. 用資料庫,在資料庫建一張表,需要鎖的節點都可以嘗試用 select * from Lock where id=xx for update. 這個時候只有一個節點能拿到結果,其它的都會等待,就能實現一個簡單的悲觀鎖。
2. 用 Zookeeper 來做分布式鎖,具體可以搜一下。
3. 自己實現,搞個節點來做這個事情,所有要獲取鎖的都走 RPC 調用來請求鎖,用完以後記得釋放,不然其他的節點就會掛那裡。
H. java trylock能實現分布式鎖嗎
一、zookeeper
1、實現原理:
基於zookeeper瞬時有序節點實現的分布式鎖,其主要邏輯如下(該圖來自於IBM網站)。大致思想即為:每個客戶端對某個功能加鎖時,在zookeeper上的與該功能對應的指定節點的目錄下,生成一個唯一的瞬時有序節點。判斷是否獲取鎖的方式很簡單,只需要判斷有序節點中序號最小的一個。當釋放鎖的時候,只需將這個瞬時節點刪除即可。同時,其可以避免服務宕機導致的鎖無法釋放,而產生的死鎖問題。
二、memcached分布式鎖
1、實現原理:
memcached帶有add函數,利用add函數的特性即可實現分布式鎖。add和set的區別在於:如果多線程並發set,則每個set都會成功,但最後存儲的值以最後的set的線程為准。而add的話則相反,add會添加第一個到達的值,並返回true,後續的添加則都會返回false。利用該點即可很輕松地實現分布式鎖。
三、redis分布式鎖
redis分布式鎖即可以結合zk分布式鎖鎖高度安全和memcached並發場景下效率很好的優點,可以利用jedis客戶端實現
I. 高並發沒鎖可不行,三種分布式鎖詳解
Java中的鎖主要包括synchronized鎖和JUC包中的鎖,這些鎖都是針對單個JVM實例上的鎖,對於分布式環境如果我們需要加鎖就顯得無能為力。在單個JVM實例上,鎖的競爭者通常是一些不同的線程,而在分布式環境中,鎖的競爭者通常是一些不同的線程或者進程。如何實現在分布式環境中對一個對象進行加鎖呢?答案就是分布式鎖。
目前分布式鎖的實現方案主要包括三種:
基於資料庫實現分布式鎖主要是利用資料庫的唯一索引來實現,唯一索引天然具有排他性,這剛好符合我們對鎖的要求:同一時刻只能允許一個競爭者獲取鎖。加鎖時我們在資料庫中插入一條鎖記錄,利用業務id進行防重。當第一個競爭者加鎖成功後,第二個競爭者再來加鎖就會拋出唯一索引沖突,如果拋出這個異常,我們就判定當前競爭者加鎖失敗。防重業務id需要我們自己來定義,例如我們的鎖對象是一個方法,則我們的業務防重id就是這個方法的名字,如果鎖定的對象是一個類,則業務防重id就是這個類名。
基於緩存實現分布式鎖:理論上來說使用緩存來實現分布式鎖的效率最高,加鎖速度最快,因為Redis幾乎都是純內存操作,而基於資料庫的方案和基於Zookeeper的方案都會涉及到磁碟文件IO,效率相對低下。一般使用Redis來實現分布式鎖都是利用Redis的 SETNX key value 這個命令,只有當key不存在時才會執行成功,如果key已經存在則命令執行失敗。
基於Zookeeper:Zookeeper一般用作配置中心,其實現分布式鎖的原神如理和遲備Redis類似,我們在Zookeeper中創建瞬時節點,利用節點不能重復創建的特性來保證排他性。
在實現分布式鎖的時候我們需要考慮一些問題,例如:分布式鎖是否可重入,分布式鎖的釋放時機,分布式鎖服務端是否有單點問題等。
上面已經分析了基於資料庫實現分布式鎖的基本原理:通過唯一索引保持排他性,加鎖時插入一條記錄,解鎖是刪除這條記錄。下面我們就簡要實現一下基於資料庫的分布式鎖。
id欄位是資料庫的自增id,unique_mutex欄位就是我們的防重id,也就是加鎖的對象,此對象唯一。在這張表上我們加了一個唯一索引,保證unique_mutex唯一性。holder_id代表競爭到鎖的持有者id。
如果當前sql執行成功代表加鎖成功,如果拋出唯一索引異常(DuplicatedKeyException)則代表加鎖失敗,當前鎖已經被其他競爭者獲取。
解鎖很簡單,直接刪除此條記錄即可。
是否可重入 :就以上的方案來說,我們實現的分布式鎖是不可重碼瞎毀入的,即是是同一個競爭者,在獲取鎖後未釋放鎖之前再來加鎖,一樣會加鎖失敗,因此是不可重入的。解決不可重入問題也很簡單:加鎖時判斷記錄中是否存在unique_mutex的記錄,如果存在且holder_id和當前競爭者id相同,則加鎖成功。這樣就可以解決不可重入問題。
鎖釋放時機 :設想如果一個競爭者獲取鎖時候,進程掛了,此時distributed_lock表中的這條記錄就會一直存在,其他競爭者無法加鎖。為了解決這個問題,每次加鎖之前我們先判斷已經存在的記錄的創建時間和當前系統時間之間的差是否已經超過超時時間,如果已經超過則先刪除這條記錄,再插入新的記錄。另外在解鎖時,必須是鎖的持有者來解鎖,其他競爭者無法解鎖。這點可以通過holder_id欄位來判定。
資料庫單點問題 :單個資料庫容易產生單點問題:如果資料庫掛了,我們的鎖服務就掛了。對於這個問題,可以考慮實現資料庫的高可用方案,例如MySQL的MHA高可用解決方案。
使用Jedis來和Redis通信。
可以看到,我們加鎖就一行代碼:
jedis.set(String key, String value, String nxxx, String expx, int time);
這個set()方法一共五個形參:
第一個為key,我們使用key來當鎖,因為key是唯一的。
第二個為value,這里寫的是鎖競爭者的id,在解鎖時,我們需要判斷當前解鎖的競爭者id是否為鎖持有者。
第三個為nxxx,這個參數我們填的是NX,意思是SET IF NOT EXIST,即當key不存在時,我們進行set操作;若key已經存在,則不做任何操作。
第四個為expx,這個參數我們傳的是PX,意思是我們要給這個key加一個過期時間的設置,具體時間由第五個參數決定;
第五個參數為time,與第四個參數相呼應,代表key的過期時間。
總的來說,執行上面的set()方法就只會導致兩種結果:1.當前沒有鎖(key不存在),那麼久進行加鎖操作,並對鎖設置一個有效期,同時value表示加鎖的客戶端。2.已經有鎖存在,不做任何操作。
上述解鎖請求中, SET_IF_NOT_EXIST (不存在則執行)保證了加鎖請求的排他性,緩存超時機制保證了即使一個競爭者加鎖之後掛了,也不會產生死鎖問題:超時之後其他競爭者依然可以獲取鎖。通過設置value為競爭者的id,保證了只有鎖的持有者才能來解鎖,否則任何競爭者都能解鎖,那豈不是亂套了。
解鎖的步驟:
注意到這里解鎖其實是分為2個步驟,涉及到解鎖操作的一個原子性操作問題。這也是為什麼我們解鎖的時候用Lua腳本來實現,因為Lua腳本可以保證操作的原子性。那麼這里為什麼需要保證這兩個步驟的操作是原子操作呢?
設想:假設當前鎖的持有者是競爭者1,競爭者1來解鎖,成功執行第1步,判斷自己就是鎖持有者,這是還未執行第2步。這是鎖過期了,然後競爭者2對這個key進行了加鎖。加鎖完成後,競爭者1又來執行第2步,此時錯誤產生了:競爭者1解鎖了不屬於自己持有的鎖。可能會有人問為什麼競爭者1執行完第1步之後突然停止了呢?這個問題其實很好回答,例如競爭者1所在的JVM發生了GC停頓,導致競爭者1的線程停頓。這樣的情況發生的概率很低,但是請記住即使只有萬分之一的概率,在線上環境中完全可能發生。因此必須保證這兩個步驟的操作是原子操作。
是否可重入 :以上實現的鎖是不可重入的,如果需要實現可重入,在 SET_IF_NOT_EXIST 之後,再判斷key對應的value是否為當前競爭者id,如果是返回加鎖成功,否則失敗。
鎖釋放時機 :加鎖時我們設置了key的超時,當超時後,如果還未解鎖,則自動刪除key達到解鎖的目的。如果一個競爭者獲取鎖之後掛了,我們的鎖服務最多也就在超時時間的這段時間之內不可用。
Redis單點問題 :如果需要保證鎖服務的高可用,可以對Redis做高可用方案:Redis集群+主從切換。目前都有比較成熟的解決方案。
利用Zookeeper創建臨時有序節點來實現分布式鎖:
其基本思想類似於AQS中的等待隊列,將請求排隊處理。其流程圖如下:
解決不可重入 :客戶端加鎖時將主機和線程信息寫入鎖中,下一次再來加鎖時直接和序列最小的節點對比,如果相同,則加鎖成功,鎖重入。
鎖釋放時機 :由於我們創建的節點是順序臨時節點,當客戶端獲取鎖成功之後突然session會話斷開,ZK會自動刪除這個臨時節點。
單點問題 :ZK是集群部署的,主要一半以上的機器存活,就可以保證服務可用性。
Zookeeper第三方客戶端curator中已經實現了基於Zookeeper的分布式鎖。利用curator加鎖和解鎖的代碼如下:
J. java怎麼實現redis分布式鎖
Redis有一系列的命令,特點是以NX結尾,NX是Not eXists的縮寫,如SETNX命令就應該理解為:SET if Not eXists。這系列的命令非常有用,這里講使用SETNX來實現分布式鎖。
用SETNX實現分布式鎖
利用SETNX非常簡單地實現分布式鎖。例如:某客戶端要獲得一個名字foo的鎖,客戶端使用下面的命令進行獲取:
SETNX lock.foo <current Unix time + lock timeout + 1>
如返回1,則該客戶端獲得鎖,把lock.foo的鍵值設置為時間值表示該鍵已被鎖定,該客戶端最後可以通過DEL lock.foo來釋放該鎖。
如返回0,表明該鎖已被其他客戶端取得,這時我們可以先返回或進行重試等對方完成或等待鎖超時。
解決死鎖
上面的鎖定邏輯有一個問題:如果一個持有鎖的客戶端失敗或崩潰了不能釋放鎖,該怎麼解決?我們可以通過鎖的鍵對應的時間戳來判斷這種情況是否發生了,如果當前的時間已經大於lock.foo的值,說明該鎖已失效,可以被重新使用。
發生這種情況時,可不能簡單的通過DEL來刪除鎖,然後再SETNX一次,當多個客戶端檢測到鎖超時後都會嘗試去釋放它,這里就可能出現一個競態條件,讓我們模擬一下這個場景:
C0操作超時了,但它還持有著鎖,C1和C2讀取lock.foo檢查時間戳,先後發現超時了。
C1 發送DEL lock.foo
C1 發送SETNX lock.foo 並且成功了。
C2 發送DEL lock.foo
C2 發送SETNX lock.foo 並且成功了。
這樣一來,C1,C2都拿到了鎖!問題大了!
幸好這種問題是可以避免D,讓我們來看看C3這個客戶端是怎樣做的:
C3發送SETNX lock.foo 想要獲得鎖,由於C0還持有鎖,所以Redis返回給C3一個0
C3發送GET lock.foo 以檢查鎖是否超時了,如果沒超時,則等待或重試。
反之,如果已超時,C3通過下面的操作來嘗試獲得鎖:
GETSET lock.foo <current Unix time + lock timeout + 1>
通過GETSET,C3拿到的時間戳如果仍然是超時的,那就說明,C3如願以償拿到鎖了。
如果在C3之前,有個叫C4的客戶端比C3快一步執行了上面的操作,那麼C3拿到的時間戳是個未超時的值,這時,C3沒有如期獲得鎖,需要再次等待或重試。留意一下,盡管C3沒拿到鎖,但它改寫了C4設置的鎖的超時值,不過這一點非常微小的誤差帶來的影響可以忽略不計。
注意:為了讓分布式鎖的演算法更穩鍵些,持有鎖的客戶端在解鎖之前應該再檢查一次自己的鎖是否已經超時,再去做DEL操作,因為可能客戶端因為某個耗時的操作而掛起,操作完的時候鎖因為超時已經被別人獲得,這時就不必解鎖了。
示例偽代碼
根據上面的代碼,我寫了一小段Fake代碼來描述使用分布式鎖的全過程:
# get lock
lock = 0
while lock != 1:
timestamp = current Unix time + lock timeout + 1
lock = SETNX lock.foo timestamp
if lock == 1 or (now() > (GET lock.foo) and now() > (GETSET lock.foo timestamp)):
break;
else:
sleep(10ms)
# do your job
do_job()
# release
if now() < GET lock.foo:
DEL lock.foo
是的,要想這段邏輯可以重用,使用python的你馬上就想到了Decorator,而用Java的你是不是也想到了那誰?AOP + annotation?行,怎樣舒服怎樣用吧,別重復代碼就行。