分布式锁实现
引入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>
说明:关于集成Redisson,我们需要注意与Spring Boot的版本对应。具体对应的关系如下:
注意:3.13.6对应的Spring Boot的版本为2.3.0,而redis-spring-data为redis-spring-data-23。我们可以通过查看pom文件的引用从而得到依赖关系。
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"
说明:本文配置的是单机环境,如果需要配置集群环境,可以采用如下配置:
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工具类
@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);
}
/**
* 带超时的锁
* @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> 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 等待时间
* @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<Future> 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的基本使用。