atguiguLock 锁的名字,是redis的 key
hasHkey为:
82a218d0-27c8-4028-a8ca-dfa514da61c7:71 #UUID:线程ID
value为:1。如果再次调用lock,会变成2
RLock lock = redisson.getLock(REDIS_LOCK);
lock.lock(30, TimeUnit.SECONDS);
//如果是被锁定的
if (lock.isLocked()) {
//如果是 当前线程持有的话
if (lock.isHeldByCurrentThread()) {
//才进行解锁
lock.unlock();
}
}
//1 声明一个uuid ,将做为一个value 放入我们的key所对应的值中
String uuid = UUID.randomUUID().toString();
//2 定义一个锁:lua 脚本可以使用同一把锁,来实现删除!
// 访问skuId 为25号的商品 100008348542
String locKey = "lock:" + "25"; // 锁住的是每个商品的数据
// 3 获取锁
Boolean lock = redisTemplate.opsForValue()
.setIfAbsent(locKey, uuid, 3, TimeUnit.SECONDS);
//锁的key,锁的值uuid,锁3秒
//如果这个锁存在,就不设置
Redis 集群没有使用一致性hash, 而是引入了哈希槽的概念。
Redis Cluster 采用虚拟哈希槽分区,所有的键根据哈希函数映射到 0 ~ 16383 整数槽内,每个key通过CRC16校验后对16384取模来决定放置哪个槽(Slot),每一个节点负责维护一部分槽以及槽所映射的键值数据。
计算公式:slot = CRC16(key) & 16383。
这种结构很容易添加或者删除节点,并且无论是添加删除或者修改某一个节点,都不会造成集群不可用的状态。使用哈希槽的好处就在于可以方便的添加或移除节点。
第一步,先定位 Master 的节点,
通过key,就是 redisson.getLock(“myLock”)的字符串参数,
第二步:加锁
相同的客户端线程是如何实现可重入加锁的
7:客户端尝试获取锁超时时间的机制,底层是如何实现的
8客户端锁超时自动释放机制在底层又是如何实现的
slot
英
/slɒt/
n.
(可投入东西的)狭长孔,狭槽;
v.
把……投入窄孔中,把……放到指定位置;为……安排时间,安置;
private void lock(long leaseTime, TimeUnit unit, boolean interruptibly) throws InterruptedException {
long threadId = Thread.currentThread().getId();
Long ttl = this.tryAcquire(-1L, leaseTime, unit, threadId);
}
private <T> RFuture<Long> tryAcquireAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId) {
if (leaseTime != -1L) {
return this.tryLockInnerAsync(waitTime, leaseTime, unit,
threadId, RedisCommands.EVAL_LONG);
} else {
}
}
acquire
英
/əˈkwaɪə(r)/
v.
获得,得到;学到,习得;患上(疾病);逐渐具有,开始学会
<T> RFuture<T> tryLockInnerAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {
//过期时间为 30000毫秒
this.internalLockLeaseTime = unit.toMillis(leaseTime);
return this.evalWriteAsync(this.getName(),
LongCodec.INSTANCE, command,
"if下面的 lua脚本",
Collections.singletonList(this.getName()),
this.internalLockLeaseTime, //30000毫秒
this.getLockName(threadId)
);
}
//锁名字:就是 ID:线程名。id默认是uuid
protected String getLockName(long threadId) {
return this.id + ":" + threadId;
}
//初始化 时间为30秒。3万毫秒
public Config() {
this.transportMode = TransportMode.NIO;
this.lockWatchdogTimeout = 30000L;
}
PEXPIRE key milliseconds 设置key的有效时间以毫秒为单位
#pexpire
exists num11 //不存在,返回为0
(integer) 0
PTTL key 获取key的有效毫秒数
#pttl
key为:atguiguLock
hashKey为:a4fafbec-ad68-4ee1-9420-ed22e547083f:71
hash值为:1
// 如果 keys[1](lockKey) == 0,说明不存在,执行这些逻辑。
// keys[1] 不存在,使用hincrby命令,设置redis的key为:lockKey。
// 设置 hash的key为:UUID:线程名ID,hash的值为:1
//早起的版本为 hset设置。新版为:hincrby
if (redis.call('exists', KEYS[1]) == 0) then redis.call('hincrby', KEYS[1], ARGV[2], 1);
//调用 过期时间为 参数1。30万毫秒,30秒
redis.call('pexpire', KEYS[1], ARGV[1]);
return nil;
end;
//如果 这个 hash 存在(即:hexists 返回1)
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then
//使用 hincrby,增加1
redis.call('hincrby', KEYS[1], ARGV[2], 1);
//重置过期时间为 30秒
redis.call('pexpire', KEYS[1], ARGV[1]);
return nil;
end;
return redis.call('pttl', KEYS[1]);
RFuture<Long> ttlRemainingFuture =
this.tryLockInnerAsync(
waitTime,
this.commandExecutor.getConnectionManager().getCfg()
.getLockWatchdogTimeout(),
TimeUnit.MILLISECONDS,
threadId,
RedisCommands.EVAL_LONG
);
ttlRemainingFuture.onComplete((ttlRemaining, e) -> {
if (e == null) {
if (ttlRemaining == null) {
this.scheduleExpirationRenewal(threadId);
}
}
});
return ttlRemainingFuture;
private void scheduleExpirationRenewal(long threadId) {
RedissonLock.ExpirationEntry entry = new RedissonLock.ExpirationEntry();
RedissonLock.ExpirationEntry oldEntry =
(RedissonLock.ExpirationEntry)EXPIRATION_RENEWAL_MAP
.putIfAbsent(this.getEntryName(), entry);
if (oldEntry != null) {
oldEntry.addThreadId(threadId);
} else {
entry.addThreadId(threadId);
//续命逻辑在这里
this.renewExpiration();
}
}
private void renewExpiration() {
RedissonLock.ExpirationEntry ee =
(RedissonLock.ExpirationEntry)EXPIRATION_RENEWAL_MAP
.get(this.getEntryName());
if (ee != null) {
Timeout task = this.commandExecutor.getConnectionManager()
.newTimeout(new TimerTask() {
public void run(Timeout timeout) throws Exception {
RedissonLock.ExpirationEntry ent =
(RedissonLock.ExpirationEntry)
RedissonLock.EXPIRATION_RENEWAL_MAP
.get(RedissonLock.this.getEntryName());
if (ent != null) {
Long threadId = ent.getFirstThreadId();
if (threadId != null) {
RFuture<Boolean> future =
RedissonLock.this.renewExpirationAsync(threadId);
future.onComplete((res, e) -> {
if (e != null) {
RedissonLock.log.error("Can't update lock "
+ RedissonLock.this.getName()
+ " expiration", e);
} else {
if (res) {
//锁续命完毕,执行 onComplete 方法,又调用 他自己。
RedissonLock.this.renewExpiration();
}
}
});
}
}
}
//延迟执行的参数,为 超时时间 / 3L
}, this.internalLockLeaseTime / 3L, TimeUnit.MILLISECONDS);
ee.setTimeout(task);
}
}
protected RFuture<Boolean> renewExpirationAsync(long threadId) {
return this.evalWriteAsync(this.getName(), LongCodec.INSTANCE,
RedisCommands.EVAL_BOOLEAN,
"if 续命的脚本",
Collections.singletonList(this.getName()),
this.internalLockLeaseTime,
this.getLockName(threadId));
}
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1)
then redis.call('pexpire', KEYS[1], ARGV[1]);
return 1; end;
return 0;
protected RFuture<Boolean> unlockInnerAsync(long threadId) {
return this.evalWriteAsync(this.getName(), LongCodec.INSTANCE,
RedisCommands.EVAL_BOOLEAN, "if 下面的lua脚本",
Arrays.asList(this.getName(),
this.getChannelName()),
LockPubSub.UNLOCK_MESSAGE,
this.internalLockLeaseTime,
this.getLockName(threadId));
}
// 如果 这个 hashKey 不存在,直接返回0
if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then return nil;end;
//否则,这个 key存在,就 减去1,接收返回值,
local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1);
//如果返回值 大于0,就续期30秒。
if (counter > 0) then redis.call('pexpire', KEYS[1], ARGV[2]); return 0;
//如果这个 结果 不大于0,就删除,并发发布一个解锁的消息
else redis.call('del', KEYS[1]); redis.call('publish', KEYS[2], ARGV[1]);
return 1; end;
return nil;
PUBLISH channel message
发布一条消息到频道
this.getChannelName() 发布到:
LockPubSub.UNLOCK_MESSAGE,
public static final Long UNLOCK_MESSAGE = 0L;
//最终返回:redisson_lock__channel:{atguiguLock}
String getChannelName() {
return prefixName("redisson_lock__channel", this.getName()); //atguiguLock
}
RFuture<V> extends Future<V>, CompletionStage{
void onComplete(BiConsumer<? super V, ? super Throwable> var1);
}
//加锁的逻辑
//早起的版本为 hset设置。新版为:hincrby
if (redis.call('exists', KEYS[1]) == 0) then redis.call('hincrby', KEYS[1], ARGV[2], 1);
//调用 过期时间为 参数1。30万毫秒,30秒
redis.call('pexpire', KEYS[1], ARGV[1]);
return nil;
end;
//如果 这个 hash 存在(即:hexists 返回1)
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then
//使用 hincrby,增加1
redis.call('hincrby', KEYS[1], ARGV[2], 1);
//重置过期时间为 30秒
redis.call('pexpire', KEYS[1], ARGV[1]);
return nil;
end;
return redis.call('pttl', KEYS[1]);
//续命的逻辑
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1)
then redis.call('pexpire', KEYS[1], ARGV[1]);
return 1; end;
return 0;
//释放锁的逻辑
// 如果 这个 hashKey 不存在,直接返回0
if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then return nil;end;
//否则,这个 key存在,就 减去1,接收返回值,
local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1);
//如果返回值 大于0,就续期30秒。
if (counter > 0) then redis.call('pexpire', KEYS[1], ARGV[2]); return 0;
//如果这个 结果 不大于0,就删除,并发发布一个解锁的消息
else redis.call('del', KEYS[1]); redis.call('publish', KEYS[2], ARGV[1]);
return 1; end;
return nil;
PUBLISH channel message
发布一条消息到频道
this.getChannelName() 发布到:
LockPubSub.UNLOCK_MESSAGE, public static final Long UNLOCK_MESSAGE = 0L;
//最终返回:redisson_lock__channel:{atguiguLock}
String getChannelName() {
return prefixName("redisson_lock__channel", this.getName()); //atguiguLock
}