public class RedisHelper.LockOps extends Object
使用方式(示例): boolean flag = false; String lockName = "sichuan:mianyang:fucheng:ds"; String lockValue = UUID.randomUUID().toString(); try { // 非阻塞获取(锁的最大存活时间采用默认值) flag = RedisHelper.LockOps.getLock(lockName, lockValue); // 非阻塞获取e.g. flag = RedisHelper.LockOps.getLock(lockName, lockValue, 3, TimeUnit.SECONDS); // 阻塞获取(锁的最大存活时间采用默认值) flag = RedisHelper.LockOps.getLockUntilTimeout(lockName, lockValue, 2000); // 阻塞获取e.g. flag = RedisHelper.LockOps.getLockUntilTimeout(lockName, lockValue, 2, TimeUnit.SECONDS, 2000); if (!flag) { throw new RuntimeException(" obtain redis-lock[" + lockName + "] fail"); } // your logic // ... } finally { if (flag) { RedisHelper.LockOps.releaseLock(lockName, lockValue); } }
|--------------------------------------------------------------------------------------------------------------------| |单机版分布式锁、集群版分布式锁,特别说明: | | - 此锁是针对单机Redis的分布式锁; | | - 对于Redis集群而言, 此锁可能存在失效的情况。考虑如下情况: | | 首先,当客户端A通过key-value(假设为key名为key123)在Master上获取到一个锁。 | | 然后,Master试着把这个数据同步到Slave的时候突然挂了(此时Slave上没有该分布式锁的key123)。 | | 接着,Slave变成了Master。 | | 不巧的是,客户端B此时也一以相同的key去获取分布式锁; | | 因为现在的Master上没有key123代表的分布式锁, | | 所以客户端B此时再通过key123去获取分布式锁时, | | 就能获取成功。 | | 那么此时,客户端A和客户端B同时获取到了同一把分布式锁,分布式锁失效。 | | - 在Redis集群模式下,如果需要严格的分布式锁的话,可使用Redlock算法来实现。Redlock算法原理简述: | | - 获取分布式锁: | | 1. 客户端获取服务器当前的的时间t0。 | | 2. 使用相同的key和value依次向5个实例获取锁。 | | 注:为了避免在某个redis节点耗时太久而影响到对后面的Redis节点的锁的获取; | | 客户端在获取每一个Redis节点的锁的时候,自身需要设置一个较小的等待获取锁超时的时间, | | 一旦都在某个节点获取分布式锁的时间超过了超时时间,那么就认为在这个节点获取分布式锁失败, | | (不把时间浪费在这一个节点上),继续获取下一个节点的分布式锁。 | | 3. 客户端通过当前时间(t1)减去t0,计算(从所有redis节点)获取锁所消耗的总时间t2(注:t2=t1-t0)。 | | 只有t2小于锁本身的锁定时长(注:若锁的锁定时长是1小时, 假设下午一点开始上锁,那么锁会在下午两点 | | 的时候失效, 而你却在两点后才获取到锁,这个时候已经没意义了),并且,客户端在至少在多半Redis | | 节点上获取到锁, 我们才认为分布式锁获取成功。 | | 5. 如果锁已经获取,那么 锁的实际有效时长 = 锁的总有效时长 - 获取分布式锁所消耗的时长; 锁的实际有效时长 应保证 > 0。 | | 注: 也就是说, 如果获取锁失败,那么 | | A. 可能是 获取到的锁的个数,不满足大多数原则。 | | B. 也可能是 锁的实际有效时长不大于0。 | | - 释放分布式锁: 在每个redis节点上试着删除锁(, 不论有没有在该节点上获取到锁)。 | | - 集群下的分布式锁,可直接使用现有类库 | | | | 注: 如果Redis集群项目能够容忍master宕机导致单机版分布式锁失效的情况的话,那么是直接使用单机版分布式锁在Redis集群的项目中的; | | 如果Redis集群项目不能容忍单机版分布式锁失效的情况的话,那么请使用基于RedLock算法的集群版分布式锁; | |--------------------------------------------------------------------------------------------------------------------|
| 限定符和类型 | 字段和说明 |
|---|---|
long |
DEFAULT_LOCK_TIMEOUT
分布式锁默认(最大)存活时长
|
TimeUnit |
DEFAULT_TIMEOUT_UNIT
DEFAULT_LOCK_TIMEOUT的单位
|
| 构造器和说明 |
|---|
LockOps() |
| 限定符和类型 | 方法和说明 |
|---|---|
boolean |
getLock(String key,
String value)
获取(分布式)锁.
|
boolean |
getLock(String key,
String value,
long timeout,
TimeUnit unit)
获取(分布式)锁
注: 获取结果是即时返回的、是非阻塞的。
|
boolean |
getLock(String key,
String value,
long timeout,
TimeUnit unit,
boolean recordLog)
获取(分布式)锁
注: 获取结果是即时返回的、是非阻塞的。
|
boolean |
getLockUntilTimeout(String key,
String value,
long retryTimeoutLimit)
获取(分布式)锁。
|
boolean |
getLockUntilTimeout(String key,
String value,
long timeout,
TimeUnit unit,
long retryTimeoutLimit)
获取(分布式)锁。
|
boolean |
releaseLock(String key,
String value)
释放(分布式)锁
注: 此方式能(通过value的唯一性)保证: 自己加的锁, 只能被自己释放。
|
public final long DEFAULT_LOCK_TIMEOUT
public final TimeUnit DEFAULT_TIMEOUT_UNIT
public boolean getLockUntilTimeout(String key, String value, long retryTimeoutLimit)
注: 获取结果是阻塞的, 要么成功, 要么超时, 才返回。
retryTimeoutLimit - 重试的超时时长(ms)
其它参数可详见:getLock(String, String, long, TimeUnit)public boolean getLockUntilTimeout(String key, String value, long timeout, TimeUnit unit, long retryTimeoutLimit)
注: 获取结果是阻塞的, 要么成功, 要么超时, 才返回。
retryTimeoutLimit - 重试的超时时长(ms)
其它参数可详见:getLock(String, String, long, TimeUnit, boolean)public boolean getLock(String key, String value, long timeout, TimeUnit unit)
注: 获取结果是即时返回的、是非阻塞的。
public boolean getLock(String key, String value, long timeout, TimeUnit unit, boolean recordLog)
注: 获取结果是即时返回的、是非阻塞的。
key - 锁名value - 锁名对应的value
注: value一般采用全局唯一的值, 如: requestId、uuid等。
这样, 释放锁的时候, 可以再次验证value值,
保证自己上的锁只能被自己释放, 而不会被别人释放。
当然, 如果锁超时时, 会被redis自动删除释放。timeout - 锁的(最大)存活时长
注: 一般的, 获取锁与释放锁 都是成对使用的, 在锁在达到(最大)存活时长之前,都会被主动释放。
但是在某些情况下(如:程序获取锁后,释放锁前,崩了),锁得不到释放, 这时就需要等锁过
了(最大)存活时长后,被redis自动删除清理了。这样就能保证redis中不会留下死数据。unit - timeout的单位recordLog - 是否记录日志Copyright © 2023 Irvingsoft. All rights reserved.