这是常规 Redisson 使用代码
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Component
public class RedissonDemo {
private static final Logger logger = LoggerFactory.getLogger(RedissonDemo.class);
@Autowired
private RedissonClient redissonClient;
public void redissonDemo() {
String lockKey = "自定义一个key(一般是用户Id锁自己的操作)";
RLock redissonLock = redissonClient.getLock(lockKey);
boolean isLocked = false;
try {
// 尝试获取锁,等待时间10秒,锁定时间30秒
isLocked = redissonLock.tryLock(10, 30, TimeUnit.SECONDS);
if (isLocked) {
// TODO 执行业务操作
logger.info("成功获取锁,执行业务操作");
} else {
logger.warn("加锁失败!");
}
} catch (InterruptedException e) {
logger.error("加锁过程中发生异常", e);
Thread.currentThread().interrupt();
} finally {
if (isLocked) {
redissonLock.unlock();
logger.info("锁已释放");
}
}
}
}
idea查看trylock方法 其是一个接口,我们去找具体实现RedisonLock类的发现其调用
@Override
public boolean tryLock() {
return get(tryLockAsync());
}
<T> RFuture<T> tryLockInnerAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {
return evalWriteAsync(getRawName(), LongCodec.INSTANCE, command,
"if (redis.call('exists', KEYS[1]) == 0) then " +
"redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
"redis.call('pexpire', KEYS[1], ARGV[1]); " +
"return nil; " +
"end; " +
"if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +
"redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
"redis.call('pexpire', KEYS[1], ARGV[1]); " +
"return nil; " +
"end; " +
"return redis.call('pttl', KEYS[1]);",
Collections.singletonList(getRawName()), unit.toMillis(leaseTime), getLockName(threadId));
}
原码讲解:
- KEYS[1]:Collections.singletonList(getRawName()) 是Redis中存的键名,不是key名。来自于redisson.getLock(lockKey)时指定的名字
- ARGV[1]:unit.toMillis(leaseTime) 锁的持有时间
- ARGV[2]:getLockName(threadId) key的名字
- hincrby Redis的Hash的键自增
- pexpire 设定过期时间
整体讲解
- 首先判断键不存在,直接指定为1,并加入过期时间,加锁成功,返回空。
- 如果键存在且key等于1时,加锁成功,返回空。
- 上述条件不满足,直接返回键的过期时间。
上述就是lua的加锁脚本。再去看看释放锁的源码(也再RedisonLock类中):
protected RFuture<Boolean> unlockInnerAsync(long threadId) {
return evalWriteAsync(getRawName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
"if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then " +
"return nil;" +
"end; " +
"local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); " +
"if (counter > 0) then " +
"redis.call('pexpire', KEYS[1], ARGV[2]); " +
"return 0; " +
"else " +
"redis.call('del', KEYS[1]); " +
"redis.call('publish', KEYS[2], ARGV[1]); " +
"return 1; " +
"end; " +
"return nil;",
Arrays.asList(getRawName(), getChannelName()), LockPubSub.UNLOCK_MESSAGE, internalLockLeaseTime, getLockName(threadId));
}
大致原理太多太复杂了,不搞了!
反正整体来看的原理就是
- 重入性:记录线程id与重入次数
- 重试性:利用Redis的Pub/Sub 实现等待、唤醒、获取锁失败的重试机制
解决Redis集群的主从一致性原理
Redis主从在切换时,会导致数据不一致。如果在切换异常期间就会导致解锁失败等情况。
Redisson解决办法时MuiltLock。就是向所有节点加锁,当所有节点锁加成功后,才算加锁成功!避免了数据不一致性!
特殊说明:
上述文章均是作者实际操作后产出。烦请各位,请勿直接盗用!转载记得标注原文链接:www.zanglikun.com
第三方平台不会及时更新本文最新内容。如果发现本文资料不全,可访问本人的Java博客搜索:标题关键字。以获取最新全部资料 ❤
第三方平台不会及时更新本文最新内容。如果发现本文资料不全,可访问本人的Java博客搜索:标题关键字。以获取最新全部资料 ❤