亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 數據庫 > Redis > 正文

如何實現基于 Redis 的分布式鎖

2020-03-22 18:06:49
字體:
來源:轉載
供稿:網友
前言

分布式鎖在分布式應用中應用廣泛,想要搞懂一個新事物首先得了解它的由來,這樣才能更加的理解甚至可以舉一反三。

首先談到分布式鎖自然也就聯想到分布式應用。

在我們將應用拆分為分布式應用之前的單機系統中,對一些并發場景讀取公共資源時如扣庫存,賣車票之類的需求可以簡單的使用同步或者是加鎖就可以實現。

但是應用分布式了之后系統由以前的單進程多線程的程序變為了多進程多線程,這時使用以上的解決方案明顯就不夠了。

因此業界常用的解決方案通常是借助于一個第三方組件并利用它自身的排他性來達到多進程的互斥。如:

基于 DB 的唯一索引。

基于 ZK 的臨時有序節點。

基于 Redis 的 NX EX 參數。

這里主要基于 Redis 進行討論。

實現

既然是選用了 Redis,那么它就得具有排他性才行。同時它最好也有鎖的一些基本特性:

高性能(加、解鎖時高性能)

可以使用阻塞鎖與非阻塞鎖。

不能出現死鎖。

可用性(不能出現節點 down 掉后加鎖失敗)。

這里利用 Redis set key 時的一個 NX 參數可以保證在這個 key 不存在的情況下寫入成功。并且再加上 EX 參數可以讓該 key 在超時之后自動刪除。

所以利用以上兩個特性可以保證在同一時刻只會有一個進程獲得鎖,并且不會出現死鎖(最壞的情況就是超時自動刪除 key)。

加鎖

實現代碼如下:

 private html' target='_blank'>static final String SET_IF_NOT_EXIST = NX  private static final String SET_WITH_EXPIRE_TIME = PX  public boolean tryLock(String key, String request) { String result = this.jedis.set(LOCK_PREFIX + key, request, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, 10 * TIME); if (LOCK_MSG.equals(result)){ return true ; }else { return false ; }

注意這里使用的 jedis 的

String set(String key, String value, String nxxx, String expx, long time);

api。

該命令可以保證 NX EX 的原子性。

一定不要把兩個命令(NX EX)分開執行,如果在 NX 之后程序出現問題就有可能產生死鎖。

阻塞鎖

同時也可以實現一個阻塞鎖:

 //一直阻塞 public void lock(String key, String request) throws InterruptedException { for (;;){ String result = this.jedis.set(LOCK_PREFIX + key, request, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, 10 * TIME); if (LOCK_MSG.equals(result)){ break ; //防止一直消耗 CPU  Thread.sleep(DEFAULT_SLEEP_TIME) ; //自定義阻塞時間 public boolean lock(String key, String request,int blockTime) throws InterruptedException { while (blockTime = 0){ String result = this.jedis.set(LOCK_PREFIX + key, request, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, 10 * TIME); if (LOCK_MSG.equals(result)){ return true ; blockTime -= DEFAULT_SLEEP_TIME ; Thread.sleep(DEFAULT_SLEEP_TIME) ; return false ; }
解鎖

解鎖也很簡單,其實就是把這個 key 刪掉就萬事大吉了,比如使用 del key 命令。

但現實往往沒有那么 easy。

如果進程 A 獲取了鎖設置了超時時間,但是由于執行周期較長導致到了超時時間之后鎖就自動釋放了。這時進程 B 獲取了該鎖執行很快就釋放鎖。這樣就會出現進程 B 將進程 A 的鎖釋放了。

所以最好的方式是在每次解鎖時都需要判斷鎖是否是自己的。

這時就需要結合加鎖機制一起實現了。

加鎖時需要傳遞一個參數,將該參數作為這個 key 的 value,這樣每次解鎖時判斷 value 是否相等即可。

