日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術(shù)文章
文章詳情頁

SpringBoot整合Redis正確的實(shí)現(xiàn)分布式鎖的示例代碼

瀏覽:18日期:2023-05-08 10:12:43

前言

最近在做分塊上傳的業(yè)務(wù),使用到了Redis來維護(hù)上傳過程中的分塊編號。

每上傳完成一個分塊就獲取一下文件的分塊集合,加入新上傳的編號,手動接口測試下是沒有問題的,前端通過并發(fā)上傳調(diào)用就出現(xiàn)問題了,并發(fā)的get再set,就會存在覆蓋寫現(xiàn)象,導(dǎo)致最后的分塊數(shù)據(jù)不對,不能觸發(fā)分塊合并請求。

遇到并發(fā)二話不說先上鎖,針對執(zhí)行代碼塊加了一個JVM鎖之后問題就解決了。

仔細(xì)一想還是不太對,項(xiàng)目是分布式部署的,做了負(fù)載均衡,一個節(jié)點(diǎn)的代碼被鎖住了,請求輪詢到其他節(jié)點(diǎn)還是可以進(jìn)行覆蓋寫,并沒有解決到問題啊

沒辦法,只有用上分布式鎖了。之前對于分布式鎖的理論還是很熟悉的,沒有比較好的應(yīng)用場景就沒寫過具體代碼,趁這個機(jī)會就學(xué)習(xí)使用一下分布式鎖。

理論

分布式鎖是控制分布式系統(tǒng)之間同步訪問共享資源的一種方式。是為了解決分布式系統(tǒng)中,不同的系統(tǒng)或是同一個系統(tǒng)的不同主機(jī)共享同一個資源的問題,它通常會采用互斥來保證程序的一致性

SpringBoot整合Redis正確的實(shí)現(xiàn)分布式鎖的示例代碼

通常的實(shí)現(xiàn)方式有三種:

基于 MySQL 的悲觀鎖來實(shí)現(xiàn)分布式鎖,這種方式使用的最少,這種實(shí)現(xiàn)方式的性能不好,且容易造成死鎖,并且MySQL本來業(yè)務(wù)壓力就很大了,再做鎖也不太合適 基于 Redis 實(shí)現(xiàn)分布式鎖,單機(jī)版可用setnx實(shí)現(xiàn),多機(jī)版建議用Radission 基于 ZooKeeper 實(shí)現(xiàn)分布式鎖,利用 ZooKeeper 順序臨時節(jié)點(diǎn)來實(shí)現(xiàn)

為了確保分布式鎖可用,我們至少要確保鎖的實(shí)現(xiàn)同時滿足以下四個條件:

互斥性。在任意時刻,只有一個客戶端能持有鎖。 不會發(fā)生死鎖。即使有一個客戶端在持有鎖的期間崩潰而沒有主動解鎖,也能保證后續(xù)其他客戶端能加鎖。 具有容錯性。只要大部分的Redis節(jié)點(diǎn)正常運(yùn)行,客戶端就可以加鎖和解鎖。 解鈴還須系鈴人。加鎖和解鎖必須是同一個客戶端,客戶端自己不能把別人加的鎖給解了。

本文就使用的是Redis的setnx實(shí)現(xiàn),如果Redis是多機(jī)版的可以去了解下Radssion,封裝的就特別的好,也是官方推薦的

代碼

1. 加依賴

引入Spring Boot和Redis整合的快速使用依賴

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>

2. 加配置

application.properties中加入Redis連接相關(guān)配置

spring.redis.host=xxxspring.redis.port=6379spring.redis.database=0spring.redis.password=xxxspring.redis.timeout=10000# 設(shè)置jedis連接池spring.redis.jedis.pool.max-active=50spring.redis.jedis.pool.min-idle=20

3. 重寫Redis的序列化規(guī)則

默認(rèn)使用的JDK的序列化,不自己設(shè)置一下Redis中的數(shù)據(jù)是看不懂的

