av一区二区在线观看_亚洲男人的天堂网站_日韩亚洲视频_在线成人免费_欧美日韩精品免费观看视频_久草视

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

SpringBoot中使用redis做分布式鎖的方法

瀏覽:5日期:2023-04-26 09:38:28

一.模擬問(wèn)題

最近在公司遇到一個(gè)問(wèn)題,掛號(hào)系統(tǒng)是做的集群,比如啟動(dòng)了兩個(gè)相同的服務(wù),病人掛號(hào)的時(shí)候可能會(huì)出現(xiàn)同號(hào)的情況,比如兩個(gè)病人掛出來(lái)的號(hào)都是上午2號(hào).這就出現(xiàn)了問(wèn)題,由于是集群部署的,所以單純?cè)诖a中的方法中加鎖是不能解決這種情況的.下面我將模擬這種情況,用redis做分布式鎖來(lái)解決這個(gè)問(wèn)題.

1.新建掛號(hào)明細(xì)表

SpringBoot中使用redis做分布式鎖的方法

2.在idea上新建項(xiàng)目

SpringBoot中使用redis做分布式鎖的方法

SpringBoot中使用redis做分布式鎖的方法

SpringBoot中使用redis做分布式鎖的方法

下圖是創(chuàng)建好的項(xiàng)目結(jié)構(gòu),上面那個(gè)parent項(xiàng)目是其他項(xiàng)目不用管它,和新建的沒(méi)有關(guān)系

SpringBoot中使用redis做分布式鎖的方法

3.開(kāi)始創(chuàng)建controller,service,dao(mapper),寫(xiě)好后整體結(jié)構(gòu)如下

SpringBoot中使用redis做分布式鎖的方法

這里貼上service實(shí)現(xiàn)類(lèi)的代碼,主要代碼就是這一塊:

package com.zk.service.impl; import com.zk.mapper.MzMapper;import com.zk.service.MzService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import java.util.HashMap;import java.util.Map; /** * 門(mén)診操作service實(shí)現(xiàn)類(lèi) * * @author zk * @date 2020-9-9 */@Servicepublic class MzServiceImpl implements MzService { @Autowired private MzMapper mzMapper; @Override public Map<String, Object> gh(String ksdm, String ysdm,String brid) { Map<String,Object> resultMap = new HashMap<>(); int ghxh = 0; //獲取當(dāng)前的掛號(hào)序號(hào) Map<String, Object> ghxhMap = mzMapper.getGhxh(ksdm,ysdm); //如果為空,說(shuō)明還沒(méi)有人掛這個(gè)醫(yī)生的號(hào),當(dāng)前是一號(hào) if(ghxhMap == null){ ghxh = 1; }else{ ghxh = (int)ghxhMap.get('GHXH'); ghxh++; } //實(shí)際場(chǎng)景中,先獲取到ghxh后,還會(huì)進(jìn)行收費(fèi)等其他操作,這里模擬一下需要耗費(fèi)時(shí)間,為了方便測(cè)試出現(xiàn)問(wèn)題,這里時(shí)間設(shè)置稍微長(zhǎng)一點(diǎn) try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } //新增掛號(hào)明細(xì)記錄 mzMapper.addGhmx(ksdm,ysdm,ghxh,brid); resultMap.put('code','200'); resultMap.put('msg','success'); return resultMap; }}

4.進(jìn)行測(cè)試

1)清空數(shù)據(jù)庫(kù)表

2)使用postman發(fā)送post請(qǐng)求,假設(shè)ksdm=1表示皮膚科,ysdm=1表示醫(yī)生華佗,brbh=1表示張三,現(xiàn)在張三去醫(yī)院掛皮膚科華佗醫(yī)生的號(hào),收費(fèi)員就會(huì)操作系統(tǒng)調(diào)用上面寫(xiě)的掛號(hào)接口.

SpringBoot中使用redis做分布式鎖的方法

調(diào)用成功后,看看數(shù)據(jù)庫(kù)里的數(shù)據(jù)

SpringBoot中使用redis做分布式鎖的方法

可以看到張三掛到了華佗醫(yī)生的第一個(gè)號(hào),接著把請(qǐng)求參數(shù)的brbh改成2表示李四,李四也去掛華佗醫(yī)生的號(hào)

SpringBoot中使用redis做分布式鎖的方法

請(qǐng)求成功后查看數(shù)據(jù)庫(kù)

SpringBoot中使用redis做分布式鎖的方法

可以看到李四掛了華佗醫(yī)生的第二個(gè)號(hào).現(xiàn)在就是正常的掛號(hào),沒(méi)有出現(xiàn)問(wèn)題.

3)postman開(kāi)第二個(gè)請(qǐng)求窗口,兩個(gè)窗口同時(shí)去掉接口進(jìn)行掛號(hào)

