Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务。
Redisson提供了使用Redis的最简单和最便捷的方法。Redisson的宗旨是促进使用者对Redis的关注分离(Separation of Concern),从而让使用者能够将精力更集中地放在处理业务逻辑上。
二、基本使用语法:1、获取锁
RLock lock = redisson.getLock(“anyLock”);
2、加锁
最常见的使用方法:
lock.lock();
使用tryLock方法获取锁,返回是个布尔值。
boolean res = lock.tryLock();
可以设置leaseTime参数自动解锁
lock.lock(10, TimeUnit.SECONDS); //表示10秒后自动解锁
还可以指定最多等待多少时间
lock.tryLock(100, 10, TimeUnit.SECONDS); //表示尝试加锁,最多等待100秒,上锁以后10秒自动解锁
3、常用代码模板
三、应用场景boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
if (res) {
try {
…(业务逻辑)
} finally {
lock.unlock();
}
}
1:标签组同步问题
当调用新增标签组带有标签的接口时,同步到企微进行两次回调,一次是标签回调,另一次是标签组回调。两次回调都会有新增标签组的方法,并且是异步的,导致会有冲突。
期望:两次新增标签组的方法能够按顺序执行,第一个保存完后,第二个才会执行,并且第二个发现已经有标签组了直接返回。
解决方案:在方法前面加一个锁,使其两次回调都能顺序执行该方法,能保证只保存一个标签组。
@Override
public QYWXCommonResultVO groupAdd(ToolTagGroupAddDTO dto) {
RLock lock = redissonClient.getLock("lock:" + "saveQywxTagGroupId:" + dto.getTagGroupId());
if (lock.tryLock()) {
int count = sjbTagGroupService.count(new LambdaQueryWrapper()
.eq(SjbTagGroup::getQywxId, dto.getTagGroupId()).or().eq(SjbTagGroup::getName,dto.getName()));
if (count > 0) {
return QYWXCommonResultVO.success();
}
try {
SjbTagGroup tagGroup = new SjbTagGroup();
tagGroup.setName(dto.getName());
tagGroup.setOrdering(sjbTagGroupService.count() + 1);
tagGroup.setQywxId(dto.getTagGroupId());
sjbTagGroupService.save(tagGroup);
} catch (Exception e) {
log.error("保存标签组失败:" + e.getMessage());
} finally {
if (lock.isLocked()) {
lock.unlock();
}
}
}
return QYWXCommonResultVO.success();
}
2:防止并发批量注册问题
原本的注册商户方法是可以被并发请求攻击,导致能批量注册商户。
期望:在短时间内,只能有一个注册商户请求能成功,其余的都返回错误。
解决方案:注册逻辑添加读写锁,获取写锁(互斥锁),并且因为注册逻辑代码运行时间很快,所以需要添加线程休眠3秒。
@Override
public LoginVO register(String phone, String storeName, StoreTypeEnum storeType, String password, Integer terminalFlag) {
RReadWriteLock lock = redissonClient.getReadWriteLock("sjb:register:" + phone);
if (lock.writeLock().tryLock()) {
try {
Thread.sleep(3000);
……(具体注册逻辑)
} catch (InterruptedException e) {
log.error(e.getMessage());
throw new BusinessException("当前无法注册,请稍后再试");
} finally {
if (lock.writeLock().isLocked()) {
lock.writeLock().unlock();
}
}
} else {
throw new BusinessException("当前无法注册,请稍后再试");
}
}
四、源码分析
第一步:set nx
一直以为是set nx,然后客户端自旋实现。
发现并不是set nx,而源码居然是一段lua代码?
KEYS[1] 就是分布式锁的key,即REDLOCK_KEY;
ARGV[1] 就是internalLockLeaseTime,即锁的租约时间,默认30s;
ARGV[2] 就是getLockName(threadId),是获取锁时set的唯一值,即UUID+threadId:释放锁
命令:
1.hincrby:指定增量
2.pexpire:设置成功返回1,失败返回0
3.hexists:hash值在hash中是否存在
4.pttl:返回过期时间
Lua代码解释(不太熟lua,有错欢迎纠正)
第一个if end,表示key没有被锁住,直接拿到锁return null
第二个if end,表示被锁住的key是当前线程,增加重入次数(居然是可重入锁),然后return null
第三个return,表示不是上面两种情况时返回过期时间
第二步确实是自旋
如果有写错的地方,欢迎大家指正,感谢!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)