java相对主流分布式锁 - redis的Redisson (Java implementation)
the Redlock 设计
1.引入依赖
<!-- https://mvnrepository.com/artifact/org.redisson/redisson -->
<!-- 以后试用redisson作为分布式锁,分布式对象等功能框架-->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.12.0</version>
</dependency>
2.配置redis
整理单节点模式的Configuration
@Configuration
public class MyRedissonConfig {
/**
* 所有的对redisson的试用 都通过RedissonClient对象
* @return
* @throws IOException
*/
@Bean(destroyMethod = "shutdown")
public RedissonClient redisson() throws IOException{
Config config = new Config();
//集群模式
// config.useClusterServers().addNodeAddress("localhost:3001","127.0.0.1:6379");
//单节点模式
//1.创建配置
config.useSingleServer().setAddress("redis://192.168.31.125:6379");
//2.创建实例RedissonClient
return Redisson.create(config);
}
}
测试类 测试配置完成:
这个测试有一些必要性 能检验出你的maven引入依赖是否完整合乎版本的导入项目 能再逻辑之前,提前发现外部问题
比如 本人idea装载maven包 必须重启才能获取到正确的包 能有效检验
@Autowired
RedissonClient redissonClient;
@Test
public void redisson(){
System.out.println(redissonClient);
}
控制台如下:
org.redisson.Redisson@1b69fc07
延伸:文档中配置有两种方式
程序化配置或者配置文件配置
a.程序化配置
Redisson程序化的配置方法是通过构建Config
对象实例来实现的。例如:
Config config = new Config();
config.setTransportMode(TransportMode.EPOLL);
config.useClusterServers()
//可以用"rediss://"来启用SSL连接
.addNodeAddress("redis://127.0.0.1:7181");
b.文件方式配置
Redisson既可以通过用户提供的YAML格式的文本文件来配置
通过yaml格式配置
Redisson的配置文件可以是或YAML格式。 也通过调用config.fromYAML
方法并指定一个File
实例来实现读取YAML格式的配置:
Config config = Config.fromYAML(new File("config-file.yaml"));
RedissonClient redisson = Redisson.create(config);
调用config.toYAML
方法可以将一个Config
配置实例序列化为一个含有YAML数据类型的字符串:
Config config = new Config();
// ... 省略许多其他的设置
String jsonFormat = config.toYAML();
3.分布式锁
a.可重入锁
基于Redis的Redisson分布式可重入锁RLock Java对象实现了java.util.concurrent.locks.Lock
接口。同时还提供了异步(Async)、反射式(Reactive)和RxJava2标准的接口。
RLock lock = redisson.getLock("anyLock");
// 最常见的使用方法
lock.lock();
实际使用DEMO:
* redis的看门狗机制 解决了两个问题: * 1.锁的自动续期 如果业务超长,运行期间会自动给锁续期 不用担心业务时间长锁过期被删掉。 * 2.加锁业务完成,不会给当前锁续期,即使不手动解锁 lock.unlock() 锁也会过去 无死锁问题。
@ResponseBody
@GetMapping("/redisson")
public String redisson(){
//1.调用getLock获取一把锁 只要锁名字一样 就是同一把锁
RLock myLock = redisson.getLock("myLock");
//2 加锁 阻塞式等待
/**
* redis的看门狗机制 解决了两个问题:
* 1.锁的自动续期 如果业务超长,运行期间会自动给锁续期 不用担心业务时间长锁过期被删掉。
* 2.加锁业务完成,不会给当前锁续期,即使不手动解锁 lock.unlock() 锁也会过去 无死锁问题。
*/
myLock.lock();
myLock.lock(10,Time);
try{
System.out.println("加锁成功后 执行业务");
System.out.println("当前线程ID:" + Thread.currentThread().getId());
Thread.sleep(10000);
}catch (Exception e ){
}finally {
//不论业务执行结果如何 解锁
System.out.println("释放锁");
System.out.println("当前线程ID:" + Thread.currentThread().getId());
//redis的看门狗
myLock.unlock();
}
return "reddison";
}
延伸
了解看门狗机制:
myLock.lock();
1.如果传递了锁的超时时间,就发送给redis 执行脚本,进行占锁,默认超时时间是传递的时间
2.如果未指定超时时间,就使用30*1000【LockWatchdogTimeOut 看门狗的默认时间 默认30s】
只要占锁成功,就会启动一个定时任务【重新给所设置过期时间,新的过期时间就是看门狗的默认时间】
internalLockLeaseTime 续期时间 默认:看门狗时间/3 即:默认10s
每隔10s都自动续时,需要到满看门狗时间 30s (即:过期时间剩余20s 看门狗定时任务进程就让过期实际时间变成30s)推荐方法:
myLock.lock(30, TimeUnit.SECONDS);
即:不触发看门狗,给与对应的过期时间 30s 满足大多数操作时常。
节省了整个续期定时任务操作。手动解锁(即使没有解锁,30s也会解开)。
延伸
trylock 解决lock的阻塞方案 限时阻塞:
trylock 控制阻塞时间:
myLock.lock(10, TimeUnit.SECONDS);
myLock.trylock(100,10,TimeUnit.SECONDS);
trylock区别lock:
lock为阻塞方法
trylock尝试最多等待100s,上锁后10自动解锁
boolean res = myLock.trylock(100,10,TimeUnit.SECONDS);
if(res){
try{
…
} finally{
mylock.unlock();
}
}
延伸
公平锁:
默认占锁都是抢占,让请求有序占锁
请求的有序占用锁 (默认的是一个锁释放,其他抢占)
Rlock fairLock = redisson.getFairLock(“anyLock”);
fairLock.lock();