redis实现分布式锁
环境:
springboot 2.0.4
maven
一、在pom中添加依赖
|
二、在application.yml下填写redis的配置
|
spring.redis.host: 127.0.0.1 spring.redis.database: 0 |
三、 自定义redistemplate,指定序列化的方式,防止后面出现序列化的错误
@Configuration
public class RedisConf {
@Bean
public RedisTemplate<String, Object> redisTemplate( RedisConnectionFactory factory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
redisTemplate.setConnectionFactory(factory);
RedisSerializer stringRedisSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringRedisSerializer);
redisTemplate.setValueSerializer(stringRedisSerializer);
redisTemplate.setHashKeySerializer(stringRedisSerializer);
redisTemplate.setHashValueSerializer(stringRedisSerializer);
return redisTemplate;
}
}
四、实现方法
/*
* redis高并发击穿的处理,采用分布式锁
* 击穿是指:就是某一个热点数据,缓存中某一时刻失效了,因而大量并发请求打到数据库上,就像被击穿了一样。说白了,就是某个数据,数据库有,但是缓存中没有。
* */
@RestController
@CrossOrigin
public class LogFacade {
private static Logger logger = LoggerFactory.getLogger(LogFacade.class);
@Resource
private RedisTemplate redisTemplate;
private static final Long SUCCESS = 1L;
@GetMapping("/getTest")
public String getTest() throws InterruptedException {
// 从redis中获取一个值
String str = (String) redisTemplate.opsForValue().get("1");
JSONObject result = null;
// 当值被获取且不为空,直接返回结果
if(!StringUtils.isEmpty(str)){
return str;
}else{
String uuid = UUID.randomUUID().toString();
//获取锁
Boolean lock = getLock("lock",uuid,100);
System.out.println("lock"+lock);
//加锁成功则进行相关操作
if(lock == true){
再次尝试从redis中取值,不为空则返回
String str2 = (String) redisTemplate.opsForValue().get("1");
if(!StringUtils.isEmpty(str2)){
releaseLock("lock",uuid);
return str2;
}
JSONObject obj = ... // 从数据库获取数据
System.out.println("击穿至数据库");
SetParams setParams = new SetParams();
setParams.ex(10);
//数据保存至redis
redisTemplate.opsForValue().set("1",obj.toJSONString(),100, TimeUnit.SECONDS);
result = obj;
// 解锁
Boolean unlock = releaseLock("lock",uuid);
System.out.println("unlock"+unlock);
return result.toJSONString();
}else{
// 未能获取锁的,在等待一会后进行重试
Thread.sleep(500);
return getTest();
}
}
}
// 注意采用这种方式去创建锁是为了保证锁的原子性,避免出现死锁(重要)
/**
* 获取锁
* @param lockKey
* @param value
* @param expireTime:单位-秒
* @return
*/
public boolean getLock(String lockKey, String value, int expireTime){
boolean ret = false;
try{
String script = "if redis.call('setNx',KEYS[1],ARGV[1]) then if redis.call('get',KEYS[1])==ARGV[1] then return redis.call('expire',KEYS[1],ARGV[2]) else return 0 end end";
RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
Object result = redisTemplate.execute(redisScript, Collections.singletonList(lockKey),value, expireTime+"");
if(SUCCESS.equals(result)){
return true;
}
}catch(Exception e){
e.printStackTrace();
}
return ret;
}
// 解锁时保证加锁人和解锁人是一致的
/**
* 释放锁
* @param lockKey
* @param value
* @return
*/
public boolean releaseLock(String lockKey, String value) {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
Object result = redisTemplate.execute(redisScript, Collections.singletonList(lockKey), value);
if (SUCCESS.equals(result)) {
return true;
}
return false;
}
}

浙公网安备 33010602011771号