1:什么是缓存分布式锁
首先这是一个锁 那么就是应对并发使用的
然后它是分布式 那意味着这个锁可以在一个服务上锁 然后锁住另一个服务的逻辑
最后它是缓存 那代表着这个锁效率十分快同时具有失效的时间
可应用于防止用户重复下单
2:分布式锁的关键代码
//主要有两点是非常核心的
//1:根据key 判断该锁是否已经存在了
//2:该key需要设置过期时间
@Slf4j
@SpringBootTest
@RunWith(SpringRunner.class)
public class RedisDistributedLockJunit {
@Autowired
StringRedisTemplate redisTemplate;
@Test
public void redisLockTest() {
RedisConnection connection = null;
connection = redisTemplate.getConnectionFactory().getConnection();
String key = "member:name:HSJ";
byte[] content ="content".getBytes();
//关键点1:如果该key已经存在 则返回false 同时不会修改其内容
boolean isExist = connection.setNX(key.getBytes(), content);
System.out.println("是否已经存在:"+isExist);
//注意保证使用的key是存在的
//redisTemplate.expire(key,timeout,timeunit);
//参数说明 key 需要设置的key timeout:key的生存时间 timeuint:时间单位(小时,分钟,秒……)
//TimeUnit.MILLISECONDS 是毫秒
// 关键点2:重置过期时间
redisTemplate.expire(key, 15000L, TimeUnit.MILLISECONDS);
//获取过期时间
System.out.println("获取过期时间:"+redisTemplate.getExpire(key));
}
}
3:业务代码使用分布式缓存锁
@RestController
@RequestMapping("/order")
@Slf4j
public class RedisDistributedLockController {
@Autowired
private RedisDistributedLockComponent redisDistributedLockComponent;
@PostMapping(value = "/member/create")
public String orderPay(Long memberId) {
String memberOrderLockKey = "member:createOrder:" + memberId;
//该锁会锁住这个下单的用户ID5秒
boolean memberLock = redisDistributedLockComponent.getLock(memberOrderLockKey, 2000L, 5000L);
//如果memberLock 为 false 则失败
if (!memberLock) {
return "fail 请不要重复下单";
}
try {
//下单耗时3秒操作
System.out.println(new Date());
Thread.sleep(3000L);
System.out.println(new Date());
} catch (Exception e) {
log.error("创建预授权订单,用户ID:{},异常:{}", memberId, e.getMessage(), e);
} finally {
boolean isRelease = redisDistributedLockComponent.releaseLock(memberOrderLockKey);
if (!isRelease) {
log.error("该key的分布式锁释放失败{}", memberOrderLockKey);
}
}
return "success 下单成功";
}
}
4:业务代码使用分布式缓存锁
@Component
@Slf4j
public class RedisDistributedLockComponent {
@Autowired
private StringRedisTemplate stringRedisTemplate;
/**
* 分布式锁
*/
// public class DistributedLock {
//锁超时时间 4秒
private static final long DEFAULT_LOCK_TIME_OUT = 4000L;
//请求超时时间5秒
private static final long DEFAULT_ACQUIRE_TIME_OUT = 5000L;
/**
* 获取锁
* @param key
* @param acquireTimeout 不断重试添加锁 直到超时 或者成功
* @param lockTimeOut 如果添加锁成功 该锁的有效时间
* @return
*/
public boolean getLock(String key, long acquireTimeout, long lockTimeOut) {
//初始化锁为 false
boolean lock = false;
key = "lock:" + key;
long now = System.currentTimeMillis();
acquireTimeout = now + acquireTimeout;
//如果入参acquireTimeout 小于等于 0 则使用默认值
if (0L>= acquireTimeout ) {
acquireTimeout = now + DEFAULT_ACQUIRE_TIME_OUT;
}
//如果入参lockTimeOut 小于等于 0 则使用默认值
if (0 >= lockTimeOut) {
lockTimeOut = DEFAULT_LOCK_TIME_OUT;
}
//只要是字符串就行了content
byte[] content ="content".getBytes();
RedisConnection connection = null;
try {
//获取redis的连接
connection = stringRedisTemplate.getConnectionFactory().getConnection();
//在acquireTimeout(请求锁超时时间内) 一直尝试添加锁 200L(0.2秒) 一次
//超过时间则添加锁失败
do {
//原子性 SET if Not Exists
if (connection.setNX(key.getBytes(), content)) {
//设置该key对应的锁超时时间
stringRedisTemplate.expire(key, lockTimeOut, TimeUnit.MILLISECONDS);
//设置锁为true
lock = true;
break;
}
Thread.sleep(200L);
log.info("key : {} 等待锁", key);
} while (System.currentTimeMillis() <= acquireTimeout);
} catch (Exception e) {
log.error("获取锁异常", e);
return false;
} finally {
if (null != connection) {
connection.close();
}
}
return lock;
}
/**
* 释放锁
* @param key
* @return
*/
public boolean releaseLock(String key) {
boolean releaseLock = false;
key = "lock:" + key;
try {
stringRedisTemplate.delete(key);
releaseLock = true;
} catch (Exception e) {
log.error("释放锁异常", e);
return false;
}
return releaseLock;
}
}
5:测试
使用postman 或者 Jmeter连续调用两次该接口就行
先进来的获取分布式锁 该锁会锁住5秒 5秒内不会有其他人无法获取锁
后进来的不断尝试获取锁 在2秒内 每0.2秒尝试获取一次
执行逻辑的时间是3秒 执行逻辑这段时间锁(5秒)还在 重复请求无法重复进入该逻辑
项目连接
请配合项目代码食用效果更佳:
项目地址:
https://github.com/hesuijin/hesuijin-study-project
Git下载地址:
https://github.com.cnpmjs.org/hesuijin/hesuijin-study-project.git
redis-module项目模块下
网友评论