欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

springboot利用redisson实现分布式锁

程序员文章站 2022-04-15 17:46:39
最近开发一了个答题抽奖项目,由于部署项目采用了负载均衡策略,分配奖品时必须使用分布式锁,项目开发完成后记录一下利用redisson实现分布式锁的过程一、springboot项目整合redissonredisson pom依赖如下 org.redisson redisson

最近开发一了个答题抽奖项目,由于部署项目采用了负载均衡策略,分配奖品时必须使用分布式锁,项目开发完成后记录一下利用redisson实现分布式锁的过程

一、springboot项目整合redisson

  • redisson pom依赖如下,springboot为2.x,如果是更底版本,redisson可能也需要换成更低的版本。
 <!--Redis分布式锁--> <dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.11.0</version> </dependency> 
  • yml配置文件中的redis配置如下
spring: redis: host: localhost port: 3306 timeout: 6000 database: 5 password: '123456' lettuce: pool: max-active: -1 #最大连接数 max-idle: 8 #最大空闲数 max-wait: 15000 #最大连接阻塞等待时间 min-idle: 0 #最小空闲数 
  • 配置RedissonClient

创建RedisProperties类,装配redis配置

import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component @ConfigurationProperties(prefix = "spring.redis") public class RedisProperties { private String host; private int port; private String password; private int database; public String getHost() { return host; } public void setHost(String host) { this.host = host; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public int getDatabase() { return database; } public void setDatabase(int database) { this.database = database; } } 

然后创建RedissionConfig类,配置RedissonClient

 import org.apache.log4j.Logger; import org.redisson.Redisson; import org.redisson.api.RedissonClient; import org.redisson.config.Config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class RedissionConfig { @Autowired private RedisProperties redisProperties; private Logger logger = Logger.getLogger(this.getClass()); @Bean public RedissonClient redissonClient() { RedissonClient redissonClient; Config config = new Config(); String url = "redis://" + redisProperties.getHost() + ":" + redisProperties.getPort(); config.useSingleServer().setAddress(url) .setPassword(redisProperties.getPassword()) .setDatabase(redisProperties.getDatabase()); try { redissonClient = Redisson.create(config); return redissonClient; } catch (Exception e) { logger.error("RedissonClient init redis url:[{}], Exception:" + url); return null; } } } 

到此Redisson就整合完成了

二、利用Redisson实现分布式锁

创建DistributedRedisLock类,代码如下

import java.util.concurrent.TimeUnit; import org.apache.log4j.Logger; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /**
 * @title 分布式redis锁
 * @author gavin
 * @date 2020年7月10日
 */ @Component public class DistributedRedisLock { @Autowired private RedissonClient redissonClient; private Logger logger = Logger.getLogger(this.getClass()); // 加锁 public Boolean lock(String lockName) { try { if (redissonClient == null) { logger.info("redissonClient为空"); return false; } RLock lock = redissonClient.getLock(lockName); // 锁30秒后自动释放,防止死锁 lock.lock(30, TimeUnit.SECONDS); logger.info("线程" + Thread.currentThread().getName() + "加锁" + lockName + "成功"); // 加锁成功 return true; } catch (Exception e) { logger.error("加锁异常:" + lockName); e.printStackTrace(); return false; } } // 释放锁 public Boolean unlock(String lockName) { try { if (redissonClient == null) { logger.info("redissonClient为空"); return false; } RLock lock = redissonClient.getLock(lockName); if (lock.isLocked()) { if (lock.isHeldByCurrentThread()) { // 主动释放锁 lock.unlock(); logger.info("线程" + Thread.currentThread().getName() + "解锁" + lockName + "成功"); return true; } } return true; } catch (Exception e) { logger.error(Thread.currentThread().getName() + "解锁异常:" + lockName); e.printStackTrace(); return false; } } } 

DistributedRedisLock 类中有两个方法,lock是加锁方法,unlock是释放锁方法。其中有两个点需要注意

  1. lock.lock(30, TimeUnit.SECONDS),这行代码的作用是,如果加锁的业务代码运行时间超过了30秒,便会自动释放锁,这是为防止业务代码运行时间过长或者出错导致死锁。
  2. lock.isHeldByCurrentThread(),在释放锁时,这行判断必须要加上,它会判断当前线程释放的锁是否属于当前线程,防止解锁错乱。

三、测试分布式锁

我采用的测试逻辑是,对redis中一个值为100的变量,使用100线程同时对该变量进行减1操作,看最后变量的值是否为0,如果为0,则分布式锁有效。

创建TestLockController测试类,代码如下

import java.util.concurrent.CountDownLatch; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("lock") public class TestLockController { @Autowired private DistributedRedisLock distributedRedisLock; // redision分布式锁id private static String lock = "lock_id"; // 测试数据key private static String dataKey = "lock_count"; // 测试数据value private int count = 100; // 模拟线程数 private int threadNumber = 100; @Autowired private StringRedisTemplate stringRedisTemplate; /**
	 * @title 在redis中初始化设置lock_count=100,用于测试
	 * @author gavin
	 * @date 2020年8月26日
	 */ @GetMapping("lockInit") public Object testInit() { this.stringRedisTemplate.opsForValue().set(dataKey, String.valueOf(this.count)); return this.stringRedisTemplate.opsForValue().get(dataKey); } /**
	 * @title 测试不加锁
	 * @author gavin
	 * @date 2020年8月26日
	 * @return
	 * @throws InterruptedException
	 */ @GetMapping("/testNoLock") public Object testNoLock() throws InterruptedException { // 多线程计数器 CountDownLatch latch = new CountDownLatch(this.threadNumber); for(int i=0;i<this.threadNumber;i++) { new Thread(new Runnable() { @Override public void run() { String valueStr = stringRedisTemplate.opsForValue().get(dataKey); int value = Integer.valueOf(valueStr); value = value - 1; stringRedisTemplate.opsForValue().set(dataKey, String.valueOf(value)); // 每个线程执行完毕,计数器减1 latch.countDown(); } }).start(); } // 在此阻塞,等待所有线程结束 latch.await(); System.out.println(this.threadNumber + "个线程全部执行完毕"); // 返回最终的dataKey return stringRedisTemplate.opsForValue().get(dataKey); } /**
	 * @title 测试分布式锁
	 * @author gavin
	 * @date 2020年8月26日
	 * @return
	 * @throws InterruptedException
	 */ @GetMapping("/testUseLock") public Object testLock() throws InterruptedException { // 多线程计数器 CountDownLatch latch = new CountDownLatch(this.threadNumber); for(int i=0;i<this.threadNumber;i++) { new Thread(new Runnable() { @Override public void run() { boolean lockFlag = distributedRedisLock.lock(lock); // 加分布式锁 if(lockFlag) { String valueStr = stringRedisTemplate.opsForValue().get(dataKey); int value = Integer.valueOf(valueStr); value = value - 1; stringRedisTemplate.opsForValue().set(dataKey, String.valueOf(value)); // 加锁 distributedRedisLock.unlock(lock); // 每个线程执行完毕,计数器减1 latch.countDown(); } } }).start(); } // 在此阻塞,等待所有线程结束 latch.await(); System.out.println(this.threadNumber + "个线程全部执行完毕"); // 返回最终的dataKey return stringRedisTemplate.opsForValue().get(dataKey); } } 

测试类中有三个方法,分别是

  1. lockInit方法,向redis中存储一个key为lock_count,值为100的变量
  2. testNoLock方法,不加分布式锁进行测试
  3. testLock方法,使用分布式锁进行测试

启动项目,打开postman,首先执行lockInit,在redis中初始化变量lock_count=100
springboot利用redisson实现分布式锁
然后我们先测试一下不加分布式锁的testNoLock方法,运行结果如下图
springboot利用redisson实现分布式锁
可以看到,lock_count最终的值为98,说明100个线程运行过程中出现了并发。

再次执行lockInit方法,使lock_count值为100,然后再运行testLock方法,运行结果如下图
springboot利用redisson实现分布式锁
可以看到,lock_count的值为0,说明没有出现并发,分布式锁生效了。

本文地址:https://blog.csdn.net/u012693016/article/details/108244005