package com.km.config.configuration;
import com.km.exception.RedisLockException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* @author Demon-HY
* @date 2019-8-12
*/
@Configuration
@ConditionalOnClass({RedisOperations.class})
public class LockAutoConfiguration {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private StringRedisTemplate redisTemplate;
@Bean
public DistributedLock distributedLock() {
return new DistributedLock();
}
/**
* Redis 分布式锁
*/
public class DistributedLock {
/**
* 加锁
*
* @param bizCacheKey 业务 key
* @param expireSeconds 到期时间,单位秒
* @return
*/
public boolean lock(String bizCacheKey, Long expireSeconds) {
try {
String lockKey = String.format("km:lock:%s", bizCacheKey);
return redisTemplate.opsForValue().setIfAbsent(lockKey, "1", expireSeconds, TimeUnit.SECONDS);
} catch (Exception e) {
logger.error("分布式锁加锁失败, 信息:{}.", e.getMessage(), e);
return false;
}
}
/**
* 加锁,失败会抛出异常
*
* @param bizCacheKey 业务 key
* @param expireSeconds 到期时间,单位秒
* @throws RedisLockException
*/
public void lockMust(String bizCacheKey, Long expireSeconds) throws RedisLockException {
if (!this.lock(bizCacheKey, expireSeconds)) {
throw new RedisLockException();
}
}
/**
* 释放锁
*
* @param bizCacheKey 业务 key
*/
public void unlock(String bizCacheKey) {
try {
String lockKey = String.format("km:lock:%s", bizCacheKey);
redisTemplate.delete(lockKey);
} catch (Exception e) {
logger.error("分布式锁释放失败, 信息:{}.", e.getMessage(), e);
}
}
}
}
这里最核心的,是redis的 setIfAbsent() 方法,该方法会在写入值之前判断是否已经存在key,如果存在则返回false,这里使用的redis版本需要 >= 2.1.8,因为2.1.8之前的 setIfAbsent() 方法不支持设置过期时间
使用到的异常类
package com.km.exception;
/**
* @author Demon-HY
* @date 2019-8-12
*/
public class RedisLockException extends RuntimeException{
public RedisLockException() {
super("当前有其他程序正在执行,请稍后再试......");
}
public RedisLockException(String msg) {
super(msg);
}
}
利用 increment() 实现分布式锁
increment