這篇文章主要講解了“Zookeeper怎么寫分布式鎖框架”,文中的講解內(nèi)容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“Zookeeper怎么寫分布式鎖框架”吧!
創(chuàng)新互聯(lián)科技有限公司專業(yè)互聯(lián)網(wǎng)基礎(chǔ)服務(wù)商,為您提供西云機房,高防物理服務(wù)器租用,成都IDC機房托管,成都主機托管等互聯(lián)網(wǎng)服務(wù)。
一、核心代碼如下:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
@Component
public class RedisLock {
@Autowired
private StringRedisTemplate template;
@Autowired
private DefaultRedisScript<Long> redisScript;
private static final Long RELEASE_SUCCESS = 1L;
private long timeout = 3000;
public boolean lock(String key, String value) {
//執(zhí)行set命令
Boolean absent = template.opsForValue().setIfAbsent(key, value, timeout, TimeUnit.MILLISECONDS);//1
//其實沒必要判NULL,這里是為了程序的嚴謹而加的邏輯
if (absent == null) {
return false;
}
//是否成功獲取鎖
return true;
}
public boolean unlock(String key, String value) {
//使用Lua腳本:先判斷是否是自己設(shè)置的鎖,再執(zhí)行刪除
Long result = template.execute(redisScript, Arrays.asList(key,value));
//返回最終結(jié)果
return RELEASE_SUCCESS.equals(result);
}
public void setTimeout(long timeout) {
this.timeout = timeout;
}
@Bean
public DefaultRedisScript<Long> defaultRedisScript() {
DefaultRedisScript<Long> defaultRedisScript = new DefaultRedisScript<>();
defaultRedisScript.setResultType(Long.class);
defaultRedisScript.setScriptText("if redis.call('get', KEYS[1]) == KEYS[2] then return redis.call('del', KEYS[1]) else return 0 end");
return defaultRedisScript;
}
}
執(zhí)行上面的setIfAbsent()方法就只會導致兩種結(jié)果:
1. 當前沒有鎖(key不存在),那么就進行加鎖操作,并對鎖設(shè)置個有效期,同時value表示加鎖的客戶端。
2. 已有鎖存在,不做任何操作。
在任意時刻,該代碼都能保證只有一個客戶端能持有鎖,并且每一個分布式鎖都加了過期時間,保證不會出現(xiàn)死鎖,容錯性暫時不考慮的話,加鎖和解鎖通過key保證了對多個客戶端而言都是同一把鎖,value的作用則是保證對同一把鎖的加鎖和解鎖操作都是同一個客戶端。
二、為什么上述方案不夠好
為了理解我們想要提高的到底是什么,我們先看下當前大多數(shù)基于Redis的分布式鎖三方庫的現(xiàn)狀。 用Redis來實現(xiàn)分布式鎖最簡單的方式就是在實例里創(chuàng)建一個鍵值,創(chuàng)建出來的鍵值一般都是有一個超時時間的(這個是Redis自帶的超時特性),所以每個鎖最終都會釋放(參見前文屬性2)。而當一個客戶端想要釋放鎖時,它只需要刪除這個鍵值即可。 表面來看,這個方法似乎很管用,但是這里存在一個問題:在我們的系統(tǒng)架構(gòu)里存在一個單點故障,如果Redis的master節(jié)點宕機了怎么辦呢?有人可能會說:加一個slave節(jié)點!在master宕機時用slave就行了!但是其實這個方案明顯是不可行的,因為這種方案無法保證第1個安全互斥屬性,因為Redis的復制是異步的。 總的來說,這個方案里有一個明顯的競爭條件(race condition),舉例來說:
客戶端A在master節(jié)點拿到了鎖。
master節(jié)點在把A創(chuàng)建的key寫入slave之前宕機了。
slave變成了master節(jié)點
B也得到了和A還持有的相同的鎖(因為原來的slave里還沒有A持有鎖的信息)
當然,在某些特殊場景下,前面提到的這個方案則完全沒有問題,比如在宕機期間,多個客戶端允許同時都持有鎖,如果你可以容忍這個問題的話,那用這個基于復制的方案就完全沒有問題,否則的話我還是建議你對上述方案進行改進。比如,考慮使用Redlock算法。
三、Redlock算法
在分布式版本的算法里我們假設(shè)我們有N個Redis master節(jié)點,這些節(jié)點都是完全獨立的,我們不用任何復制或者其他隱含的分布式協(xié)調(diào)算法。我們已經(jīng)描述了如何在單節(jié)點環(huán)境下安全地獲取和釋放鎖。因此我們理所當然地應(yīng)當用這個方法在每個單節(jié)點里來獲取和釋放鎖。在我們的例子里面我們把N設(shè)成5,這個數(shù)字是一個相對比較合理的數(shù)值,因此我們需要在不同的計算機或者虛擬機上運行5個master節(jié)點來保證他們大多數(shù)情況下都不會同時宕機。一個客戶端需要做如下操作來獲取鎖:
獲取當前時間(單位是毫秒)。
輪流用相同的key和隨機值在N個節(jié)點上請求鎖,在這一步里,客戶端在每個master上請求鎖時,會有一個和總的鎖釋放時間相比小的多的超時時間。比如如果鎖自動釋放時間是10秒鐘,那每個節(jié)點鎖請求的超時時間可能是5-50毫秒的范圍,這個可以防止一個客戶端在某個宕掉的master節(jié)點上阻塞過長時間,如果一個master節(jié)點不可用了,我們應(yīng)該盡快嘗試下一個master節(jié)點。
客戶端計算第二步中獲取鎖所花的時間,只有當客戶端在大多數(shù)master節(jié)點上成功獲取了鎖(在這里是3個),而且總共消耗的時間不超過鎖釋放時間,這個鎖就認為是獲取成功了。
如果鎖獲取成功了,那現(xiàn)在鎖自動釋放時間就是最初的鎖釋放時間減去之前獲取鎖所消耗的時間。
如果鎖獲取失敗了,不管是因為獲取成功的鎖不超過一半(N/2+1)還是因為總消耗時間超過了鎖釋放時間,客戶端都會到每個master節(jié)點上釋放鎖,即便是那些他認為沒有獲取成功的鎖。
感謝各位的閱讀,以上就是“Zookeeper怎么寫分布式鎖框架”的內(nèi)容了,經(jīng)過本文的學習后,相信大家對Zookeeper怎么寫分布式鎖框架這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!
網(wǎng)站名稱:Zookeeper怎么寫分布式鎖框架
當前鏈接:http://muchs.cn/article24/jpjcje.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供面包屑導航、用戶體驗、網(wǎng)站排名、云服務(wù)器、電子商務(wù)、網(wǎng)站策劃
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)