為了保障程序的高可用性牙甫,避免用戶在使用公司產(chǎn)品的時候,突然掛掉訪問不了落午,一般公司的后臺服務(wù),都需要部署多個實例蜕便,這樣就算一個掛了,另外幾個實例還能夠正常的工作忽妒,也就是做了所謂的負(fù)載均衡玩裙。
這樣也就會產(chǎn)生了一個問題,服務(wù)中的定時腳本也會被部署多份段直,當(dāng)指定的時間一到,腳本也會運行多個溶诞,可能會造成一些不必要的計算和麻煩鸯檬。
怎么去保證,同一時刻螺垢,只有一個腳本在運行呢喧务?比較好的,普遍適用的方式枉圃,就是使用分布式鎖功茴。
分布式鎖的使用
假設(shè)你啟動了多個實例A, B, C, D,為了達(dá)到同一時間只有一個實例在運行孽亲,我們需要在redis里面設(shè)置一把鎖:
SETNX keyName = 1
上面的SETNX
命令只有當(dāng)keyName沒有被設(shè)置值的時候坎穿,才會成功,這樣返劲,當(dāng)A, B, C, D在啟動的時候玲昧,先去執(zhí)行上面的命令,成功的那個篮绿,才去執(zhí)行孵延,就可以保證同一時刻只有一個腳本在運行,當(dāng)然程序運行結(jié)束后亲配,需要執(zhí)行:
DEL keyName
來釋放鎖尘应。
死鎖問題
但是這樣也有一個問題惶凝,假設(shè)A設(shè)置了鎖,但是在后續(xù)執(zhí)行的時候犬钢,程序奔潰了梨睁,這樣keyName
的值永遠(yuǎn)為1,B, C, D再也沒有獲取到鎖的機會娜饵,甚至連A自己重新啟動后也無法獲取鎖坡贺。
為了避免這種問題,我們可以為keyName
設(shè)置一個超時時間t
箱舞,這樣即使拿到鎖的程序崩潰了遍坟,超時時間一過,鎖也會自動釋放晴股。
鎖沖突問題
一般情況下愿伴,為了避免單點故障,我們一般會采用redis的主從架構(gòu)电湘,一個redis的master節(jié)點隔节,掛上n個slave從節(jié)點,這樣如果master奔潰了寂呛,會啟用一個slave節(jié)點繼續(xù)服務(wù)怎诫,考慮這樣一種情況:
- A調(diào)用
SETNX
設(shè)置了master節(jié)點上的鎖,然后開始執(zhí)行贷痪。- master節(jié)點在將鎖的信息同步到slave節(jié)點前幻妓,崩潰了
- slave被切換成master繼續(xù)服務(wù),但此時這個節(jié)點沒有了鎖的信息
- B調(diào)用
SETNX
也成功的設(shè)置了鎖(跟A沖突)
這種問題怎么處理呢劫拢?我們需要借助另外的一些master節(jié)點肉津,假設(shè)你有N個redis的master節(jié)點,并且這些節(jié)點之間相互獨立舱沧,不存在同步數(shù)據(jù)的關(guān)系妹沙,然后獲取鎖的操作走下面的過程:
- 獲取當(dāng)前時間
t
- 然后用
t
嘗試在N個master節(jié)點上輪流獲取鎖,只有當(dāng)客戶端在大部分master節(jié)點上獲取到了鎖熟吏,才認(rèn)為這個鎖是有效的距糖,否則就釋放所有的鎖。
延時問題
上面這么干分俯,鎖是不是靠譜肾筐?會不會有兩個客戶端可能同時拿到鎖?答案是有可能的缸剪!
我們來分析一下吗铐,假設(shè)從0時刻開始,客戶端向R1, R2, R3, R4, R5三個master節(jié)點發(fā)送了獲取鎖的請求杏节,假設(shè)獲取了R1, R3, R4三把鎖唬渗,受網(wǎng)絡(luò)影響典阵,拿到鎖消耗的實踐分別為T1, T2, T3:
如果鎖的超時時間設(shè)置為T,三把鎖分別釋放的時間為T11, T22和T33:
注意镊逝,客戶端至少在T2壮啊,才會認(rèn)為自己拿到鎖,所以它會認(rèn)為自己在T22時刻才失去鎖撑蒜,但是在T33時刻歹啼,客戶端A已經(jīng)釋放了R1上的鎖,此時另外的一個客戶端是有可能拿到大部分的鎖成為鎖的擁有者座菠,同樣會造成鎖沖突狸眼。
當(dāng)然解決方法也是有的,就是定義鎖的超時時間為:最先釋放鎖的時間-獲取鎖的總消耗時間浴滴,比如上面的情況拓萌,超時時間就應(yīng)該定義為:T11-T2
時鐘差問題
超時問題解決之后,是不是一定可以保證互斥性了呢升略?也不一定微王,因為各個機器之間還可能存在著時鐘差問題,比如客戶端認(rèn)為的T11時刻品嚣,R1所在的服務(wù)器可能認(rèn)為是T1'炕倘,R3所在的服務(wù)器可能認(rèn)為是T1'':
所以,客戶端以為自己還在持有鎖腰根,但實際情況可能這把鎖已經(jīng)被master節(jié)點釋放掉了激才,這種情況該如何處理呢?
這個的解決方式也很簡單额嘿,我們只需要把過期時間定義為:T1'-T2就可以了,其中T1' = T11 - TT劣挫,TT表示機器之間的時鐘偏差册养,一般這個偏差在幾毫秒左右。
總結(jié)
時鐘偏差問題解決后压固,鎖是不是一定就是安全的了呢球拦?忽略一些可有可無的細(xì)節(jié)問題,目前來看是安全的帐我,這種鎖也是redis官方推薦的坎炼,但是官方也承認(rèn)可能隱藏著一些不可知的因素,具體還是要等待大家的發(fā)現(xiàn)拦键。