/** * @author Chkl * @create 2020/6/7 * @since 1.0.0 */@Componentpublic class RedisConfig { /** * 改造RedisTemplate,重寫序列化規(guī)則,避免存入序列化內(nèi)容看不懂 * @param connectionFactory * @return */ @Bean public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) { RedisTemplate redisTemplate = new RedisTemplate(); redisTemplate.setConnectionFactory(connectionFactory); // 設(shè)置key和value的序列化規(guī)則 redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer(Object.class)); redisTemplate.setKeySerializer(new StringRedisSerializer()); return redisTemplate; }}

4. 如何正確的上鎖

直接上代碼

@Componentpublic class RedisLock { @Autowired private StringRedisTemplate redisTemplate; private long timeout = 3000; /** * 上鎖 * @param key 鎖標(biāo)識 * @param value 線程標(biāo)識 * @return 上鎖狀態(tài) */ public boolean lock(String key, String value) { long start = System.currentTimeMillis(); while (true) { //檢測是否超時 if (System.currentTimeMillis() - start > timeout) {return false; } //執(zhí)行set命令 Boolean absent = redisTemplate.opsForValue().setIfAbsent(key, value, timeout, TimeUnit.MILLISECONDS);//1 //是否成功獲取鎖 if (absent) {return true; } return false; } }}

核心代碼就是

Boolean absent = redisTemplate.opsForValue().setIfAbsent(key, value, timeout, TimeUnit.MILLISECONDS);

setIfAbsent方法就相當(dāng)于命令行下的Setnx方法,指定的 key 不存在時,為 key 設(shè)置指定的值

參數(shù)分別是key、value、超時時間和時間單位

key,表示針對于這段資源的唯一標(biāo)識 value,表示針對于這個線程的唯一標(biāo)識。為什么有了key了還需要設(shè)置value呢,就是為了滿足四個條件的最后一個:解鈴還須系鈴人。只有通過key和value的組合才能保證解鎖時是同一個線程來解鎖 超時時間,必須和setnx一起進(jìn)行操作,不能再setnx結(jié)束后再執(zhí)行。如果加鎖成功了,還沒有設(shè)置過期時間就宕機(jī)了,鎖就永遠(yuǎn)不會過期,變成死鎖

5. 如何正確解鎖

@Componentpublic class RedisLock { @Autowired private StringRedisTemplate redisTemplate; @Autowired private DefaultRedisScript<Long> redisScript; private static final Long RELEASE_SUCCESS = 1L; /** * 解鎖 * @param key 鎖標(biāo)識 * @param value 線程標(biāo)識 * @return 解鎖狀態(tài) */ public boolean unlock(String key, String value) { //使用Lua腳本:先判斷是否是自己設(shè)置的鎖,再執(zhí)行刪除 Long result = redisTemplate.execute(redisScript, Arrays.asList(key,value)); //返回最終結(jié)果 return RELEASE_SUCCESS.equals(result); } /** * @return lua腳本 */ @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; }}

解鎖過程需要兩步操作

1.判斷操作線程是否是加鎖的線程2.如果是加鎖線程,執(zhí)行解鎖操作

這兩步操作也需要原子的進(jìn)行操作,但是Redis不支持這兩步的合并的操作,所以,就只有使用lua腳本實(shí)現(xiàn)來保證原子性咯如果在判斷是加鎖的線程之后,并且執(zhí)行解鎖之前,鎖到期了,被其他線程獲得鎖了,這時候再進(jìn)行解鎖就會解掉其他線程的鎖,使得不滿足解鈴還須系鈴人

6. 實(shí)際應(yīng)用

沒有使用分布式鎖時的保存文件分塊的代碼

/** * 保存文件分塊編號到redis * @param chunkNumber 分塊號 * @param identifier 文件唯一編號 * @return 文件分塊的大小 */ @Override public Integer saveChunk(Integer chunkNumber, String identifier) { //從Redis獲取已經(jīng)存在的分塊編號集合 Set<Integer> oldChunkNumber = (Set<Integer>) JSON.parseObject(redisOperator.get('chunkNumberList_'+identifier),Set.class); //如果不存在分塊集合,創(chuàng)建一個集合 if (Objects.isNull(oldChunkNumber)) { Set<Integer> newChunkNumber = new HashSet<>(); newChunkNumber.add(chunkNumber); redisOperator.set('chunkNumberList_'+identifier, JSON.toJSONString(newChunkNumber),36000); return newChunkNumber.size(); //如果分塊集合已經(jīng)存在了,就添加一個編號 } else { oldChunkNumber.add(chunkNumber); redisOperator.set('chunkNumberList_'+identifier, JSON.toJSONString(oldChunkNumber),36000); return oldChunkNumber.size(); } }

存在的問題是:當(dāng)并發(fā)的請求進(jìn)來之后,可能獲取同一個狀態(tài)的集合進(jìn)行修改,修改后直接寫入,造成同一個狀態(tài)獲得的集合操作線程覆蓋寫的現(xiàn)象

使用分布式鎖保證同時只能有一個線程能獲取到集合并進(jìn)行修改,避免了覆蓋寫現(xiàn)象

使用分布式鎖代碼

/** * 保存文件分塊編號到redis * @param chunkNumber 分塊號 * @param identifier 文件唯一編號 * @return 文件分塊的大小 */ @Override public Integer saveChunk(Integer chunkNumber, String identifier) { //通過UUID生成一個請求線程識別標(biāo)志作為鎖的value String threadUUID = CoreUtil.getUUID(); //上鎖,以共享資源標(biāo)識:文件唯一編號,作為key,以線程標(biāo)識UUID作為value redisLock.lock(identifier,threadUUID); //從Redis獲取已經(jīng)存在的分塊編號集合 Set<Integer> oldChunkNumber = (Set<Integer>) JSON.parseObject(redisOperator.get('chunkNumberList_'+identifier),Set.class); //如果不存在分塊集合,創(chuàng)建一個集合 if (Objects.isNull(oldChunkNumber)) { Set<Integer> newChunkNumber = new HashSet<>(); newChunkNumber.add(chunkNumber); redisOperator.set('chunkNumberList_'+identifier, JSON.toJSONString(newChunkNumber),36000); //解鎖 redisLock.unlock(identifier,threadUUID); return newChunkNumber.size(); //如果分塊集合已經(jīng)存在了,就添加一個編號 } else { oldChunkNumber.add(chunkNumber); redisOperator.set('chunkNumberList_'+identifier, JSON.toJSONString(oldChunkNumber),36000); //解鎖 redisLock.unlock(identifier,threadUUID); return oldChunkNumber.size(); } }

代碼中使用的共享資源標(biāo)識是文件唯一編號identifier,它能標(biāo)識加鎖代碼段中的唯一資源,即key為'chunkNumberList_'+identifier的集合

代碼中使用的線程唯一標(biāo)識是UUID,能保證加鎖和解鎖時獲取的標(biāo)識不會重復(fù)

到此這篇關(guān)于SpringBoot整合Redis正確的實(shí)現(xiàn)分布式鎖的示例代碼的文章就介紹到這了,更多相關(guān)SpringBoot整合Redis分布式鎖內(nèi)容請搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!

標(biāo)簽: Spring
相關(guān)文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
久久久久久网| 亚洲91久久| 午夜亚洲福利在线老司机| 88xx成人免费观看视频库| 美女福利一区二区三区| 日韩国产一区二区| 夜鲁夜鲁夜鲁视频在线播放| 国产aⅴ精品一区二区四区| 国产精品mm| 欧美午夜网站| 欧美日韩一视频区二区| 欧美日韩亚洲一区三区| 国产精品theporn| 精品国产欧美| 深夜福利视频一区二区| 激情综合自拍| 久热综合在线亚洲精品| 亚洲精品无播放器在线播放| 日本久久一区| 美女视频网站久久| 亚洲伊人av| 久久中文字幕二区| 夜夜嗨av一区二区三区网站四季av| 在线视频免费在线观看一区二区| 欧美在线资源| 久久亚洲精品伦理| 欧美日韩午夜| 国产一区一一区高清不卡| 97se综合| 一本色道精品久久一区二区三区| 日韩精品一二区| 欧美日韩一区二区三区不卡视频| 欧美激情福利| 日韩精品免费一区二区在线观看 | 国产精品美女久久久| 一区二区三区网站| 久久激情综合网| 亚洲一区资源| 视频一区二区三区中文字幕| 国产精品亚洲四区在线观看 | 日韩av字幕| 日本一区二区高清不卡| 欧美日韩少妇| 91欧美极品| 亚洲涩涩在线| 日本在线一区二区三区| 麻豆国产在线| 亚洲性视频在线| 高清日韩中文字幕| 在线亚洲激情| 久久爱www.| 一区二区自拍| 国产精品第一| 欧美另类专区| 美女性感视频久久| 午夜精品亚洲| 另类综合日韩欧美亚洲| 伊人精品在线| 欧美激情99| 模特精品在线| 国产一区不卡| 亚洲精选av| 日韩欧美网址| 日韩av在线免费观看不卡| 午夜影院一区| 久久精品凹凸全集| 99久久激情| 国产精选一区| 欧美日韩国产欧| 精品一区二区三区视频在线播放| 国产精品免费看| 国产自产自拍视频在线观看| 日韩精品国产欧美| 欧美精品一卡| 成人福利av| 国产人成精品一区二区三| 日韩视频免费| 国产一区不卡| 欧美亚洲福利| 亚洲资源av| 亚洲人成在线网站| 国产免费av一区二区三区| 91久久亚洲| 国产精品黑丝在线播放| 国产日韩亚洲欧美精品| 国产精品色网| 成人羞羞视频在线看网址| 麻豆精品视频在线观看免费| 中文字幕一区二区三区四区久久| 成人久久久久| 日韩成人精品一区| 国产精品观看| 日韩精品欧美精品| 亚洲一区激情| 最近高清中文在线字幕在线观看1| 国产一精品一av一免费爽爽| 亚洲欧美网站| 国产99亚洲| 91综合视频| 麻豆免费精品视频| 久久狠狠久久| 日本不卡视频在线观看 | 亚洲我射av| 99成人在线| 成人av动漫在线观看| 日韩大片在线| 国产中文在线播放| 国产成人精品一区二区三区在线| 青青草国产成人99久久| 亚洲久久视频| 亚洲激情偷拍| 黄色亚洲大片免费在线观看| 久久久亚洲一区| 日韩欧美1区| 中文字幕人成乱码在线观看| 福利欧美精品在线| 成人在线观看免费视频| 成人高清一区| 人人草在线视频| 蜜桃精品在线| 日韩中文影院| 国产精品久久久久久久久久10秀| 九九久久国产| 国产福利亚洲| 狠狠久久伊人| 成人国产精品| 欧美日韩视频网站| 成人va天堂| 久久精品影视| 91精品国产调教在线观看| 久久婷婷亚洲| 99精品一区| 一区免费在线| 男女性色大片免费观看一区二区| 美女精品在线| 日韩精品视频在线看| 国产精品777777在线播放 | 88xx成人免费观看视频库| 亚洲1234区| 激情婷婷综合| 水蜜桃久久夜色精品一区的特点| 在线看片日韩| 久久国内精品自在自线400部| 欧美亚洲免费| 久久精品欧洲| 99久久激情| 蜜臀久久久99精品久久久久久| 亚洲丝袜美腿一区| 日韩av成人高清| 老司机免费视频一区二区| 成人在线免费观看91| 欧洲av一区二区| 美女精品网站| 国产精品亚洲四区在线观看| 久久亚州av| 亚洲成人av观看| 亚洲一区二区三区免费在线观看| 日韩精品一区二区三区免费视频| 国产精品亚洲二区| 中文在线а√天堂| 国产精品日韩欧美一区| 欧美一区精品| а√天堂8资源中文在线| 欧美日韩国产欧| 日韩高清不卡在线| 美女视频黄 久久| 国产一区观看| 日韩一二三区在线观看| 欧美激情日韩| 亚洲精品网址| 日本特黄久久久高潮| 超级白嫩亚洲国产第一| 99re国产精品| 国产欧美日韩影院| 综合日韩av| 亚洲有吗中文字幕| 久久久久九九精品影院| 国产真实久久| 日韩精彩视频在线观看| 麻豆理论在线观看| 乱人伦精品视频在线观看| 国产精品1区| 亚洲高清影视| 老司机免费视频一区二区| 欧美日韩国产一区精品一区| 国产日韩视频在线| 欧美一区三区| 久久精品av麻豆的观看方式| 久久蜜桃精品| 欧美视频久久| 欧美精品一区二区久久| 国产亚洲一区二区三区啪| 欧美粗暴jizz性欧美20| 国产精品亚洲欧美一级在线| 亚洲香蕉网站| 久久精品xxxxx| 欧美精选一区二区三区| 免费精品一区| 久久国产成人| 精品国模一区二区三区|