@scheduled定时任务加分布式锁-整合Redisson

1,048 阅读4分钟

前提介绍

后端代码框架:SpringBoot+mybatis

Redisson的优势

Redisson提供了一个监控锁的看门狗(watch dog),它的作用是在Redisson实例被关闭前,不断(默认每10s)的延长锁(redis中的目标key)的有效期(默认续期到30s),也就是说,如果一个拿到锁的线程一直没有完成逻辑,那么看门狗会帮助线程不断的延长锁的超时时间,锁不会因为超时而被释放。加锁的业务只要运行完成,就不会给当前锁续期,即使不手动解锁,锁默认会在30s内自动过期,不会产生死锁问题; 默认情况下,看门狗的续期时间是30s,也可以通过修改Config.lockWatchdogTimeout来另行指定。 另外Redisson还提供了可以指定leaseTime参数的加锁方法来指定加锁的时间。超过这个时间后锁便自动解开了,不会延长锁的有效期。

Redisson的使用

1、加依赖

<!--redisson-->
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.13.6</version>
    <exclusions>
        <exclusion>
            <groupId>org.redisson</groupId>
            <artifactId>redisson-spring-data-23</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-data-21</artifactId>
    <version>3.13.6</version>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

注意:关于版本,直接去官网找下最新版即可,通过redisson-spring-data-xx来适配Spring的版本。上述就是适配spring2.x版本的配置;

2、改配置

redisson-spring-boot-starter支持两种配置方式,第一种是完全兼容spring-boot-starter-data-redis的配置(redis和redisson都在application.yml中配置); 第二种是使用redisson自己的配置方式,即在application.yml中只配置redis,然后使用spring.redis.redisson.config=classpath:redisson.yml来指定redisson的配置文件名称,然后在redisson.yml中配置redisson。

方式一(application.yml文件)

spring: 
  ## Redis配置
  redis: 
    password: xxxx1234
    cluster: 
      nodes: 10.163.xx.111:6381,10.163.xx.111:6382,10.163.xx.112:6381,10.163.xx.112:6382,10.163.xx.113:6381,10.163.xx.113:6382
    jedis:
      pool:
        #最大连接数
        max-active: 8
        #最大阻塞等待时间(负数表示没限制)
        max-wait: -1
        #最小空闲 
        min-idle: 0
        #最大空闲 
        max-idle: 8
    #连接超时时间  
    timeout: 10000
  ##Redisson配置
  redisson:
    enable: true
    cluster-servers-config:
      cluster-nodes: ${spring.redis.cluster.nodes}
      load-balancer-mode: RADOM
      password: ${spring.redis.password}
      slave-connection-minimum-idle-size: 8
      slave-connection-pool-size: 16
      sslEnableEndpointIdentification: false
    threads: 8
    nettyThreads: 8
    transportMode: NIO

方式二(redisson.yml文件)

# 单节点配置
singleServerConfig:
  # 连接空闲超时,单位:毫秒
  idleConnectionTimeout: 10000
  # 连接超时,单位:毫秒
  connectTimeout: 10000
  # 命令等待超时,单位:毫秒
  timeout: 3000
  # 命令失败重试次数,如果尝试达到 retryAttempts(命令失败重试次数) 仍然不能将命令发送至某个指定的节点时,将抛出错误。
  # 如果尝试在此限制之内发送成功,则开始启用 timeout(命令等待超时) 计时。
  retryAttempts: 3
  # 命令重试发送时间间隔,单位:毫秒
  retryInterval: 1500
  # 密码
  password: redis.shbeta
  # 单个连接最大订阅数量
  subscriptionsPerConnection: 5
  # 客户端名称
  clientName: axin
  #  # 节点地址
  address: redis://redis.shbeta@m-redis-k8s.shbeta.ke.com:36479
  # 发布和订阅连接的最小空闲连接数
  subscriptionConnectionMinimumIdleSize: 1
  # 发布和订阅连接池大小
  subscriptionConnectionPoolSize: 50
  # 最小空闲连接数
  connectionMinimumIdleSize: 32
  # 连接池大小
  connectionPoolSize: 64
  # 数据库编号
  database: 6
  # DNS监测时间间隔,单位:毫秒
  dnsMonitoringInterval: 5000
