分布式锁一般有三种实现方式:1. 数据库乐观锁;2. 基于Redis的分布式锁;3. 基于ZooKeeper的分布式锁。
和其他两种锁比起来,Redis实现的分布式锁性能更高,对于高并发场景更加支持,ZK实现的分布式锁是强一致性的,也就是说是非常安全的,但是性能有所下降
Redis是AP模型,ZK是CP模型
可靠性
首先,为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件:
- 互斥性。在任意时刻,只有一个客户端能持有锁。——锁的基础特性
- 不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。——锁的可靠性
- 具有容错性。只要大部分的Redis节点正常运行,客户端就可以加锁和解锁。——主要指failover时,主从切换会不会丢锁的问题,不丢锁的前提是强一致
- 解铃还须系铃人。加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。
代码实现
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.16.3</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.75</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
针对于实现Redis分布式锁方案,研究各种文章和以及部分源码,实现了三种较可靠的实现方式,这里先引入主要借鉴的文章
https://zhuanlan.zhihu.com/p/53838899
https://zhuanlan.zhihu.com/p/150602212
https://www.cnblogs.com/fightingtong/p/12360222.html
直接上方案代码吧,接口结构这个没啥关系
简易分布式非重入锁
/**
* @author Franky
* @title: SimpleDistributedLock
* @description: 简单Redis实现分布式锁,非重入锁,不保证failover时锁状态正常
* @date 2021年10月26日 11:12
*/
@Component
public class SimpleRedisDistributedLock implements DistributedLock {
// 永远等待
private static final Long PERMANENT_WAIT_TIME = -1L;
// 立即返回
private static final Long IMMEDIATELY_RETURN = 0L;
private ThreadLocal<String> localId = ThreadLocal.withInitial(() -> String.format("%s-%s", Thread.currentThread().getName(), UUID.randomUUID().toString()));
@Autowired
private ValueOperations stringOperator;
@Autowired
private RedisTemplate redisTemplate;
@Override
public Boolean lock(String key) throws InterruptedException {
return lock(key, 30L, TimeUnit.SECONDS);
}
@Override
public Boolean lock(String key, Long expireTime, TimeUnit unit) throws InterruptedException {
return lock(key, expireTime, PERMANENT_WAIT_TIME, unit);
}
@Override
public Boolean lockNow(String key, Long expireTime, TimeUnit unit) {
Boolean success = false;
try {
success = lock(key, expireTime, IMMEDIATELY_RETURN, unit);
} catch (InterruptedException e) {
// 吃掉可能出现的异常
e.printStackTrace();
}
return success;
}
@Override
public Boolean lock(final String key, final Long expireTime, Long waitTime, final TimeUnit unit) throws InterruptedException {
Boolean success = true;
Long endTime = 0L;
if (waitTime < 0)
waitTime = PERMANENT_WAIT_TIME;
else if (0 > waitTime)
endTime = System.currentTimeMillis() + unit.toMillis(waitTime);
do {
// 阻塞获取lock,每一百毫秒请求一次
if (!success) TimeUnit.MILLISECONDS.sleep(100L);
// 需要原子性操作
success = stringOperator.setIfAbsent(key, getRequestId(), expireTime, unit);
// 阻塞/未到等待时间的情况下,获取失败重复获取
}while (!success && (waitTime == PERMANENT_WAIT_TIME || endTime > System.currentTimeMillis()));
return success;
}
@Override
public Boolean unlock(String key) {
try {
// lua脚本使redis原子性操作
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
// ClassPathResource resource = new ClassPathResource(script); 可以使用外置脚本
Long result = (Long)redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList(key), getRequestId());
return 1 == result;
}finally {
this.localId.remove();
}
}
@Override
public void unlock(String key, Consumer<? super Object> action) {
Boolean unlock = unlock(key);
if (null != action)
action.accept(unlock);
}
/**
* 返回唯一requestId
* @return
*/
private String getRequestId() {
return this.localId.get();
}
}
对于这里的Lock和unLock给了几个生产中常用的重载方法
这部分内容主要是参考了https://zhuanlan.zhihu.com/p/53838899这篇文章得到的,写的非常详细
分布式重入锁
/**
* @author Franky
* @title: ReEnRedisDistributedLock
* @description: 自定义可重入锁,不保证failover时锁状态正常
* @date 2021年10月27日 16:24
*/
@Component
public class ReEnRedisDistributedLock implements DistributedLock {
// 永远等待
private static final Long PERMANENT_WAIT_TIME = -1L;
// 立即返回
private static final Long IMMEDIATELY_RETURN = 0L;
private ThreadLocal<String> localId = ThreadLocal.withInitial(() -> String.format("%s-%s", Thread.currentThread().getName(), UUID.randomUUID().toString()));
private ThreadLocal<Long> expireTime = new ThreadLocal<>();
@Autowired
private RedisTemplate redisTemplate;
@Override
public Boolean lock(String key) throws InterruptedException {
return lock(key, 30L, TimeUnit.SECONDS);
}
@Override
public Boolean lock(String key, Long expireTime, TimeUnit unit) throws InterruptedException {
return lock(key, expireTime, PERMANENT_WAIT_TIME, unit);
}
@Override
public Boolean lockNow(String key, Long expireTime, TimeUnit unit) throws InterruptedException {
return lock(key, expireTime, IMMEDIATELY_RETURN, unit);
}
@Override
public Boolean lock(String key, Long expireTime, Long waitTime, TimeUnit unit) throws InterruptedException {
boolean NonBlock = true;
boolean permanent = false;
this.expireTime.set(unit.toMillis(expireTime));
if (waitTime < 0)
permanent = true;
Long current = System.currentTimeMillis();
// lua脚本 可重入锁
String script = "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]);";
do {
// 阻塞获取lock,每一百毫秒请求一次
if (!NonBlock) TimeUnit.MILLISECONDS.sleep(100L);
// 返回null 表示成功获取锁
Object result = redisTemplate.execute(new DefaultRedisScript(script, Long.class), Arrays.asList(key), unit.toMillis(expireTime), getRequestId());
if (null == result) return true;
NonBlock = false;
waitTime -= System.currentTimeMillis() - current;
current = System.currentTimeMillis();
// 阻塞/未到等待时间的情况下,获取失败重复获取
}while (permanent || waitTime > 0);
return false;
}
@Override
public Boolean unlock(String key) {
Object result = unlock(key, Object.class);
return null != result;
}
@Override
public void unlock(String key, Consumer<? super Object> action) {
Object result = unlock(key, Object.class);
if (null != action)
action.accept(result);
}
/**
* 返回唯一requestId
* @return
*/
private String getRequestId() {
return this.localId.get();
}
/**
* 对指定key进行解锁
* @param key key
* @param tClass 标识返回值类型
* @param <T> 标识返回值类型
* @returnnull:解锁失败; 0:解重入锁一次成功; 1:解锁成功
*/
public<T> T unlock(String key, Class<T> tClass) {
try {
// lua脚本使redis原子性操作
String script = "if (redis.call('hexists', KEYS[1], ARGV[2]) == 0) then " +
"return nil;" +
"end; " +
"local counter = redis.call('hincrby', KEYS[1], ARGV[2], -1); " +
"if (counter > 0) then " +
"redis.call('pexpire', KEYS[1], ARGV[1]); " +
"return 0; " +
"else " +
"redis.call('del', KEYS[1]); " +
"return 1; " +
"end; " +
"return nil;";
// ClassPathResource resource = new ClassPathResource(script); 可以使用外置脚本
Object result = redisTemplate.execute(new DefaultRedisScript(script, Long.class), Arrays.asList(key), this.expireTime.get(), getRequestId());
return (T)result;
}finally {
// 防止内存泄漏
this.expireTime.remove();
this.localId.remove();
}
}
}
这部分内容主要是查看了Redisson源码后简化了部分,自定义了一个可重入锁,因为Redisson中会优先保证锁信息被其他可能存在的从节点同步后才会返回生成,虽然这个保证了锁的容错性,但是也降低了性能,这里仿照写了一个可重入锁,不保证容错性,但是在实际生产中可以通过补偿逻辑去保证容错性。
Redisson实现分布式锁
/**
* @author Franky
* @title: RedissonDistributedLock
* @description: 通过 redisson client 实现分布式可重入锁,保证failover时锁状态正常
* @date 2021年10月27日 11:11
*/
@Component
public class RedissonDistributedLock implements DistributedLock {
private ThreadLocal<RLock> reEntranceLock = new ThreadLocal();
@Autowired
private RedissonClient redisson;
@Override
public Boolean lock(String key) throws InterruptedException {
this.getRLock(key).lock();
return true;
}
@Override
public Boolean lock(String key, Long expireTime, TimeUnit unit) throws InterruptedException {
this.getRLock(key).lock(expireTime, unit);
return true;
}
@Override
public Boolean lockNow(String key, Long expireTime, TimeUnit unit) throws InterruptedException {
return this.lock(key, expireTime, 0L, unit);
}
@Override
public Boolean lock(String key, Long expireTime, Long waitTime, TimeUnit unit) throws InterruptedException {
return this.getRLock(key).tryLock(waitTime, expireTime, unit);
}
@Override
public Boolean unlock(String key) {
try {
this.getRLock(key).unlock();
return true;
}finally {
this.reEntranceLock.remove();
}
}
@Override
public void unlock(String key, Consumer<? super Object> action) {
unlock(key);
action.accept(true);
}
private RLock getRLock(String key) {
if (null == this.reEntranceLock.get()) {
// To increase reliability during failover, all operations wait for propagation to all Redis slaves.
this.reEntranceLock.set(this.redisson.getLock(key));
}
return this.reEntranceLock.get();
}
}
使用Redisson实现分布式锁,基本具体上面提到的四个特性,并且是可重入的,而且定义锁非常简单,简化了开发,但是问题么,上面也提到了
Redisson锁的原理推荐:https://zhuanlan.zhihu.com/p/150602212
Redisson基本流程
![image.png](https://img-blog.csdnimg.cn/img_convert/4f735aea7a115a9855b4c8438446d90d.png#clientId=ub5b6bff3-48f7-4&from=paste&id=u782e7d2d&margin=[object Object]&name=image.png&originHeight=860&originWidth=1440&originalType=url&ratio=1&size=556208&status=done&style=none&taskId=u86ac23cf-678a-49b0-aeec-4dc659d53f3)
借图一张,肯定不会和上面的原理重复,主要是梳理整个流程,就是将流程图汇总的内容通过源码来梳理一下
其实上面这个流程图指的是RedissonClient.lcok()
这个无参的方法,主要是体现到两点,如果没有设置锁过期时间,那么Redisson会默认一个过期时间,默认值是30Second,当然这个值也是可以设置的,如:
@Bean("redissonClient")
public RedissonClient getRedisson() {
Config config = new Config();
// 默认expire 时间 redisson lock 128行
config.setLockWatchdogTimeout(3000);
config.useSingleServer()
.setTimeout(1000000)
.setAddress(String.format("redis://%s:%s", this.host, this.port))
.setPassword(this.password);
return Redisson.create(config);
}
第二点就是客户端B不断尝试加锁,这个是因为没有指定等待时间,如果指定等待时间,那么会在等待时间到后返回失败。
无参lock()方法
根据流程图,还是先从无参的lock出发,然后再看一下其他流程
在RedissonLock
类中,有这样的代码
@Override
public void lock() {
try {
// 默认过期时间是-1,那么将走WatchDog逻辑,默认过期时间并且自动延长锁时间
lock(-1, null, false);
} catch (InterruptedException e) {
throw new IllegalStateException();
}
}
@Override
public void lock(long leaseTime, TimeUnit unit) {
try {
lock(leaseTime, unit, false);
} catch (InterruptedException e) {
throw new IllegalStateException();
}
}
@Override
public void lockInterruptibly() throws InterruptedException {
lock(-1, null, true);
}
@Override
public void lockInterruptibly(long leaseTime, TimeUnit unit) throws InterruptedException {
lock(leaseTime, unit, true);
}
/**
针对于无等待时间获取锁方式都会走这个方法
*/
private void lock(long leaseTime, TimeUnit unit, boolean interruptibly) throws InterruptedException {
long threadId = Thread.currentThread().getId();
// 所有获取锁逻辑都使用tryAcquire
Long ttl = tryAcquire(-1, leaseTime, unit, threadId);
// lock acquired
if (ttl == null) {
return;
}
// 获取锁失败,走下面逻辑, interruptibly为ture,代表当线程被中断时,中断获取锁逻辑
RFuture<RedissonLockEntry> future = subscribe(threadId);
if (interruptibly) {
commandExecutor.syncSubscriptionInterrupted(future);
} else {
commandExecutor.syncSubscription(future);
}
try {
// 死循环获取锁
while (true) {
ttl = tryAcquire(-1, leaseTime, unit, threadId);
// lock acquired
if (ttl == null) {
break;
}
// waiting for message
if (ttl >= 0) {
try {
future.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
if (interruptibly) {
throw e;
}
future.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
}
} else {
if (interruptibly) {
future.getNow().getLatch().acquire();
} else {
future.getNow().getLatch().acquireUninterruptibly();
}
}
}
} finally {
unsubscribe(future, threadId);
}
// get(lockAsync(leaseTime, unit));
}
这里有个疑问,关于Redisson第一次获取锁失败后,会进行订阅,应该和它的尝试次数是有关系的,可以中断线程,待以后确定
在tryAcquire
方法后,会进入这个方法
private <T> RFuture<Long> tryAcquireAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId) {
RFuture<Long> ttlRemainingFuture;
if (leaseTime != -1) {
ttlRemainingFuture = tryLockInnerAsync(waitTime, leaseTime, unit, threadId, RedisCommands.EVAL_LONG);
} else {
// 当锁的过期时间未设置时,默认internalLockLeaseTime这个时间为过期时间
ttlRemainingFuture = tryLockInnerAsync(waitTime, internalLockLeaseTime,
TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);
}
// 响应完成后执行
ttlRemainingFuture.onComplete((ttlRemaining, e) -> {
// 报错后返回
if (e != null) {
return;
}
// lock acquired
if (ttlRemaining == null) {
if (leaseTime != -1) {
internalLockLeaseTime = unit.toMillis(leaseTime);
} else {
// 对于为设置过期时间的锁,watchDog自动延长锁的过期时间
scheduleExpirationRenewal(threadId);
}
}
});
return ttlRemainingFuture;
}
// 具体加锁逻辑,下面的lua表达式在https://zhuanlan.zhihu.com/p/150602212文章中有很好的解释
<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));
}
// 自动延长锁的过期时间
protected void scheduleExpirationRenewal(long threadId) {
ExpirationEntry entry = new ExpirationEntry();
// 这是一个关于可重入锁的逻辑,只要没有将全部的锁unlock掉,下面的延迟刷新过期时间任务就会一直有效,后面有unlock的逻辑代码
ExpirationEntry oldEntry = EXPIRATION_RENEWAL_MAP.putIfAbsent(getEntryName(), entry);
if (oldEntry != null) {
oldEntry.addThreadId(threadId);
} else {
entry.addThreadId(threadId);
try {
// 刷新过期时间
renewExpiration();
} finally {
if (Thread.currentThread().isInterrupted()) {
cancelExpirationRenewal(threadId);
}
}
}
}
private void renewExpiration() {
ExpirationEntry ee = EXPIRATION_RENEWAL_MAP.get(getEntryName());
if (ee == null) {
return;
}
// 刷新锁的过期时间主要是发生在这里
Timeout task = commandExecutor.getConnectionManager().newTimeout(new TimerTask() {
@Override
public void run(Timeout timeout) throws Exception {
ExpirationEntry ent = EXPIRATION_RENEWAL_MAP.get(getEntryName());
if (ent == null) {
return;
}
Long threadId = ent.getFirstThreadId();
if (threadId == null) {
return;
}
// 看下面的方法
RFuture<Boolean> future = renewExpirationAsync(threadId);
future.onComplete((res, e) -> {
if (e != null) {
log.error("Can't update lock " + getRawName() + " expiration", e);
EXPIRATION_RENEWAL_MAP.remove(getEntryName());
return;
}
// 成功后递归
if (res) {
// reschedule itself
renewExpiration();
} else {
cancelExpirationRenewal(null);
}
});
}
}, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);
// 这个在后面取消锁的自动延长时间任务时使用
ee.setTimeout(task);
}
// 这里直接lua表达式,就是判断指定的锁是否存在,若存在则刷新过期时间返回1,其他返回0,刷新的过期时间依然是默认时间
protected RFuture<Boolean> renewExpirationAsync(long threadId) {
return evalWriteAsync(getRawName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
"if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +
"redis.call('pexpire', KEYS[1], ARGV[1]); " +
"return 1; " +
"end; " +
"return 0;",
Collections.singletonList(getRawName()),
internalLockLeaseTime, getLockName(threadId));
}
有参tryLock()方法
这里类似的逻辑,但是不会走watchDog逻辑,因为在这里的时候就被限制住了
![image.png](https://img-blog.csdnimg.cn/img_convert/c39200253b085dcdf29114ade83c6aba.png#clientId=ub5b6bff3-48f7-4&from=paste&height=465&id=u94cfb69a&margin=[object Object]&name=image.png&originHeight=930&originWidth=2551&originalType=binary&ratio=1&size=166542&status=done&style=none&taskId=u160bc33f-f969-4ade-a878-0eb5aadbde1&width=1275.5)
这个方法和lock
方法主要的差距在这里,其他内容都一样
@Override
public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {
long time = unit.toMillis(waitTime);
long current = System.currentTimeMillis();
long threadId = Thread.currentThread().getId();
// 尝试获取锁
Long ttl = tryAcquire(waitTime, leaseTime, unit, threadId);
// lock acquired
if (ttl == null) {
return true;
}
// 判断等待时间是否到了,到了就返回false,获取失败
time -= System.currentTimeMillis() - current;
if (time <= 0) {
acquireFailed(waitTime, unit, threadId);
return false;
}
current = System.currentTimeMillis();
RFuture<RedissonLockEntry> subscribeFuture = subscribe(threadId);
if (!subscribeFuture.await(time, TimeUnit.MILLISECONDS)) {
if (!subscribeFuture.cancel(false)) {
subscribeFuture.onComplete((res, e) -> {
if (e == null) {
unsubscribe(subscribeFuture, threadId);
}
});
}
acquireFailed(waitTime, unit, threadId);
return false;
}
try {
time -= System.currentTimeMillis() - current;
if (time <= 0) {
acquireFailed(waitTime, unit, threadId);
return false;
}
// 下面依然是死循环获取锁,但是在等待时间到后,会自动返回失败,而不是一直等待下去
while (true) {
long currentTime = System.currentTimeMillis();
ttl = tryAcquire(waitTime, leaseTime, unit, threadId);
// lock acquired
if (ttl == null) {
return true;
}
time -= System.currentTimeMillis() - currentTime;
if (time <= 0) {
acquireFailed(waitTime, unit, threadId);
return false;
}
// waiting for message
currentTime = System.currentTimeMillis();
if (ttl >= 0 && ttl < time) {
subscribeFuture.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
} else {
subscribeFuture.getNow().getLatch().tryAcquire(time, TimeUnit.MILLISECONDS);
}
time -= System.currentTimeMillis() - currentTime;
if (time <= 0) {
acquireFailed(waitTime, unit, threadId);
return false;
}
}
} finally {
unsubscribe(subscribeFuture, threadId);
}
// return get(tryLockAsync(waitTime, leaseTime, unit));
}
unlock方法
@Override
public RFuture<Void> unlockAsync(long threadId) {
RPromise<Void> result = new RedissonPromise<>();
// 解锁的lua逻辑
RFuture<Boolean> future = unlockInnerAsync(threadId);
future.onComplete((opStatus, e) -> {
// 取消上面提到的自动刷新过期时间任务
cancelExpirationRenewal(threadId);
// 其他异常
if (e != null) {
result.tryFailure(e);
return;
}
// 锁不存在时,发生这个异常
if (opStatus == null) {
IllegalMonitorStateException cause = new IllegalMonitorStateException("attempt to unlock lock, not locked by current thread by node id: "
+ id + " thread-id: " + threadId);
result.tryFailure(cause);
return;
}
result.trySuccess(null);
});
return result;
}
protected void cancelExpirationRenewal(Long threadId) {
ExpirationEntry task = EXPIRATION_RENEWAL_MAP.get(getEntryName());
if (task == null) {
return;
}
if (threadId != null) {
task.removeThreadId(threadId);
}
// 这里判断了一下是否所有锁都unlock了,这是针对可重入锁,如果没有,不取消自动刷新过期时间任务
if (threadId == null || task.hasNoThreads()) {
Timeout timeout = task.getTimeout();
if (timeout != null) {
timeout.cancel();
}
EXPIRATION_RENEWAL_MAP.remove(getEntryName());
}
}
// unlock逻辑
// 当锁不存在时,返回null
// 将锁的次数减1,查看锁是否全部unlock,如果没有刷新过期时间,
// 全部unlock则删除锁并返回1
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));
}
关于Redisson的源码情况大概就到这里了,如有错误,烦请指正!!!