Redisson联锁

一、联锁概述

联锁(RedissonMultiLock)对象可以将多个RLock对象关联为一个联锁,实现加锁和解锁功能。每个RLock对象实例可以来自于不同的Redisson实例。

如果负责储存分布式锁的某些Redis节点宕机以后,而且这些锁正好处于锁住状态,就会出现死锁问题。为了避免这种情况的发生,Redisson内部提供了一个监控锁的看门狗。看门狗的作用是在Redisson实例被关闭前,不断延长锁的有效期。默认情况下,看门狗的检查锁的超时时间是30秒,也可以通过修改Config.lockWatchdogTimeout来另行指定。

二、实践

我是在Springboot项目中使用的Redisson分布式锁。

2.1 pom.xml
<dependency>
     <groupId>org.redisson</groupId>
     <artifactId>redisson</artifactId>
     <version>3.14.1</version>
</dependency>
2.2 application.properties
server.port=8083

## master 数据源配置
master.datasource.url=jdbc:mysql://10.5.146.156:3306/USDP?useUnicode=true&characterEncoding=utf8
master.datasource.username=root
master.datasource.password=click1
master.datasource.driverClassName=com.mysql.jdbc.Driver

# 连接池的配置信息
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
# 初始化大小:建立物理连接的个数
spring.datasource.initialSize=5  
# 最小连接数
spring.datasource.minIdle=5  
# 最大连接数
spring.datasource.maxActive=20  
# 配置获取连接等待超时的时间
spring.datasource.maxWait=60000  
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
spring.datasource.timeBetweenEvictionRunsMillis=60000  
# 配置一个连接在池中最小生存的时间,单位是毫秒
spring.datasource.minEvictableIdleTimeMillis=300000  
# 用来检测连接是否有效的sql,要求是一个查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用
spring.datasource.validationQuery=SELECT 1 FROM DUAL  
spring.datasource.testWhileIdle=true  
spring.datasource.testOnBorrow=false  
spring.datasource.testOnReturn=false  
# 打开PSCache,并且指定每个连接上PSCache的大小;
# 是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭
spring.datasource.poolPreparedStatements=false  
spring.datasource.maxPoolPreparedStatementPerConnectionSize=20  
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙;
# 属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有:监控统计用的filter:stat, 日志用的filter:log4j, 防御sql注入的filter:wall
spring.datasource.filters=stat,wall,log4j  
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000


## 打印SQL语句
##SpringBoot默认是使用info级别,没有指定级别的就用Springboot默认规定的级别:root级别
logging.level.com.test=INFO
#该属性用来配置日志文件名,如果该属性不配置,默认文件名为spring.log
logging.file=/opt/applog/interfaceautotestagent/test.log
#logging.file=/var/log/test.log
logging.pattern.console=%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n
#指定文件中日志的输出格式
logging.pattern.file=%msg%n


# REDIS
# Redis数据库索引(默认为0)
spring.redis.database=0
## Redis服务器地址
#spring.redis.host=10.237.78.40
## Redis服务器连接端口
#spring.redis.port=6379
## Redis服务器连接密码(默认为空)
#spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.lettuce.pool.max-active=16
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.lettuce.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.lettuce.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.lettuce.pool.min-idle=0
# 连接超时时间(毫秒)spring boot 1.x,redisson新加的
spring.redis.timeout=60000
# 执行命令超时时间,单位毫秒
spring.redis.command-timeout: 15000
# 是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个
spring.redis.test-on-borrow: true
# 集群模式下,逗号分隔的键值对(主机:端口)形式的服务器列表
#spirng.redis.cluster.nodes=10.237.79.150:6379,10.237.79.150:6380,10.237.79.151:6379,10.237.79.151:6380,10.237.79.152:6379,10.237.79.152:6380
spirng.redis.cluster.nodes=172.24.83.165:6379,172.24.83.165:6380,172.24.83.165:6381,172.24.83.165:6382,172.24.83.165:6383,172.24.83.165:6384
# 集群模式下,集群最大转发的数量
spring.redis.cluster.max-redirects=3
# 最大的连接重试次数
spring.redis.cluster.max-attempts=5