所以解鎖代碼就不能是簡單的 del了。

 public boolean unlock(String key,String request){ //lua script String script = if redis.call( get , KEYS[1]) == ARGV[1] then return redis.call( del , KEYS[1]) else return 0 end  Object result = null ; if (jedis instanceof Jedis){ result = ((Jedis)this.jedis).eval(script, Collections.singletonList(LOCK_PREFIX + key), Collections.singletonList(request)); }else if (jedis instanceof JedisCluster){ result = ((JedisCluster)this.jedis).eval(script, Collections.singletonList(LOCK_PREFIX + key), Collections.singletonList(request)); }else { //throw new RuntimeException( instance is error ) ; return false ; if (UNLOCK_MSG.equals(result)){ return true ; }else { return false ; }

這里使用了一個 lua 腳本來判斷 value 是否相等,相等才執行 del 命令。

使用 lua 也可以保證這里兩個操作的原子性。

因此上文提到的四個基本特性也能滿足了:

使用 Redis 可以保證性能。

阻塞鎖與非阻塞鎖見上文。

利用超時機制解決了死鎖。

Redis 支持集群部署提高了可用性。

使用

我自己有擼了一個完整的實現,并且已經用于了生產,有興趣的朋友可以開箱使用:

maven 依賴:

 dependency  groupId top.crossoverjie.opensource /groupId  artifactId distributed-redis-lock /artifactId  version 1.0.0 /version  /dependency 

配置 bean :

@Configurationpublic class RedisLockConfig { @Bean public RedisLock build(){ RedisLock redisLock = new RedisLock() ; HostAndPort hostAndPort = new HostAndPort( 127.0.0.1 ,7000) ; JedisCluster jedisCluster = new JedisCluster(hostAndPort) ; // Jedis 或 JedisCluster 都可以 redisLock.setJedisCluster(jedisCluster) ; return redisLock ;}

使用:

 @Autowired private RedisLock redisLock ; public void use() { String key = key  String request = UUID.randomUUID().toString(); try { boolean locktest = redisLock.tryLock(key, request); if (!locktest) { System.out.println( locked error  return;
}

使用很簡單。這里主要是想利用 Spring 來幫我們管理 RedisLock 這個單例的 bean,所以在釋放鎖的時候需要手動(因為整個上下文只有一個 RedisLock 實例)的傳入 key 以及 request(api 看起來不是特別優雅)。

也可以在每次使用鎖的時候 new 一個 RedisLock 傳入 key 以及 request,這樣倒是在解鎖時很方便。但是需要自行管理 RedisLock 的實例。各有優劣吧。

單測

在做這個項目的時候讓我不得不想提一下單測。

因為這個應用是強依賴于第三方組件的(Redis),但是在單測中我們需要排除掉這種依賴。比如其他伙伴 fork 了該項目想在本地跑一遍單測,結果運行不起來:

有可能是 Redis 的 ip、端口和單測里的不一致。

Redis 自身可能也有問題。

也有可能是該同學的環境中并沒有 Redis。

所以最好是要把這些外部不穩定的因素排除掉,單測只測我們寫好的代碼。

于是就可以引入單測利器 Mock 了。

它的想法很簡答,就是要把你所依賴的外部資源統統屏蔽掉。如:數據庫、外部接口、外部文件等等。

使用方式也挺簡單,可以參考該項目的單測:

 @Test public void tryLock() throws Exception { String key = test  String request = UUID.randomUUID().toString(); Mockito.when(jedisCluster.set(Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyLong())).thenReturn( OK  boolean locktest = redisLock.tryLock(key, request); System.out.println( locktest= + locktest); Assert.assertTrue(locktest); //check Mockito.verify(jedisCluster).set(Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyLong()); }

這里只是簡單演示下,可以的話下次仔細分析分析。

它的原理其實也挺簡單,debug 的話可以很直接的看出來:

這里我們所依賴的 JedisCluster 其實是一個 cglib 代理對象。所以也不難想到它是如何工作的。

比如這里我們需要用到 JedisCluster 的 set 函數并需要它的返回值。

Mock 就將該對象代理了,并在實際執行 set 方法后給你返回了一個你自定義的值。

這樣我們就可以隨心所欲的測試了,完全把外部依賴所屏蔽了。

總結

至此一個基于 Redis 的分布式鎖完成,但是依然有些問題。

如在 key 超時之后業務并沒有執行完畢但卻自動釋放鎖了,這樣就會導致并發問題。

就算 Redis 是集群部署的,如果每個節點都只是 master 沒有 slave,那么 master 宕機時該節點上的所有 key 在那一時刻都相當于是釋放鎖了,這樣也會出現并發問題。就算是有 slave 節點,但如果在數據同步到 salve 之前 master 宕機也是會出現上面的問題。

感興趣的朋友還可以參考 Redisson 的實現。

以上就是如何實現基于 Redis 的分布式鎖的詳細內容,PHP教程

鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播更多信息之目的,如作者信息標記有誤,請第一時間聯系我們修改或刪除,多謝。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
成人免费网站在线观看| 91精品国产777在线观看| 国模视频一区二区三区| 久久精品国产成人| 91成人天堂久久成人| 欧美有码在线视频| 成人乱色短篇合集| 77777少妇光屁股久久一区| 97人人模人人爽人人喊中文字| 亚洲免费一级电影| 欧美做爰性生交视频| 欧美国产精品日韩| 国语自产精品视频在线看抢先版图片| www高清在线视频日韩欧美| 欧美xxxx综合视频| 国产成人精品午夜| 夜夜嗨av一区二区三区四区| 狠狠操狠狠色综合网| 国产精品网红直播| 高清一区二区三区四区五区| 亚洲精品久久久久中文字幕欢迎你| 国产精品香蕉国产| 亚州国产精品久久久| 亚洲自拍小视频免费观看| 国产精品日韩在线观看| 中文字幕日韩欧美在线视频| 欧美一级视频一区二区| 日本欧美一二三区| 国产精品久久久久999| 日韩大陆欧美高清视频区| 热99在线视频| 国产在线观看一区二区三区| 久久久国产精品亚洲一区| 91爱视频在线| 视频在线观看99| 国产成+人+综合+亚洲欧洲| 欧美激情精品久久久久| 日韩女优人人人人射在线视频| 欧美亚洲视频在线看网址| 一二美女精品欧洲| 在线日韩欧美视频| 亚洲天堂视频在线观看| 国产精品高清免费在线观看| 久久久久久久久久久免费精品| 国产精品男人的天堂| 亚洲国产另类久久精品| 亚洲福利在线观看| 亚洲黄页视频免费观看| 成人日韩在线电影| 社区色欧美激情 | 97激碰免费视频| 97香蕉久久超级碰碰高清版| 国产精品美女在线| 欧美成人中文字幕在线| 国产999精品视频| 亚洲欧美在线第一页| 成人综合网网址| 亚洲日本中文字幕| 亚洲2020天天堂在线观看| 亚洲曰本av电影| 欧美色道久久88综合亚洲精品| 97色在线播放视频| 欧美日韩成人在线观看| 久久夜色精品亚洲噜噜国产mv| 亚洲欧美中文日韩在线v日本| 伊人亚洲福利一区二区三区| 亚洲跨种族黑人xxx| 国产精品情侣自拍| 精品偷拍一区二区三区在线看| 爱福利视频一区| 欧洲美女7788成人免费视频| 国产在线精品播放| 亚洲视频一区二区三区| 亚洲国产欧美久久| 国产欧亚日韩视频| 久久亚洲精品小早川怜子66| 4438全国亚洲精品在线观看视频| 51精品国产黑色丝袜高跟鞋| 成人中文字幕在线观看| 热久久这里只有精品| 亚洲成人精品久久| 国产拍精品一二三| 欧美一乱一性一交一视频| 欧美肥臀大乳一区二区免费视频| 日韩av网站导航| 一区二区三区www| 国产精品福利久久久| 狠狠干狠狠久久| 国产欧美日韩免费| 一区二区亚洲欧洲国产日韩| 国产一区二区美女视频| 一本色道久久88精品综合| 国产精品自拍偷拍视频| 91久久国产婷婷一区二区| 热门国产精品亚洲第一区在线| 粗暴蹂躏中文一区二区三区| 色伦专区97中文字幕| 亚洲第一精品夜夜躁人人爽| 成人在线视频网站| 91精品久久久久久久久不口人| 成人福利网站在线观看11| 精品久久久久久久久久久久久| 97香蕉超级碰碰久久免费的优势| 国产精品69精品一区二区三区| 欧美超级乱淫片喷水| 久久韩剧网电视剧| 久久久久在线观看| 国产午夜精品理论片a级探花| 欧美高清视频在线观看| 精品福利免费观看| 97人人做人人爱| 欧美国产极速在线| 亚洲成人在线网| 亚洲丁香婷深爱综合| 亚洲国产精彩中文乱码av| 中文字幕视频在线免费欧美日韩综合在线看| 欧美电影免费观看高清| 国产精品久久久久aaaa九色| 国产精品普通话| 一区二区在线视频| 亚洲一区二区三区sesese| 麻豆一区二区在线观看| 国产日韩在线亚洲字幕中文| 欧美大学生性色视频| 亚洲国内精品视频| 97在线看福利| 欧美高清无遮挡| 中文字幕最新精品| 91久久在线播放| 中文字幕精品—区二区| 欧美裸体xxxx极品少妇| 九九热最新视频//这里只有精品| 亚洲美女又黄又爽在线观看| 成人福利网站在线观看| 精品国产一区二区在线| 欧美有码在线观看| 国产精品久久久久久久久免费看| 日韩有码在线播放| 亚洲成人免费网站| 久久久久99精品久久久久| 精品久久久久久| 精品国产依人香蕉在线精品| 68精品国产免费久久久久久婷婷| 色综合91久久精品中文字幕| 中日韩午夜理伦电影免费| 欧美日韩中文字幕在线视频| 久久精品电影网站| 在线观看91久久久久久| 精品一区二区三区四区| 久久免费国产视频| 在线观看91久久久久久| 在线视频欧美性高潮| 国产91在线播放| 国产成人亚洲综合青青| 国产精品第七十二页| 亚洲 日韩 国产第一| 在线播放国产一区中文字幕剧情欧美| 91久久精品国产91久久| 日韩中文视频免费在线观看| 欧美大尺度电影在线观看| 亚洲欧美激情四射在线日| 欧美激情欧美激情在线五月| 欧美成人精品一区| 亚洲最大的网站|