当前位置: 首页 > 知识库问答 >
问题:

java - Redisson的问题,发了好多次,有没有人帮忙看看?

孙渝
2023-11-12

问题1

https://segmentfault.com/q/1010000044340018

问题2

https://segmentfault.com/q/1010000044380820

问题3

// 计算并返回队列中最后一个线程的ttl,并添加到队列和set集合中// 获取队列中的最后一个元素// lindex threadsQueueName -1local lastThreadId = redis.call('lindex', KEYS[2], -1);local ttl;// 判断队列中最后一个元素不为空,且不等于uuid:threadIdif lastThreadId ~= false and lastThreadId ~= ARGV[2] then  // zscore redisson_lock_timeout:{lockName} lastThreadId - 当前时间  ttl = tonumber(redis.call('zscore', KEYS[3], lastThreadId)) - tonumber(ARGV[4]);else   // 只有队列中的元素为空的  // pttl lockName  ttl = redis.call('pttl', KEYS[1]);end;// timeout = ttl + waitTime + currentTimelocal timeout = ttl + tonumber(ARGV[3]) + tonumber(ARGV[4]);// 设置set集合中的元素,会带上一个timeout作为score// zadd redisson_lock_timeout:{lockName} timeout uuid:threadId if redis.call('zadd', KEYS[3], timeout, ARGV[2]) == 1 then  // 将等待的线程设置到队列中  // rpush redisson_lock_queue:{lockName} uuid:threadId   redis.call('rpush', KEYS[2], ARGV[2]);end;// 返回ttlreturn ttl;

这段脚本中 计算一个线程的 timeout作为score。
实际编码时
Redisson.create().getFairLock("test").tryLock(waitTime,leaseTime, TimeUnit.SECONDS);

为什么不直接使用当前时间戳+waitTime作为 score,而是使用了前一个节点的 ttl + waitTime + currentTime?

比如说我加锁等待超时时间为10秒,上面计算出来的ttl + waitTime 有可能已经是50秒了,
而当我索取锁失败的时候会返回ttl,然后本地线程阻塞 ttl的时间。
commandExecutor.getNow(subscribeFuture).getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
这不就相当于多阻塞了很长时间?

共有1个答案

柳杰
2023-11-12

这段脚本是为了处理分布式锁的获取和释放的逻辑。它使用了Redis作为存储媒介,并使用Redisson作为Java的Redis客户端。关于你提出的问题,为什么不直接使用当前时间戳+waitTime作为score,而是使用了前一个节点的ttl + waitTime + currentTime,这个主要是基于以下考虑:

  1. 防止超时时间被篡改:直接使用当前时间戳+waitTime作为score,那么这个值是可以被篡改的。如果一个恶意节点篡改了时间戳和waitTime,那么它就有可能获得不应该获得的锁。而使用前一个节点的ttl + waitTime + currentTime可以避免这个问题,因为前一个节点的ttl是Redis自动管理的,无法被篡改。
  2. 更加准确的超时时间:在分布式系统中,每个节点都有自己的系统时间,但是这些时间可能并不完全同步。如果直接使用当前时间戳+waitTime作为score,那么即使是在相同的时间点获取和释放锁,由于各个节点的系统时间可能存在误差,可能会导致实际锁的持有时间比预期的要长。而使用前一个节点的ttl + waitTime + currentTime可以避免这个问题,因为ttl是Redis自动计算的,不受各个节点系统时间的影响。
  3. 避免多线程阻塞:你提到的当索取锁失败的时候会返回ttl,然后本地线程阻塞ttl的时间的问题,这个其实并不会发生。因为在计算timeout的时候,如果队列为空,那么会直接使用lock的超时时间作为timeout,这样即使队列为空,也不会出现阻塞很长时间的情况。

总的来说,这段脚本的主要目的是为了保证分布式锁的安全性和准确性。

 类似资料: