针对单机分布式锁还是存在锁定续期、可重入的问题,本文将采用Spring Boot 集成Ression实现分布式锁进行详细讲解。
引入jar包
org.springframework.boot spring-boot-starter-data-redis io.lettuce lettuce-core org.redisson redisson-spring-boot-starter 3.13.6
说明:关于集成Redisson,我们需要注意与Spring Boot的版本对应。具体对应的关系如下:
注意:3.13.6对应的Spring Boot的版本为2.3.0,而redis-spring-data为redis-spring-data-23。我们可以通过查看pom文件的引用从而得到依赖关系。
application.yml中引入redisson.yml配置
redis: redisson: file: classpath:redisson.yml
redisson.yml配置
singleServerConfig: password: xxxx address: "redis://127.0.0.1:6379" database: 1threads: 0nettyThreads: 0codec: ! {}transportMode: "NIO"
说明:本文配置的是单机环境,如果需要配置集群环境,可以采用如下配置:
clusterServersConfig: idleConnectionTimeout: 10000 connectTimeout: 10000 timeout: 3000 retryAttempts: 3 retryInterval: 1500 failedSlaveReconnectionInterval: 3000 failedSlaveCheckInterval: 60000 password: null subscriptionsPerConnection: 5 clientName: null loadBalancer: ! {} subscriptionConnectionMinimumIdleSize: 1 subscriptionConnectionPoolSize: 50 slaveConnectionMinimumIdleSize: 24 slaveConnectionPoolSize: 64 masterConnectionMinimumIdleSize: 24 masterConnectionPoolSize: 64 readMode: "SLAVE" subscriptionMode: "SLAVE" nodeAddresses: - "redis://127.0.0.1:6379" - "redis://127.0.0.1:6380" - "redis://127.0.0.1:6381" scanInterval: 1000 pingConnectionInterval: 0 keepAlive: false tcpNoDelay: false threads: 6 nettyThreads: 12 codec: ! {} transportMode: "NIO"
@Componentpublic 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); } /** * 带超时的锁 * @param lockKey * @param timeout 超时时间 单位:秒 */ 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); } /** * 带超时的锁 * @param lockKey * @param unit 时间单位 * @param timeout 超时时间 */ 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 lock(String key, Supplier supplier) { RLock lock = lock(key); try { lock.lock(); return supplier.get(); } finally { if (lock != null && lock.isLocked()) { lock.unlock(); } } } /** * 尝试获取锁 * @param lockKey * @param waitTime 等待时间 * @param leaseTime 自动释放锁时间 * @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 时间单位 * @param waitTime 等待时间 * @param leaseTime 自动释放锁时间 * @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); } //获取库存数量 return stockNum; } catch (Exception e) { logger.error("decry stock eror",e); } finally { if(lock!=null) { lock.unlock(); } } return 0; }
@RequestMapping("/redisLockTest") public void redisLockTest() { // 初始化秒杀库存数量 redisUtil.set("seckill:goods:stock", "10"); List futureList = new ArrayList<>(); //多线程异步执行 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); } } // 等待结果,防止主线程退出 futureList.forEach(t -> { try { int stockNum =(int) t.get(); logger.info("库存剩余数量:{}",stockNum); } catch (Exception e) { logger.error("get stock num error",e); } }); }
执行结果如下:
本文针对Spring Boot集成Redisson的基本使用,关于Redisson源码的分析将在后续的文章中进行讲解,如有疑问,请随时反馈,
作者:剑圣无痕
链接:https://juejin.cn/post/7128050664336261157
来源:稀土掘金
留言与评论(共有 0 条评论) “” |