# 线程池数量,默认值: 当前处理核数量 * 2
#threads: 0
# Netty线程池数量,默认值: 当前处理核数量 * 2
#nettyThreads: 0
# 编码
codec: !<org.redisson.codec.JsonJacksonCodec> {}
# 传输模式
transportMode : "NIO"

注意:配置项需要参照org.redisson.config,如果你想配置集群模式的Redisson,就点Config的成员变量ClusterServersConfig去看下里边有哪些可配置项

3、简单代码使用

package com.nariit.adf.redission;

import org.redisson.api.RLock;

import java.util.concurrent.TimeUnit;

/**
 * Redisson分布式锁接口
 *
 * RLock的实现
 * 有可重入非公平锁(RedissonLock)、可重入公平锁(RedissonFairLock)、联锁(RedissonMultiLock)、 红锁(RedissonRedLock)、
 * 读锁(RedissonReadLock)、 写锁(RedissonWriteLock)等
 */
public interface DistributedLocker {
    RLock lock(String lockKey);

    RLock lock(String lockKey, long timeout);

    RLock lock(String lockKey, TimeUnit unit, long timeout);

    boolean tryLock(String lockKey, TimeUnit unit, long leaseTime);

    boolean tryLock(String lockKey, TimeUnit unit, long waitTime, long leaseTime);

    void unlock(String lockKey);

    void unlock(RLock lock);
}

package com.nariit.adf.redission;

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * Redisson分布式锁实现-使用可重入非公平锁(RedissonLock)
 * leaseTime 锁的持续时间
 * waitTime 获取锁的等待时间
 */
@Component
public class RedissonDistributedLocker implements DistributedLocker {

    @Autowired
    private RedissonClient redissonClient;

    @Override
    public RLock lock(String lockKey) {
        RLock lock = this.redissonClient.getLock(lockKey);
        lock.lock();
        return lock;
    }

    @Override
    public RLock lock(String lockKey, long timeout) {
        RLock lock = this.redissonClient.getLock(lockKey);
        //支持过期解锁功能,10秒钟以后自动解锁, 无需调用unlock方法手动解锁
        lock.lock(timeout, TimeUnit.SECONDS);
        return lock;
    }

    @Override
    public RLock lock(String lockKey, TimeUnit unit, long timeout) {
        RLock lock = this.redissonClient.getLock(lockKey);
        lock.lock(timeout, unit);
        return lock;
    }

    @Override
    public boolean tryLock(String lockKey, TimeUnit unit, long leaseTime) {
        RLock lock = this.redissonClient.getLock(lockKey);
        try {
            return lock.tryLock(0L, leaseTime, unit);
        }catch (InterruptedException var7){
            return false;
        }
    }

    @Override
    public boolean tryLock(String lockKey, TimeUnit unit, long waitTime, long leaseTime) {
        RLock lock = redissonClient.getLock(lockKey);
        try {
            return lock.tryLock(waitTime, leaseTime, unit);
        }catch (InterruptedException var7){
            return false;
        }
    }

    @Override
    public void unlock(String lockKey) {
        RLock lock = redissonClient.getLock(lockKey);
        try {
            if(lock.isLocked()){
                lock.unlock();
            }
        }catch (IllegalMonitorStateException var7){}
    }

    @Override
    public void unlock(RLock lock) {
        try {
            if (lock.isLocked()) {
                lock.unlock();
            }
        } catch (IllegalMonitorStateException var3) {}
    }
}

如图片所示: 微信图片_20220329163412.png

参考链接

blog.csdn.net/qq_27579471…