一. 對於分布式的應用,一定程度上會增加處理的速度。但是也會帶來一些分布式上的麻煩,比如有個需求:後台程式部署在多台伺服器上,client向該後台程式傳送引數為 使用者賬號和 賬號型別 的rpc請求,後台程式需要返回該賬號對應的身份資訊(邏輯很簡單,先判斷庫中有沒有該賬號資訊,有就返回,沒有就新生成乙個新的身份資訊 返回)。設想如果多個client 同時傳送多個一樣的賬號和賬號型別 到後台程式,由於同時查庫沒有該賬號資訊,這樣豈不是要新生成多個不同的身份資訊。解決該問題也許可以不通過分布式鎖(專案中不是用的分布式鎖,但不屬於本文的內容的範疇,故省略),下面介紹用redis 實現分布式鎖。
二 .不安全的做法
使用jedis.setnx(key,value) . 偽**如下
if(jedis.setnx(key,value) ==1 )finally
使用該做法有3點不安全的隱患
隱患1: setnx 和 expire 不是同步的,如果剛setnx完成,還沒來得及 expire key ,就宕機了,那該鎖就 是「長生不老的」
隱患2: del 可能是刪除 「別人」設定的鎖,由於自己執行任務時間的比較長,設定的鎖因為超時已經過期了。這個時候別的執行緒已經拿到該鎖,那刪除的時候,鎖的別的執行緒設定的。
隱患3: 可能出現 多個執行緒並存的情況。如果 thread1 拿到鎖,設定的鎖已經過期了,但是還沒有執行完成 。 thread2 訪問拿到了鎖 。這樣thread1 和 thread2 同時存在。
上述隱患解決方案:
隱患1解決方案 : 如果向要setnx 和expire 是原子操作,可以使用jedis.set(key,value,n***,expx,time).)(redis2.6以上) 。具體的用法可以自己查詢baidu/google
隱患2解決方案 : 將set(key,value.n***,expx,time)中的value 設定為 thread.currentthread.getid().即根據該value判斷鎖是否是自己設定的。如果是自己設定才刪除
偽**:
if(thead.currentthread.getid().equal(jedis.get(key)))
return isgetlock;
}catch(exception e )finally }}
private void dellock(string threadid)catch(exception e)finally }}
//守護執行緒 獲取儲存的value值
private string getlockvalue()catch(exception e)finally }}
//守護執行緒 獲取key的剩餘時間
private long getttl()catch(exception e)finally }}
//守護執行緒發現快要過期,延長鎖的過期時間
private void spanexpiretime(int seconde)catch(exception e)finally }}
private class subthread implements runnableelse
}}catch(exception e )}}
private class daemonthred implements runnable
public void run() elsecatch(exception e)}}
}}四 . 後續
4.1 該**不是正式線上的**,只是寫的乙個demo,由好的實現方式或者問題,歡迎吐槽。
4.2 大家可能會問,守護執行緒 的判斷剩餘時間、獲取value值 和 設定延長過期時間 不是原子操作,會不會存在和隱患2中 判斷是否自己設定的鎖 和後續 刪除鎖一樣的問題 --不是原子操作的問題 ? 我這裡設定的鎖 過期時間是20s,還有2s的時候我就延長過期時間,也就是 守護執行緒 有足夠的時間 執行剩餘時間判斷 和 執行延長過期時間的操作 。一般來說,如果使用分布式鎖, thread訪問很快。不會等到後台守護執行緒去延長過期時間 。如果執行緒 很多時候 get 鎖 do somthings 需要花費很長時間。這個時候需要考慮下是不是需要分布式鎖,或者架構上的設計是否有改善的地方。
利用redis實現分布式鎖
因為redis是單執行緒程式,可以天然的保證執行緒安全,只要我們的命令是單條命令,就可以保證操作的安全性,而redis中給我們提供了setnx key value命令,setnx命令的作用就是當我們的redis中沒有這個key的鍵值隊時,就會建立這個鍵值隊的值,如果已經有了這個key就不作操作 所以...
利用Redis實現分布式鎖
實現 redis 完成分布式鎖 所用到的指令 思路 1 在執行具體的買票業務之前先通過 setnx 的指令去獲取其返回值,如果設定成功 返回值為1 說明獲取到了鎖,沒有設定成功 返回值為0 則說明沒有獲取到鎖,繼續迴圈執行 2.搶到鎖的執行緒先給key 設定過期時間,這一步主要是為了避免死鎖問題 在...
golang 利用redis實現分布式鎖
redis知識準備 redis setnx命令 setnx 如果key不存在則建立,並返回true 如果key存在則不操作,並返回false redis ttl命令 如果對key設定了過期時間,則ttl命令返回key到過期時所剩餘的時間 如果key沒有設定過期時間,則ttl命令返回 1 獲取鎖的過期...