應用場景
當多個機器(多個程序)會對同一條資料進行修改時,並且要求這個修改是原子性的。這裡有兩個限定:(1)多個程序之間的競爭,意味著jdk自帶的鎖失效;(2)原子性修改,意味著資料是有狀態的,修改前後有依賴。
基於redis的實踐
鎖的實現
鎖的key為目標資料的唯一鍵,value為鎖的期望超時時間點;
首先進行一次setnx命令,嘗試獲取鎖,如果獲取成功,則設定鎖的最終超時時間(以防在當前程序獲取鎖後奔潰導致鎖無法釋放);如果獲取鎖失敗,則檢查當前的鎖是否超時,如果發現沒有超時,則獲取鎖失敗;如果發現鎖已經超時(即鎖的超時時間小於等於當前時間),則再次嘗試獲取鎖,取到後判斷下當前的超時時間和之前的超時時間是否相等,如果相等則說明當前的客戶端是排隊等待的執行緒裡的第乙個嘗試獲取鎖的,讓它獲取成功即可。
public
class
redisdistributionlock
/** * 加鎖,同時設定鎖超時時間
* @param key 分布式鎖的key
* @param expiretime 單位是ms
* @return
*/public
boolean
lock
(string key,
long expiretime)
], expiretime:[{}]"
,key,expiretime)
;long now = instant.
now().
toepochmilli()
;long lockexpiretime = now + expiretime;
//setnx
boolean executeresult = redistemplateforgeneralize.
opsforvalue()
.setifabsent
(key,string.
valueof
(lockexpiretime));
logger.
debug
("redis lock debug, setnx. key:[{}], expiretime:[{}], executeresult:[{}]"
, key, expiretime,executeresult)
;//取鎖成功,為key設定expire
if(executeresult == success)
//沒有取到鎖,繼續流程
else
], oldexpiretime:[{}]"
,key,oldexpiretime)
;//鎖過期時間小於當前時間,鎖已經超時,重新取鎖
if(oldexpiretime <= now)
], oldexpiretime:[{}], now:[{}]"
, key, oldexpiretime, now)
; string valuefromredis2 = redistemplateforgeneralize.
opsforvalue()
.getandset
(key, string.
valueof
(lockexpiretime));
long currentexpiretime = long.
parselong
(valuefromredis2)
;//判斷currentexpiretime與oldexpiretime是否相等
if(currentexpiretime == oldexpiretime)
], currentexpiretime:[{}], oldexpiretime:[{}], lockexpiretime:[{}]"
, key, currentexpiretime, oldexpiretime, lockexpiretime)
; redistemplateforgeneralize.
expire
(key, finaldefaultttlwithkey, timeunit.seconds)
;return
true;}
else}}
else
]", key)
;return
false;}
}return
false;}
private object getkeywithretry
(string key,
int retrytimes)
catch
(exception e)}}
return null;
}/**
* 解鎖
* @param key
* @return
*/public
boolean
unlock
(string key)
].",key)
; redistemplateforgeneralize.
delete
(key)
;return success;
}}
自定義註解使用分布式鎖
@retention
(retentionpolicy.runtime)
@target
(elementtype.method)
public @inte***ce
redislockannoation
;/**
* 是否阻塞鎖;
* 1. true:獲取不到鎖,阻塞一定時間;
* 2. false:獲取不到鎖,立即返回
*/boolean
isspin()
default
true
;/**
* 超時時間
*/intexpiretime()
default
10000
;/**
* 等待時間
*/intwaittime()
default50;
/** * 獲取不到鎖的等待時間
*/intretrytimes()
default20;
}
實現分布式鎖的邏輯
@component
@aspect
public
class
redislockadvice
string rediskey = stringutils.
removeend
(temp.
tostring()
,"_");
//執行分布式鎖的邏輯
if(redislockannoation.
isspin()
), lockretrytime:{}"
, rediskey, lockretrytime)
;throw exceptionutil.
geneexception
(commonexceptionenum.system_error);}
threadutil.
holdxms
(redislockannoation.
waittime()
);}return pjp.
proceed()
;}finally
}else
", rediskey)
;throw exceptionutil.
geneexception
(commonexceptionenum.system_error);}
return pjp.
proceed()
;}finally}}
}
zookeeper應用場景練習(分布式鎖)
在平常的高併發的程式中,為了保證資料的一致性,因此都會用到鎖,來對當前的執行緒進行鎖定。在單機操作中,很好做到,比如可以採用synchronized lock或者其他的讀寫多來鎖定當前的執行緒。但是在分布式的系統中,就很難做到這一點。因此可以採用zookeeper中節點的特性來滿足這一點。大致實現的...
ZooKeeper的典型應用場景 分布式鎖
在平時的實際專案開發中,可以依賴關係型資料庫固有的排他性來實現不同程序間的互斥。然而,目前絕大數大型分布式系統的效能瓶頸都集中在資料庫操作上,如果上層業務再給資料庫新增一些額外的鎖,例如行鎖等,會讓資料庫更加不堪重負。定義鎖在zookeeper中,通過資料節點來表示乙個鎖,例如 exclusive ...
zookeeper適用場景 分布式鎖實現
在zookeeper應用場景有關於分布式集群配置檔案同步問題的描述,設想一下如果有100臺機器同時對同一臺機器上某個檔案進行修改,如何才能保證文字不會被寫亂,這就是最簡單的分布式鎖,本文介紹利用zk實現分布式鎖。下面是寫鎖的實現步驟 分布式寫鎖 create乙個persistent型別的znode,...