窗口一模擬張三掛號(hào)

SpringBoot中使用redis做分布式鎖的方法

窗口二模擬李四掛號(hào)

操作成功后看看數(shù)據(jù)庫(kù)

SpringBoot中使用redis做分布式鎖的方法

結(jié)果是張三和李四都掛到了三號(hào),這就出現(xiàn)了線(xiàn)程安全問(wèn)題.

3)使用加鎖的方式解決問(wèn)題

SpringBoot中使用redis做分布式鎖的方法

方法加上鎖之后,測(cè)試確實(shí)沒(méi)有問(wèn)題了,但是實(shí)際情況是集群部署的,并不是只有一個(gè)服務(wù)

4)啟動(dòng)兩個(gè)服務(wù)

idea中設(shè)置允許啟動(dòng)多個(gè)實(shí)例

SpringBoot中使用redis做分布式鎖的方法

修改端口號(hào),第一個(gè)啟動(dòng)的端口號(hào)是8080,這里改成8081

SpringBoot中使用redis做分布式鎖的方法

5)啟動(dòng)兩個(gè)服務(wù)后再用postman去請(qǐng)求,張三請(qǐng)求8080服務(wù)接口

SpringBoot中使用redis做分布式鎖的方法

李四請(qǐng)求8081接口

SpringBoot中使用redis做分布式鎖的方法

兩個(gè)窗口同時(shí)請(qǐng)求的時(shí)候,再次出現(xiàn)了ghxh相同的情況,在方法上加同步鎖不能解決這個(gè)問(wèn)題.

二.使用redis做分布式鎖解決問(wèn)題

1.首先需要啟動(dòng)一個(gè)redis,如果不知道redis怎么安裝使用的,可以看我之前寫(xiě)的'redis的安裝和使用'.因?yàn)橹拔以谔摂M機(jī)上安裝過(guò)了redis,這里直接啟動(dòng)一個(gè)單節(jié)點(diǎn)redis

SpringBoot中使用redis做分布式鎖的方法

2.項(xiàng)目的pom.xml文件引入redis依賴(lài)

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

3.在application.yml中配置redis

spring: redis: host: 192.168.1.6 port: 6379

4.新建redis鎖操作類(lèi)