#activemq
spring.activemq.broker-url=tcp://10.237.78.6:61616
spring.activemq.user=
spring.activemq.password=
#spring.activemq.in-memory=true
#spring.activemq.pool.enabled=false
#使用发布/订阅模式时,下边配置需要设置成 true  是否使用默认的destination type来支持 publish/subscribe,默认: false
spring.jms.pub-sub-domain=true
##指定最小的并发消费者数量
#spring.jms.listener.concurrency = 10
##指定最大的并发消费者数量
#spring.jms.listener.max-concurrency = 10
2.3 RedisConfigProperties
package com.test.config;

import lombok.Data;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

import java.util.List;

@Data
@Component
@PropertySource("classpath:application.properties")
public class RedisConfigProperties {

    @Value("${spirng.redis.cluster.nodes}")
    private String redisClusterNodes;
}
2.4 RedissonConfig
package com.test.config;

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.ClusterServersConfig;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.ArrayList;
import java.util.List;

@Configuration
public class RedissonConfig {
    @Autowired
    private RedisConfigProperties redisConfigProperties;

    @Bean
    public RedissonClient redissonClient1() {
        //redisson版本是3.5,集群的ip前面要加上“redis://”,不然会报错,3.2版本可不加
        List<String> clusterNodes = new ArrayList<>();
        for (int i = 0; i < 2; i++) {
            clusterNodes.add("redis://" + redisConfigProperties.getRedisClusterNodes().split(",")[i]);
        }
        Config config = new Config();
        // 添加集群地址
        ClusterServersConfig clusterServersConfig = config.useClusterServers().addNodeAddress(clusterNodes.toArray(new String[clusterNodes.size()]));
//        // 设置密码
//        clusterServersConfig.setPassword(redisConfigProperties.getPassword());
        RedissonClient redissonClient = Redisson.create(config);
        return redissonClient;
    }

    @Bean
    public RedissonClient redissonClient2() {
        //redisson版本是3.5,集群的ip前面要加上“redis://”,不然会报错,3.2版本可不加
        List<String> clusterNodes = new ArrayList<>();
        for (int i = 2; i < 4; i++) {
            clusterNodes.add("redis://" + redisConfigProperties.getRedisClusterNodes().split(",")[i]);
        }
        Config config = new Config();
        // 添加集群地址
        ClusterServersConfig clusterServersConfig = config.useClusterServers().addNodeAddress(clusterNodes.toArray(new String[clusterNodes.size()]));
//        // 设置密码
//        clusterServersConfig.setPassword(redisConfigProperties.getPassword());
        RedissonClient redissonClient = Redisson.create(config);
        return redissonClient;
    }

    @Bean
    public RedissonClient redissonClient3() {
        //redisson版本是3.5,集群的ip前面要加上“redis://”,不然会报错,3.2版本可不加
        List<String> clusterNodes = new ArrayList<>();
        for (int i = 4; i < 5; i++) {
            clusterNodes.add("redis://" + redisConfigProperties.getRedisClusterNodes().split(",")[i]);
        }
        Config config = new Config();
        // 添加集群地址
        ClusterServersConfig clusterServersConfig = config.useClusterServers().addNodeAddress(clusterNodes.toArray(new String[clusterNodes.size()]));
//        // 设置密码
//        clusterServersConfig.setPassword(redisConfigProperties.getPassword());
        RedissonClient redissonClient = Redisson.create(config);
        return redissonClient;
    }
}
2.5 使用联锁
RLock lock1 = redissonClient1.getLock("lock1");
RLock lock2 = redissonClient2.getLock("lock2");
RLock lock3 = redissonClient3.getLock("lock3");

RedissonMultiLock lock = new RedissonMultiLock(lock1, lock2, lock3);
// 同时加锁:lock1 lock2 lock3
// 所有的锁都上锁成功才算成功。
lock.lock();
...
lock.unlock();

RedissonMultiLock lock = new RedissonMultiLock(lock1, lock2, lock3);
// 给lock1,lock2,lock3加锁,如果没有手动解开的话,(leaseTime=10s)10秒钟后将会自动解开
lock.lock(10, TimeUnit.SECONDS);

// 为加锁等待(waitTime=100s)100秒时间,并在加锁成功10秒钟后自动解开
boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
...
lock.unlock();

猜你喜欢

转载自blog.csdn.net/sinat_34241861/article/details/112348930