springboot集成redission及分布式鎖的使用
1、引入jar包
<dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.13.4</version> </dependency>
2、增加Configuration類
@Configuration public class RedissonConfig { @Value("${spring.redis.host}") private String host; @Value("${spring.redis.port}") private String port; @Value("${spring.redis.password}") private String password; @Bean public RedissonClient getRedisson() { Config config = new Config(); config.useSingleServer().setAddress("redis://" + host + ":" + port).setPassword(password); return Redisson.create(config); } }
3、使用redission分布式鎖
@Autowired private RedissonClient redissonClient;
//方法區(qū) String key = "aa:bb:cc:01"; RLock rLock =redissonClient.getLock(key); try{<br>// 嘗試加鎖,最多等待1秒,上鎖以后10秒自動(dòng)解鎖<br>// 沒有Watch Dog ,10s后自動(dòng)釋放 boolean res = rLock.tryLock(1,10, TimeUnit.SECONDS); if(!res){ return new GeneralVO<>(400, "請(qǐng)勿重復(fù)提交", false); } }finally{ rLock.unlock(); }
private void redissonDoc() throws InterruptedException { //1. 普通的可重入鎖 RLock lock = redissonClient.getLock("generalLock"); // 拿鎖失敗時(shí)會(huì)不停的重試 // 具有Watch Dog 自動(dòng)延期機(jī)制 默認(rèn)續(xù)30s 每隔30/3=10 秒續(xù)到30s lock.lock(); // 嘗試拿鎖10s后停止重試,返回false // 具有Watch Dog 自動(dòng)延期機(jī)制 默認(rèn)續(xù)30s boolean res1 = lock.tryLock(10, TimeUnit.SECONDS); // 拿鎖失敗時(shí)會(huì)不停的重試 // 沒有Watch Dog ,10s后自動(dòng)釋放 lock.lock(10, TimeUnit.SECONDS); // 嘗試拿鎖100s后停止重試,返回false // 沒有Watch Dog ,10s后自動(dòng)釋放 boolean res2 = lock.tryLock(100, 10, TimeUnit.SECONDS); //2. 公平鎖 保證 Redisson 客戶端線程將以其請(qǐng)求的順序獲得鎖 RLock fairLock = redissonClient.getFairLock("fairLock"); //3. 讀寫鎖 沒錯(cuò)與JDK中ReentrantLock的讀寫鎖效果一樣 RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("readWriteLock"); readWriteLock.readLock().lock(); readWriteLock.writeLock().lock(); }
Springboot整合Redisson 鎖
Redisson是一個(gè)在Redis的基礎(chǔ)上實(shí)現(xiàn)的Java駐內(nèi)存數(shù)據(jù)網(wǎng)格
一、依賴
<dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.15.4</version> </dependency>
二、配置文件
spring: redis: database: 7 host: 116.62.178.11 port: 6379 password: 1234qwer # spring-boot 1.0默認(rèn) jedis; spring-boot2.0 默認(rèn)lettuce ,lettuce線程安全 lettuce: pool: # 連接池中的最大空閑連接 默認(rèn)8 max-idle: 8 # 連接池中的最小空閑連接 默認(rèn)0 min-idle: 500 # 連接池最大連接數(shù) 默認(rèn)8 ,負(fù)數(shù)表示沒有限制 max-active: 2000 # 連接池最大阻塞等待時(shí)間(使用負(fù)值表示沒有限制) 默認(rèn)-1 max-wait: -1 cache: type: redis
@Configuration public class RedissonConfig { @Value("${spring.redis.host}") private String host; @Value("${spring.redis.port}") private int port; @Value("${spring.redis.password}") private String password; @Bean(destroyMethod = "shutdown") RedissonClient redissonClient() throws IOException { Config config = new Config(); config.useSingleServer() .setPassword(password) .setAddress("redis://" + host + ":" + port).setDatabase(7); return Redisson.create(config); } }
三、鎖的使用
讀寫鎖
public class RedissionDemo { @Autowired private RedissonClient redissonClient; @Autowired private RedisTemplate redisTemplate; /** * 讀寫鎖 總結(jié) * * 讀鎖又叫共享鎖 * 寫鎖又叫排他鎖(互斥鎖) * 讀 + 讀 相當(dāng)于無鎖,并發(fā)讀,同時(shí)加鎖成功 * 寫 + 寫 阻塞狀態(tài) * 寫 + 讀 等待寫鎖釋放 * 讀 + 寫 等待讀鎖完,才寫, */ public String writeValue() { String str = ""; RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("writeLock"); RLock rLock = readWriteLock.writeLock(); try { rLock.lock(); str = UUID.randomUUID().toString(); redisTemplate.opsForValue().set("uuid", str); Thread.sleep(30000); } catch (Exception e) { } finally { rLock.unlock(); } return str; } /** * 讀鎖 * * @return */ public String readValue() { String str = ""; RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("writeLock"); RLock rLock = readWriteLock.readLock(); rLock.lock(); str = (String) redisTemplate.opsForValue().get("uuid"); rLock.unlock(); return str; } }
信號(hào)量
public class RedissionDemo { @Autowired private RedissonClient redissonClient; @Autowired private RedisTemplate redisTemplate; /** * 信號(hào)量 * * @return */ //停車方法 @GetMapping("/park") public String park() throws InterruptedException { //這里是獲取信號(hào)量的值,這個(gè)信號(hào)量的name一定要與你初始化的一致 RSemaphore park = redissonClient.getSemaphore("park"); //這里會(huì)將信號(hào)量里面的值-1,如果為0則一直等待,直到信號(hào)量>0 park.acquire(); //tryAcquire為非阻塞式等待 //park.tryAcquire(); return "ok"; } public String go() throws InterruptedException { //這里是獲取信號(hào)量的值,這個(gè)信號(hào)量的name一定要與你初始化的一致 RSemaphore park = redissonClient.getSemaphore("park"); //這里會(huì)將信號(hào)量里面的值+1,也就是釋放信號(hào)量 park.release(); return "ok"; } }
閉鎖
public class RedissionDemo { @Autowired private RedissonClient redissonClient; @Autowired private RedisTemplate redisTemplate; /** * 閉鎖,限流 * * @return * @throws InterruptedException */ //鎖門 public String lockdoor() throws InterruptedException { RCountDownLatch door = redissonClient.getCountDownLatch("door"); //設(shè)置一個(gè)班級(jí)有20個(gè)同學(xué) door.trySetCount(20); //需要等到20個(gè)同學(xué)全部離開,才鎖門 door.await(); return "鎖門了"; } public String leave(Long id) throws InterruptedException { RCountDownLatch door = redissonClient.getCountDownLatch("door"); //表示一個(gè)同學(xué)離開 door.countDown(); return "" + id + "號(hào)同學(xué)離開了"; } }
四、分布式秒殺
秒殺流程:
@Service @Slf4j public class DistributedSecKillBiz { @Autowired private RedisTemplate redisTemplate; @Autowired private RedissonClient redissonClient; /** * 分布式鎖。唯一缺點(diǎn) 枷鎖失效時(shí)間 * 枷鎖院子操作, * 解鎖,刪除鎖也是原子操作 瑕疵沒有續(xù)命 * * @return */ public String doKill() { String lock = UUID.randomUUID().toString(); String goodsId = "10054"; boolean flag = redisTemplate.opsForValue().setIfAbsent(goodsId, lock, 30, TimeUnit.SECONDS); if (flag) { // 獲取鎖成功 try { Long stock = redisTemplate.opsForValue().decrement(upActivityKey() + SecKillConstant.CACHE_FOODS_COUNT + goodsId); if (stock > 0) { redisTemplate.opsForValue().increment(upActivityKey() + SecKillConstant.CACHE_FOODS_COUNT + goodsId); log.info("扣減庫存成功,還剩:" + stock); } return "庫存不足,該商品已搶購?fù)辏?quot;; } catch (Exception e) { } finally { String script = "if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end"; redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Arrays.asList(goodsId), lock); } } return doKill(); } /** * 整合 redission * @return */ public String doKillDistributed() { String goodsId = "10054"; RLock lock = redissonClient.getLock(upActivityKey() + SecKillConstant.LOCK + goodsId); // 獲取鎖成功 try { //1 阻塞式等待,默認(rèn)30秒時(shí)間 //2 自動(dòng)續(xù)期,如果業(yè)務(wù)超長,續(xù)上新的30秒,不用擔(dān)心過期時(shí)間,鎖自動(dòng)刪除掉 //3 枷鎖的業(yè)務(wù)運(yùn)行完成,就不會(huì)給當(dāng)前的鎖自動(dòng)續(xù)期,即使沒有手動(dòng)釋放鎖也會(huì),30秒自動(dòng)釋放 // lock.lock(30, TimeUnit.SECONDS); //不會(huì)自動(dòng)續(xù)期需要注意 lock.lock(); Long stock = redisTemplate.opsForValue().decrement(upActivityKey() + SecKillConstant.CACHE_FOODS_COUNT + goodsId); if (stock > 0) { redisTemplate.opsForValue().increment(upActivityKey() + SecKillConstant.CACHE_FOODS_COUNT + goodsId); log.info("扣減庫存成功,還剩:" + stock); } return "庫存不足,該商品已搶購?fù)辏?quot;; } catch (Exception e) { } finally { lock.unlock(); } return "fail"; } /** * 獲取活動(dòng) * * @return */ public ActivityBo upActivity() { return new ActivityBo("七夕活動(dòng)", "SEVEN_ACTIVITY", new Date(), new Date()); } /** * 活動(dòng)公共key * * @return */ public String upActivityKey() { return SecKillConstant.SEC_KILL + upActivity().getActivityKey() + ":"; } }
五、redis鎖 單機(jī)版可用,分布式用Redisson
package com.yang.yimall.seckill.app.seckill.biz; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.script.DefaultRedisScript; import org.springframework.stereotype.Service; import java.util.Collections; import java.util.UUID; import java.util.concurrent.TimeUnit; /** * redis 鎖 集群有瑕疵 不能 續(xù)命 */ @Service public class RedisLock { @Autowired private RedisTemplate redisTemplate; private String lockName = "lockName"; private ThreadLocal<String> threadLocal = new ThreadLocal<>(); public void lock(String lockName) { if (tryLock(lockName)) { return; } lock(lockName); } public void lock() { if (tryLock(lockName)) { return; } lock(); } /** * 添加key 并且設(shè)置過期時(shí)間 原子操作 * * @param lockName * @return */ public boolean tryLock(String lockName) { String uuid = UUID.randomUUID().toString(); threadLocal.set(uuid); return redisTemplate.opsForValue().setIfAbsent(lockName, uuid, 30, TimeUnit.SECONDS); } /** * 如果查詢有key,就刪除, 原子操作 */ public void unlock() { String script = "if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end"; redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Collections.singletonList(lockName), threadLocal.get()); } }
使用
public String doKillUp() { String goodsId = "10054"; redisLock.lock(goodsId); // 獲取鎖成功 try { Long stock = redisTemplate.opsForValue().decrement(upActivityKey() + SecKillConstant.CACHE_FOODS_COUNT + goodsId); if (stock > 0) { redisTemplate.opsForValue().increment(upActivityKey() + SecKillConstant.CACHE_FOODS_COUNT + goodsId); log.info("扣減庫存成功,還剩:" + stock); } return "庫存不足,該商品已搶購?fù)辏?quot;; } catch (Exception e) { } finally { redisLock.unlock(); } return "庫存不足,該商品已搶購?fù)辏?quot;; }
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持服務(wù)器之家。
原文鏈接:https://www.cnblogs.com/jiehanshi/p/13693129.html