Spring?Boot?集成Redisson實(shí)現(xiàn)分布式鎖詳細(xì)案例
前言
Spring Boot集成Redis實(shí)現(xiàn)單機(jī)分布式鎖針對單機(jī)分布式鎖還是存在鎖定續(xù)期、可重入的問題,本文將采用Spring Boot 集成Ression實(shí)現(xiàn)分布式鎖進(jìn)行詳細(xì)講解。
分布式鎖實(shí)現(xiàn)
引入jar包
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <exclusions> <exclusion> <groupId>io.lettuce</groupId> <artifactId>lettuce-core</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.redisson</groupId> <artifactId>redisson-spring-boot-starter</artifactId> <version>3.13.6</version> </dependency>
說明:關(guān)于集成Redisson,我們需要注意與Spring Boot的版本對應(yīng)。
具體對應(yīng)的關(guān)系如下:
注意:3.13.6對應(yīng)的Spring Boot的版本為2.3.0,而redis-spring-data為redis-spring-data-23。我們可以通過查看pom文件的引用從而得到依賴關(guān)系。
Redisson的配置
application.yml中引入redisson.yml配置
redis: redisson: file: classpath:redisson.yml
redisson.yml配置
singleServerConfig: password: xxxx address: "redis://127.0.0.1:6379" database: 1 threads: 0 nettyThreads: 0 codec: !<org.redisson.codec.FstCodec> {} transportMode: "NIO"
說明:本文配置的是單機(jī)環(huán)境,如果需要配置集群環(huán)境,可以采用如下配置:
clusterServersConfig: idleConnectionTimeout: 10000 connectTimeout: 10000 timeout: 3000 retryAttempts: 3 retryInterval: 1500 failedSlaveReconnectionInterval: 3000 failedSlaveCheckInterval: 60000 password: null subscriptionsPerConnection: 5 clientName: null loadBalancer: !<org.redisson.connection.balancer.RoundRobinLoadBalancer> {} subscriptionConnectionMinimumIdleSize: 1 subscriptionConnectionPoolSize: 50 slaveConnectionMinimumIdleSize: 24 slaveConnectionPoolSize: 64 masterConnectionMinimumIdleSize: 24 masterConnectionPoolSize: 64 readMode: "SLAVE" subscriptionMode: "SLAVE" nodeAddresses: - "redis://127.0.0.1:7004" - "redis://127.0.0.1:7001" - "redis://127.0.0.1:7000" scanInterval: 1000 pingConnectionInterval: 0 keepAlive: false tcpNoDelay: false threads: 16 nettyThreads: 32 codec: !<org.redisson.codec.MarshallingCodec> {} transportMode: "NIO"
封裝Redisson工具類
@Component public class RedissonLockUtil { private static final Logger logger = LoggerFactory.getLogger(RedissonLockUtil.class); @Autowired private RedissonClient redissonClient; /** * 加鎖 * @param lockKey * @return */ public RLock lock(String lockKey) { RLock lock = redissonClient.getLock(lockKey); return lock; } /** * 公平鎖 * @param key * @return */ public RLock fairLock(String key) { return redissonClient.getFairLock(key); } /** * 帶超時(shí)的鎖 * @param lockKey * @param timeout 超時(shí)時(shí)間 單位:秒 */ public RLock lock(String lockKey, int timeout) { RLock lock = redissonClient.getLock(lockKey); lock.lock(timeout, TimeUnit.SECONDS); return lock; } /** * 讀寫鎖 * @param key * @return */ public RReadWriteLock readWriteLock(String key) { return redissonClient.getReadWriteLock(key); } /** * 帶超時(shí)的鎖 * @param lockKey * @param unit 時(shí)間單位 * @param timeout 超時(shí)時(shí)間 */ public RLock lock(String lockKey, TimeUnit unit ,int timeout) { RLock lock = redissonClient.getLock(lockKey); lock.lock(timeout, unit); return lock; } /** * 加鎖 * @param key * @param supplier * @return */ public <T> T lock(String key, Supplier<T> supplier) { RLock lock = lock(key); try { lock.lock(); return supplier.get(); } finally { if (lock != null && lock.isLocked()) { lock.unlock(); } } } /** * 嘗試獲取鎖 * @param lockKey * @param waitTime 等待時(shí)間 * @param leaseTime 自動釋放鎖時(shí)間 * @return */ public boolean tryLock(String lockKey, int waitTime, int leaseTime) { RLock lock = redissonClient.getLock(lockKey); try { return lock.tryLock(waitTime, leaseTime, TimeUnit.SECONDS); } catch (InterruptedException e) { return false; } } /** * 嘗試獲取鎖 * @param lockKey * @param unit 時(shí)間單位 * @param waitTime 等待時(shí)間 * @param leaseTime 自動釋放鎖時(shí)間 * @return */ public boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime) { RLock lock = redissonClient.getLock(lockKey); try { return lock.tryLock(waitTime, leaseTime, unit); } catch (InterruptedException e) { return false; } } /** * 釋放鎖 * @param lockKey */ public void unlock(String lockKey) { RLock lock = redissonClient.getLock(lockKey); lock.unlock(); } /** * 釋放鎖 * @param lock */ public void unlock(RLock lock) { lock.unlock(); } }
模擬秒殺扣減庫存
public int lockStock() { String lockKey="lock:stock"; String clientId = UUID.randomUUID().toString(); //加鎖 RLock lock=redissonLockUtil.lock(lockKey); lock.lock(); try { logger.info("加鎖成功 clientId:{}",clientId); int stockNum= Integer.valueOf((String)redisUtil.get("seckill:goods:stock")); if(stockNum>0) { stockNum--; redisUtil.set("seckill:goods:stock",String.valueOf(stockNum)); logger.info("秒殺成功,剩余庫存:{}",stockNum); } else { logger.error("秒殺失敗,剩余庫存:{}", stockNum); } //獲取庫存數(shù)量 return stockNum; } catch (Exception e) { logger.error("decry stock eror",e); } finally { if(lock!=null) { lock.unlock(); } } return 0; }
測試代碼
@RequestMapping("/redisLockTest") public void redisLockTest() { // 初始化秒殺庫存數(shù)量 redisUtil.set("seckill:goods:stock", "10"); List<Future> futureList = new ArrayList<>(); //多線程異步執(zhí)行 ExecutorService executors = Executors.newScheduledThreadPool(10); // for (int i = 0; i < 30; i++) { futureList.add(executors.submit(this::lockStock)); try { Thread.sleep(100); } catch (InterruptedException e) { logger.error("redisLockTest error",e); } } // 等待結(jié)果,防止主線程退出 futureList.forEach(t -> { try { int stockNum =(int) t.get(); logger.info("庫存剩余數(shù)量:{}",stockNum); } catch (Exception e) { logger.error("get stock num error",e); } }); }
執(zhí)行結(jié)果如下:
總結(jié)
本文針對Spring Boot集成Redisson的基本使用,關(guān)于Redisson源碼的分析將在后續(xù)的文章中進(jìn)行講解,如有疑問,請隨時(shí)反饋,
- SpringBoot利用注解來實(shí)現(xiàn)Redis分布式鎖
- springboot 集成redission 以及分布式鎖的使用詳解
- SpringBoot之使用Redis實(shí)現(xiàn)分布式鎖(秒殺系統(tǒng))
- Redis分布式鎖升級版RedLock及SpringBoot實(shí)現(xiàn)方法
- SpringBoot整合Redis正確的實(shí)現(xiàn)分布式鎖的示例代碼
- SpringBoot使用Redis實(shí)現(xiàn)分布式鎖
- SpringBoot使用Redisson實(shí)現(xiàn)分布式鎖(秒殺系統(tǒng))
- SpringBoot集成Redisson實(shí)現(xiàn)分布式鎖的方法示例
- springboot+redis分布式鎖實(shí)現(xiàn)模擬搶單
- 適合 Spring Boot 3.0x的Redis 分布式鎖詳解
相關(guān)文章
如何使用try-with-resource機(jī)制關(guān)閉連接
這篇文章主要介紹了使用try-with-resource機(jī)制關(guān)閉連接的操作,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07Java虛擬機(jī)JVM之server模式與client模式的區(qū)別
這篇文章主要介紹了Java虛擬機(jī)JVM的client模式和Server模式兩者的區(qū)別和聯(lián)系2017-12-12Spring?boot2.0?實(shí)現(xiàn)日志集成的方法(2)
這篇文章主要介紹了Spring?boot2.0?實(shí)現(xiàn)日志集成的方法,上一章講解了spring?boot日志簡單集成,這篇我們將日志進(jìn)行分類,常規(guī)日志、異常日志、監(jiān)控日志等,需要將日志輸出到不同的文件,具體內(nèi)容需要的小伙伴可以參考一下2022-04-04SpringBoot利用jpa連接MySQL數(shù)據(jù)庫的方法
這篇文章主要介紹了SpringBoot利用jpa連接MySQL數(shù)據(jù)庫的方法,本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-10-10