阿里大牛详细讲解:Spring Boot 集成 Redisson 实现分布式锁

朱雀 2023-09-30 20:53 56阅读 0赞

针对单机分布式锁还是存在锁定续期、可重入的问题,本文将采用 Spring Boot 集成 Ression 实现分布式锁进行详细讲解。

分布式锁实现

引入 jar 包

  1. <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>

说明:关于集成 Redisson,我们需要注意与 Spring Boot 的版本对应。具体对应的关系如下:

format_png

注意:3.13.6 对应的 Spring Boot 的版本为 2.3.0,而 redis-spring-data 为 redis-spring-data-23。我们可以通过查看 pom 文件的引用从而得到依赖关系。

Redisson 的配置

application.yml 中引入 redisson.yml 配置

  1. redis: redisson: file: classpath:redisson.yml

redisson.yml 配置

  1. singleServerConfig: password: xxxx address: "redis://127.0.0.1:6379" database: 1threads: 0nettyThreads: 0codec: !<org.redisson.codec.FstCodec> {}transportMode: "NIO"

说明:本文配置的是单机环境,如果需要配置集群环境,可以采用如下配置:

  1. 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: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: !<org.redisson.codec.MarshallingCodec> {} transportMode: "NIO"

封装 Redisson 工具类

  1. @Componentpublic class RedissonLockUtil{
  2. private static final Logger logger = LoggerFactory.getLogger(RedissonLockUtil.class); @Autowired private RedissonClient redissonClient; /** * 加锁 * @param lockKey * @return */ public RLock lock(String lockKey) {
  3. RLock lock = redissonClient.getLock(lockKey); return lock; } /** * 公平锁 * @param key * @return */ public RLock fairLock(String key) {
  4. return redissonClient.getFairLock(key); } /** * 带超时的锁 * @param lockKey * @param timeout 超时时间 单位:秒 */ public RLock lock(String lockKey, int timeout) {
  5. RLock lock = redissonClient.getLock(lockKey); lock.lock(timeout, TimeUnit.SECONDS); return lock; } /** * 读写锁 * @param key * @return */ public RReadWriteLock readWriteLock(String key) {
  6. return redissonClient.getReadWriteLock(key); } /** * 带超时的锁 * @param lockKey * @param unit 时间单位 * @param timeout 超时时间 */ public RLock lock(String lockKey, TimeUnit unit ,int timeout) {
  7. 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) {
  8. RLock lock = lock(key); try {
  9. lock.lock(); return supplier.get(); } finally {
  10. if (lock != null && lock.isLocked()) {
  11. lock.unlock(); } } }
  12. /** * 尝试获取锁 * @param lockKey * @param waitTime 等待时间 * @param leaseTime 自动释放锁时间 * @return */ public boolean tryLock(String lockKey, int waitTime, int leaseTime) {
  13. RLock lock = redissonClient.getLock(lockKey); try {
  14. return lock.tryLock(waitTime, leaseTime, TimeUnit.SECONDS); } catch (InterruptedException e) {
  15. return false; } } /** * 尝试获取锁 * @param lockKey * @param unit 时间单位 * @param waitTime 等待时间 * @param leaseTime 自动释放锁时间 * @return */ public boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime) {
  16. RLock lock = redissonClient.getLock(lockKey); try {
  17. return lock.tryLock(waitTime, leaseTime, unit); } catch (InterruptedException e) {
  18. return false; } } /** * 释放锁 * @param lockKey */ public void unlock(String lockKey) {
  19. RLock lock = redissonClient.getLock(lockKey); lock.unlock(); } /** * 释放锁 * @param lock */ public void unlock(RLock lock) {
  20. lock.unlock(); } }

模拟秒杀扣减库存

  1. public int lockStock() {
  2. String lockKey="lock:stock"; String clientId = UUID.randomUUID().toString(); //加锁 RLock lock=redissonLockUtil.lock(lockKey); lock.lock(); try {
  3. 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 {
  4. logger.error("秒杀失败,剩余库存:{}", stockNum); } //获取库存数量 return stockNum; } catch (Exception e) {
  5. logger.error("decry stock eror",e); } finally {
  6. if(lock!=null) {
  7. lock.unlock(); } } return 0; }

测试代码

  1. @RequestMapping("/redisLockTest") public void redisLockTest() {
  2. // 初始化秒杀库存数量 redisUtil.set("seckill:goods:stock", "10"); List<Future> futureList = new ArrayList<>(); //多线程异步执行 ExecutorService executors = Executors.newScheduledThreadPool(10); // for (int i = 0; i < 30; i++) {
  3. futureList.add(executors.submit(this::lockStock)); try {
  4. Thread.sleep(100); } catch (InterruptedException e) {
  5. logger.error("redisLockTest error",e); } } // 等待结果,防止主线程退出 futureList.forEach(t -> {
  6. try {
  7. int stockNum =(int) t.get(); logger.info("库存剩余数量:{}",stockNum); } catch (Exception e) {
  8. logger.error("get stock num error",e); } }); }

执行结果如下:

format_png 1

总结

本文针对 Spring Boot 集成 Redisson 的基本使用,关于 Redisson 源码的分析将在后续的文章中进行讲解,如有疑问,请随时反馈。

发表评论

表情:
评论列表 (有 0 条评论,56人围观)

还没有评论,来说两句吧...

相关阅读