Redis实现分布式锁的作用和意义,可以参考这篇文章:https://zhuanlan.zhihu.com/p/268290754
代码的话,不废话,直接看我在项目中用的。
一.配置RedisTemplate对象
我是基于RedisTemplate对象来实现分布式锁的,而不是jedis。
package com.wuyouhu.common.redis.configure;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* redis配置
*
* @author lele
*/
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport
{
@Bean
@SuppressWarnings(value = { "unchecked", "rawtypes", "deprecation" })
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.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
serializer.setObjectMapper(mapper);
template.setValueSerializer(serializer);
// 使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
template.afterPropertiesSet();
return template;
}
}
二.redis分布式锁Entity类
package com.wuyouhu.common.redis.domian;
/**
* redis分布式锁Entity类
*
* @author lele
*/
public class RedisLockEntity {
private String lockKey;
private String requestId;
public RedisLockEntity() {
}
public String getLockKey() {
return lockKey;
}
public void setLockKey(String lockKey) {
this.lockKey = lockKey;
}
public String getRequestId() {
return requestId;
}
public void setRequestId(String requestId) {
this.requestId = requestId;
}
@Override
public String toString() {
return "RedisLockEntity{" +
"lockKey='" + lockKey + '\'' +
", requestId='" + requestId + '\'' +
'}';
}
}
三.lua解锁脚本代码
放在模块的resources目录下。
if redis.call('get',KEYS[1]) == ARGV[1] then
return redis.call('del',KEYS[1])
else
return 0
end
四.redis分布式锁工具类
package com.wuyouhu.common.redis.service;
import java.util.*;
import java.util.concurrent.TimeUnit;
import com.wuyouhu.common.redis.domian.RedisLockEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;
/**
* spring redis 分布式锁工具类
*
* @author lele
**/
@SuppressWarnings(value = { "unchecked", "rawtypes" })
@Component
public class RedisService
{
private final static Logger logger = LoggerFactory
.getLogger(RedisService.class);
@Autowired
public RedisTemplate redisTemplate;
/**
* 加锁,自旋重试三次
*
* @param redisLockEntity 锁实体
* @return
*/
public boolean lock(RedisLockEntity redisLockEntity) {
boolean locked = false;
int tryCount = 3;
while (!locked && tryCount > 0) {
locked = redisTemplate.opsForValue().setIfAbsent(redisLockEntity.getLockKey(), redisLockEntity.getRequestId(), 2, TimeUnit.MINUTES);
tryCount--;
try {
Thread.sleep(300);
} catch (InterruptedException e) {
logger.error("线程被中断" + Thread.currentThread().getId(),e);
}
}
return locked;
}
/**
* 非原子解锁,可能解别人锁,不安全
*
* @param redisLockEntity
* @return
*/
public boolean unlock(RedisLockEntity redisLockEntity) {
if (redisLockEntity == null || redisLockEntity.getLockKey() == null || redisLockEntity.getRequestId() == null)
return false;
boolean releaseLock = false;
String requestId = (String) redisTemplate.opsForValue().get(redisLockEntity.getLockKey());
if (redisLockEntity.getRequestId().equals(requestId)) {
releaseLock = redisTemplate.delete(redisLockEntity.getLockKey());
}
return releaseLock;
}
/**
* 使用lua脚本解锁,不会解除别人锁
*
* @param redisLockEntity
* @return
*/
public boolean unlockLua(RedisLockEntity redisLockEntity) {
if (redisLockEntity == null || redisLockEntity.getLockKey() == null || redisLockEntity.getRequestId() == null)
return false;
DefaultRedisScript<Long> redisScript = new DefaultRedisScript();
//用于解锁的lua脚本位置
redisScript.setLocation(new ClassPathResource("unlock.lua"));
redisScript.setResultType(Long.class);
//没有指定序列化方式,默认使用上面配置的
Object result = redisTemplate.execute(redisScript, Arrays.asList(redisLockEntity.getLockKey()), redisLockEntity.getRequestId());
return result.equals(Long.valueOf(1));
}
}
五.分布式锁业务使用示例
package com.wuyouhu.templates.controller;
import com.wuyouhu.common.redis.domian.RedisLockEntity;
import com.wuyouhu.common.redis.service.RedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* 分布式锁业务
*/
@RestController
@RequestMapping("/seat")
public class SeatController extends BaseController {
@Autowired
private RedisService redisService;
/**
* 更新订单业务。。。。
* @return
*/
@GetMapping(value = "/testRedisLock")
public String testRedislock1() {
System.out.println("start....lock");
RedisLockEntity redisLockEntity = new RedisLockEntity();
redisLockEntity.setLockKey("lockkey");
redisLockEntity.setRequestId("1");
boolean lock = redisService.lock(redisLockEntity);
if (lock){//如果上锁成功
System.out.println("lock success!");
// 此时只有该拿到锁(上锁)的实例可以更新订单数量......
// 业务代码
this.testRedisUnlock2()// 更新订单完成后,释放锁。
} else { // 如果上锁不成功
System.out.println("lock fail!");
//此时没拿到锁,则不能更新订单数量。可以采取自旋直到抢到锁为止再去更新订单数量;或者是执行其它业务。
// 业务代码
}
System.out.println("end.....lock");
return "success";
}
/**
* 释放锁业务
* @return
*/
public String testRedisUnlock2() {
System.out.println("start....unlock");
RedisLockEntity redisLockEntity = new RedisLockEntity();
redisLockEntity.setLockKey("lockkey");
redisLockEntity.setRequestId("1");
boolean unlock = redisService.unlockLua(redisLockEntity);
if (unlock){
System.out.println("unlock success!");
} else {
System.out.println("unlock fail!");
}
System.out.println("end....unlock");
return "success";
}
}