package com.zk.util; import org.apache.commons.lang3.StringUtils;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.stereotype.Repository; import java.util.UUID;import java.util.concurrent.TimeUnit; /** * redis鎖操作類(lèi) * * @author zk * @date 2020-9-10 */@Repositorypublic class RedisLock { private StringRedisTemplate stringredisTemplate; public RedisLock(StringRedisTemplate stringredisTemplate) { this.stringredisTemplate = stringredisTemplate; } /** * 加鎖,無(wú)阻塞 * 加鎖過(guò)程必須設(shè)置過(guò)期時(shí)間 * 如果沒(méi)有設(shè)置過(guò)期時(shí)間,手動(dòng)釋放鎖的操作出現(xiàn)問(wèn)題,那么就發(fā)生死鎖,鎖永遠(yuǎn)不能被釋放. * 加鎖和設(shè)置過(guò)期時(shí)間過(guò)程必須是原子操作 * 如果加鎖后服務(wù)宕機(jī)或程序崩潰,來(lái)不及設(shè)置過(guò)期時(shí)間,同樣會(huì)發(fā)生死鎖. * * @param key 鎖id * @param expire 過(guò)期時(shí)間 * @return */ public String tryLock(String key, long expire) { String token = UUID.randomUUID().toString(); //setIfAbsent方法:當(dāng)key不存在的時(shí)候,設(shè)置成功并返回true,當(dāng)key存在的時(shí)候,設(shè)置失敗并返回false //token是對(duì)應(yīng)的value,expire是緩存過(guò)期時(shí)間 Boolean isSuccess = stringredisTemplate.opsForValue().setIfAbsent(key, token, expire, TimeUnit.MILLISECONDS); if (isSuccess) { return token; } return null; } /** * 加鎖,有阻塞 * * @param name 鎖名稱(chēng) * @param expire 鎖過(guò)期時(shí)間 * @param timeout 請(qǐng)求超時(shí)時(shí)間 * @return */ public String lock(String name, long expire, long timeout) { long startTime = System.currentTimeMillis(); String token; do { token = tryLock(name, expire); if (token == null) { if ((System.currentTimeMillis() - startTime) > (timeout - 50)) { break; } try { //try 50 per sec Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); return null; } } } while (token == null); return token; } /** * 解鎖操作 * 解鎖必須是解除自己加上的鎖 * 試想一個(gè)這樣的場(chǎng)景,服務(wù)A加鎖,但執(zhí)行效率非常慢,導(dǎo)致鎖失效后還未執(zhí)行完,但這時(shí)候服務(wù)B已經(jīng)拿到鎖了,這時(shí)候服務(wù)A執(zhí)行完畢了去解鎖, * 把服務(wù)B的鎖給解掉了,其他服務(wù)C、D、E...都可以拿到鎖了,這就有問(wèn)題了. * 加鎖的時(shí)候我們可以設(shè)置唯一value,解鎖時(shí)判斷是不是自己先前的value就行了. * * @param key * @param token * @return */ public boolean unlock(String key, String token) { //解鎖時(shí)需要先取出key對(duì)應(yīng)的value進(jìn)行判斷是否相等,這也是為什么加鎖的時(shí)候需要放不重復(fù)的值作為value String value = stringredisTemplate.opsForValue().get('name'); if (StringUtils.equals(value, token)) { stringredisTemplate.delete(key); return true; } return false; }}

5.修改業(yè)務(wù)操作類(lèi),用上RedisLock

package com.zk.service.impl; import com.zk.mapper.MzMapper;import com.zk.service.MzService;import com.zk.util.RedisLock;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service; import java.util.HashMap;import java.util.Map; /** * 門(mén)診操作service實(shí)現(xiàn)類(lèi) * * @author zk * @date 2020-9-9 */@Servicepublic class MzServiceImpl implements MzService { @Autowired private MzMapper mzMapper; @Autowired private RedisLock redisLock; @Override public Map<String, Object> gh(String ksdm, String ysdm, String brid) { Map<String, Object> resultMap = new HashMap<>(); int ghxh = 0; //加鎖操作 String token = null; token = redisLock.lock('gh', 3000,3500); try { //獲取到了鎖,執(zhí)行正常業(yè)務(wù) if (token != null) { //獲取當(dāng)前的掛號(hào)序號(hào) Map<String, Object> ghxhMap = mzMapper.getGhxh(ksdm, ysdm); //如果為空,說(shuō)明還沒(méi)有人掛這個(gè)醫(yī)生的號(hào),當(dāng)前是一號(hào) if (ghxhMap == null) { ghxh = 1; } else { ghxh = (int) ghxhMap.get('GHXH'); ghxh++; } //實(shí)際場(chǎng)景中,先獲取到ghxh后,還會(huì)進(jìn)行收費(fèi)等其他操作,這里模擬一下需要耗費(fèi)時(shí)間,為了方便測(cè)試出現(xiàn)問(wèn)題,這里時(shí)間設(shè)置稍微長(zhǎng)一點(diǎn) try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } //新增掛號(hào)明細(xì)記錄 mzMapper.addGhmx(ksdm, ysdm, ghxh, brid); } else { resultMap.put('code', '401'); resultMap.put('msg', '其他窗口正在操作,請(qǐng)稍后再試'); return resultMap; } } finally { //解鎖 if (token != null) { boolean gh = redisLock.unlock('gh', token); } } resultMap.put('code', '200'); resultMap.put('msg', 'success'); return resultMap; }}

6.再用postman開(kāi)兩個(gè)窗口去請(qǐng)求,和上面的操作一樣,然后再看數(shù)據(jù)庫(kù)的數(shù)據(jù)

SpringBoot中使用redis做分布式鎖的方法

問(wèn)題解決,不會(huì)再出現(xiàn)重復(fù)的ghxh.

總結(jié)

到此這篇關(guān)于SpringBoot中使用redis做分布式鎖的方法的文章就介紹到這了,更多相關(guān)SpringBoot redis分布式鎖內(nèi)容請(qǐng)搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!

標(biāo)簽: Spring
相關(guān)文章:
主站蜘蛛池模板: 久久9视频 | 91福利网| 日本国产精品视频 | 国产成人黄色 | 爱综合 | 亚洲精品久久久久久一区二区 | 日韩欧美在线不卡 | 中文字幕 在线观看 | www狠狠爱com | 久草视频在线播放 | 中文字幕亚洲一区 | 日韩av在线不卡 | 九九视频在线观看视频6 | 日本不卡一区二区三区 | 精品久久久久久久久久久久久久 | 在线日韩欧美 | 一区二区三区四区日韩 | 农村黄性色生活片 | 在线视频91 | 国内久久 | 隔壁老王国产在线精品 | 99精品网 | 亚洲国产精品日韩av不卡在线 | 人人看人人干 | 日本免费一区二区三区视频 | 精品国产乱码久久久久久蜜臀 | 一区二区中文 | 97视频成人 | 亚洲欧美一区二区三区国产精品 | 欧美一级三级 | 国产成人精品久久二区二区91 | 视频一区二区中文字幕 | 一区二区三区电影网 | 精品视频一区二区 | 超碰在线人人 | 国产av毛片| h片免费在线观看 | 91视频国产精品 | 婷婷免费在线 | 国产日韩欧美精品 | 日韩av成人在线 |