redis 基于 redisson 的分布式锁

以前在开发项目的时候,一般使用自己基于 lua 脚本实现的 redis 锁,最近对分布式所深入了解了一下,才发现这样实现是有很大的问题的,比如说,如果线程执行时间过长,在锁释放之后还没有执行完成怎么办呢

最近又发现了一个比较好的 redis 的框架 redisson 这里面实现了 Watch Dog 自动延期功能,在这里记录一下,以便以后查阅

一、添加 redisson 配置

引入依赖:

 <dependency>
     <groupId>org.redisson</groupId>
     <artifactId>redisson</artifactId>
     <version>x.x.x</version>
 </dependency>

添加配置(这里我就直接复制我的配置了):

@Configuration
@ConfigurationProperties(prefix = "redisson")
public class RedissonProperties {

    // 是否启动集群模式
    public boolean clusterEnabled = false;

    // redis 服务器地址,多个以逗号分割
    private String address;

    // 使用的redis数据库编号
    private int database = 0;

    // redis 登录密码
    private String password;

    // 服务器响应超时时间
    private int timeout = 3000;

    // redis 集群扫描间隔
    private int scanInterval = 1000;

    // redis连接池大小,若为集群,则为主节点连接池大小
    private int connectionPoolSize = 64;

    // 每个从节点连接连接池大小
    private int slaveConnectionPoolSize = 250;

    // redis 最小空闲连接
    private int connectionMinimumIdleSize = 10;

    // ... get and set
}

@Bean
public RedissonClient redissonClient(RedissonProperties redissonProperties) {
    Config config = new Config();
    if(redissonProperties.clusterEnabled) {
        ClusterServersConfig serverConfig = config.useClusterServers()
                .addNodeAddress(redissonProperties.getAddress().split(","))
                .setTimeout(redissonProperties.getTimeout())
                .setScanInterval(redissonProperties.getScanInterval())
                .setMasterConnectionPoolSize(redissonProperties.getConnectionPoolSize())
                .setSlaveConnectionPoolSize(redissonProperties.getSlaveConnectionPoolSize())
                .setMasterConnectionMinimumIdleSize(redissonProperties.getConnectionMinimumIdleSize())
                .setSlaveConnectionMinimumIdleSize(redissonProperties.getConnectionMinimumIdleSize());
        if(!StringUtils.isEmpty(redissonProperties.getPassword())) {
            serverConfig.setPassword(redissonProperties.getPassword());
        }
    } else {
        SingleServerConfig serverConfig = config.useSingleServer()
                .setAddress(redissonProperties.getAddress())
                .setDatabase(redissonProperties.getDatabase())
                .setTimeout(redissonProperties.getTimeout())
                .setConnectionPoolSize(redissonProperties.getConnectionPoolSize())
                .setConnectionMinimumIdleSize(redissonProperties.getConnectionMinimumIdleSize());
        if(!StringUtils.isEmpty(redissonProperties.getPassword())) {
            serverConfig.setPassword(redissonProperties.getPassword());
        }
    }
    config.setCodec(new JsonJacksonCodec());
    return Redisson.create(config);
}

二、相关锁方法介绍

1. 获取Lock实例

// 非公平锁,无法实现线程顺序获取锁
RLock lock = redissonClient.getLock("lock-key");

// 公平锁,实现线程顺序获取锁
RLock lock = redissonClient.getFairLock("lock-key");

// 红锁,获取多个关联锁的红锁实例
RLock lock = redissonClient.getRedLock();

// 多锁,获取多个关联锁的实例
RLock lock = redissonClient.getMultiLock();

// 读写锁,不做讲解
ReadWriteLock readWriteLock = redissonClient.getReadWriteLock("");

2. RLock方法详解

// 具有Watch Dog自动延期机制; 锁默认存在时间为30s;每隔30/3=10s自动续期到30s
// 尝试获取锁,获取失败时,会不停的重试;直至拿到锁
lock.lock();

// 没有Watch Dog自动延期机制;在租赁到期后,释放锁
// 尝试获取锁,获取失败时,会不停的重试;直至拿到锁
lock.lock(120000, TimeUnit.SECONDS);

// 具有Watch Dog自动延期机制; 锁默认存在时间为30s;每隔30/3=10s自动续期到30s
// 获取锁失败时,会等待,直至达到最大等待时间,返回false
boolean bool = lock.tryLock(5, TimeUnit.SECONDS);

// 没有Watch Dog自动延期机制;在租赁到期后,释放锁
// 获取锁失败时,会等待,直至达到最大等待时间,返回false
boolean bool = lock.tryLock(5, 60, TimeUnit.SECONDS);

3. Wath Dog 自动延期机制

加入一个线程拿到了锁,设置30s超时,但是在30s后这个线程还没有执行完毕,锁释放了;这就会导致问题;RedissonWatch Dog 自动延期机制,正好可以解决这个问题。

Redisson 提供了一个监控锁的 看门狗(Wath Dog);做事在 Redisson 实例被关闭前,不断延长锁的有效期;也就是说,一个线程拿到锁,一直没有执行完逻辑,那么 看门狗 会不断延长锁超时时间。

默认情况下,看门狗 续期超时时间是30s,我们可以通过 Config.lockWatchdogTimeout 来设置这个时间,另外 Redisson 加锁时,也提供了 leaseTime 参数设置锁租赁时间。超过这个时间,锁就会自动解开,不会触发延期。

上一篇:Spring 使用注解注入对象


下一篇:APM 介绍与实现(Dog)(转载)