使用redis整合springboot实现分布式锁:
分布式锁:在对图片进行标注的时候,为了保证数据一致性的问题,只能有一个用户进行操作标记,其他的用户等到当前用户取消对图片的操作之后才能进行访问。
整体的处理逻辑: 首先创建锁的实体MathodLock,添加用户图片等属性。
在图片控制类中将判断是否获得锁并且加到表
boolean flag = redisService.lock(String.valueOf(id),String.valueOf(userId));
if (flag){
MethodLock methodLock = new MethodLock();
methodLock.setUserId(userId);
methodLock.setMethod("pictureInfo");
methodLock.setStatus(0);
methodLock.setPicId(id);
methodLockService.add(methodLock);
}
当用户解除对图片的操作之后解除锁。
MethodLock methodLock = methodLockMapper.getState(userId,"pictureInfo",id);
if (methodLock != null){
int state = methodLock.getStatus();
if (state == 0){
redisService.unlock("picId-" + String.valueOf(id),"userId-" + String.valueOf(userId));
methodLock.setStatus(1);//状态修改成解锁状态
methodLockService.update(methodLock);//更新方法锁的状态
}
首先更新配置文件RediConfig
@Slf4j
@EnableCaching
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private int port;
@Value("${spring.redis.jedis.timeout}")
private int timeout;
@Value("${spring.redis.jedis.pool.max-idle}")
private int maxIdle;
@Value("${spring.redis.jedis.pool.max-wait}")
private long maxWaitMillis;
@Value("${spring.redis.password}")
private String password;
@Value("${spring.redis.database}")
private int database;
@Bean
public JedisPool redisPoolFactory() {
log.info("JedisPool注入成功!!");
log.info("redis地址:" + host + ":" + port);
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxIdle(maxIdle);
jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout, password, database);
return jedisPool;
}
@Bean
@SuppressWarnings(value = { "unchecked", "rawtypes" })
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory)
{
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
serializer.setObjectMapper(mapper);
template.setValueSerializer(serializer);
// 使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
template.afterPropertiesSet();
return template;
}
}
首先要继承CachingConfigurerSupport类定义自己的配置类名,在配置类中定义JedisPool线程池JedissPool是一个线程安全的网络连接池,我们可以通过JedisPool创建和管理Jedis实例
spring-data-redis针对jedis提供了如下功能:
连接池自动管理,提供了一个高度封装的“RedisTemplate”类针对jedis客户端中大量api进行了归类封装,将同一类型操作封装为operation接口
ValueOperations:简单K-V操作
SetOperations:set类型数据操作
ZSetOperations:zset类型数据操作
HashOperations:针对map类型的数据操作
ListOperations:针对list类型的数据操作
过JedisPool创建和管理Jedis实例
Long result = jedis.setnx(key, value);返回值result :设置成功,返回 1 。设置失败,返回 0
expire是设置redis过期时间的命令单位为秒
实现RedisService服务类
@Service
@Slf4j
public class RedisService {
// private String lock_key = "redis_lock";//锁键
protected long internalLockLeaseTime = 3000;
private long timeOut = 999999;//获取锁的超时时间
private int expireTime = 5 * 60;
// //SET命令的参数
SetParams params = SetParams.setParams().nx().px(internalLockLeaseTime);
// private String NX = "NX";
//
// private String EX = "EX";
//
// private int expireTime = 3000;
@Autowired
JedisPool jedisPool;
/**
* 加锁
* @param picId key
* @param userId value
* @return
* */
public boolean lock(String picId,String userId) {
Jedis jedis = jedisPool.getResource();
Long start = System.currentTimeMillis();
try {
for (;;){
//SET命令返回ok,则证明获取锁成功
// String lock = jedis.set("picId-" + picId, "userId-" + userId, params);
// String key = jedis.get("picId-" + picId);
// System.out.println(key);
// if ("OK".equals(lock)){
// return true;
// }
//首先检查是否持锁
if (jedis.exists("picId-" + picId) ){
if (jedis.get("picId-" + picId).equals("userId-" + userId)){
return true;
}else {
//如果不是当前用户返回持有锁,直接返回
return false;
}
}
//给picture持有锁
if (jedis.setnx("picId-" + picId, "userId-" + userId) == 1){
jedis.expire("picId-" + picId,expireTime);
return true;
}
//否则循环等待,在timeout时间内仍未获取到锁,则获取失败
long l = System.currentTimeMillis() - start;
if (l >= timeOut){
return false;
}
try {
Thread.sleep(100);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}finally {
jedis.close();
}
}
/**
* @param picId key
* @param userId value
* @return 是否释放锁的boolean值
* */
public boolean unlock(String picId,String userId){
Jedis jedis = jedisPool.getResource();
String script = "if redis.call('get',KEYS[1]) == ARGV[1] then" +
" return redis.call('del',KEYS[1]) " +
"else" +
" return 0 " +
"end";
try{
Object result = jedis.eval(script, Collections.singletonList(picId),Collections.singletonList(userId));
if ("1".equals(result.toString())){
return true;
}
return false;
}finally {
jedis.close();
}
}
}
2119

被折叠的 条评论
为什么被折叠?



