【SpringBoot】整合Redisson(使用redisson-spring-boot-starter)

前言

Redisson简介

省略…

Redisson的优势

提供了看门狗

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

提供了多种锁

redisson 还有公平锁、读写锁的实现。
在这里插入图片描述

public interface RedissonClient {
	/**
	 * 获取锁(默认的非公平锁)
	 */
	RLock getLock(String name);
	
	/**
	 * 获取公平锁
	 */
	RLock getFairLock(String name);
	
	/**
	 * 获取读写锁
	 */
	RReadWriteLock getReadWriteLock(String name);
}
public interface RLock extends Lock, RExpirable, RLockAsync {
	void lockInterruptibly(long leaseTime, TimeUnit unit) throws InterruptedException;

	boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException;

	void lock(long leaseTime, TimeUnit unit);
	
	boolean forceUnlock();

	boolean isLocked();

	boolean isHeldByCurrentThread();

	int getHoldCount();
}

实战

加依赖


<dependency>
    <groupId>org.redissongroupId>
    <artifactId>redisson-spring-boot-starterartifactId>
    <version>3.13.6version>
    <exclusions>
        <exclusion>
            <groupId>org.redissongroupId>
            <artifactId>redisson-spring-data-23artifactId>
        exclusion>
    exclusions>
dependency>
<dependency>
    <groupId>org.redissongroupId>
    <artifactId>redisson-spring-data-21artifactId>
    <version>3.13.6version>
dependency>

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-data-redisartifactId>
dependency>

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

分析源码

redisson-spring-boot-starter这个包下只有2个类:RedissonPropertiesRedissonAutoConfiguration。阅读源码发现RedissonAutoConfiguration会自动注册RedissonConnectionFactoryRedissonClient
【SpringBoot】整合Redisson(使用redisson-spring-boot-starter)_第1张图片

改配置

redisson-spring-boot-starter支持两种配置方式,第一种是完全兼容spring-boot-starter-data-redis的配置(redisredisson都在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

方式二

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:
    config-file:
      yaml: classpath:redisson.yml

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: ! {}
# 传输模式
transportMode : "NIO"

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

简单使用

配置了redisredisson的信息之后,就可以在项目中使用@Autowired注入org.redisson.api.RedissonClientredisson提供的默认实现类是org.redisson.Redisson,且redisson-spring-boot-starter会自动往IOC容器中注册org.redisson.Redisson)。

  • Redis操作注入RedisTemplate进行使用。
  • Redission分布式锁可以引入RedissionClient进行使用。
@RestController
public class RedissonController {
	
    @Autowired
    private RedissonClient redissonClient;

    @GetMapping(value = "/redisson/{key}")
    public String redissonTest(@PathVariable("key") String lockKey) {
        RLock lock = redissonClient.getLock(lockKey);
        try {
            lock.lock();
            Thread.sleep(10000);
        } catch (Exception e) {

        } finally {
            lock.unlock();
        }
        return "已解锁";
    }
}

优化一下

封装一下对redissonClient的使用。

package com.xx.core.redisson.lock;

/**
 * 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); }

/**
 * Redisson分布式锁实现-使用可重入非公平锁(RedissonLock)
 */
@Component
public class RedissonDistributedLocker implements DistributedLocker {
	@Autowired
    private RedissonClient redissonClient;

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

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

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

	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;
        }
    }

	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 e) {
			return false;
		}
	}
	
	public void unlock(String lockKey){
	    RLock lock = redissonClient.getLock(lockKey);
	    try {
	        if (lock.isLocked()) {
	            lock.unlock();
	        }
	    } catch (IllegalMonitorStateException localIllegalMonitorStateException) {}
	}

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

再优化一下

DistributedLocker 接口新增tryLockAndRun()方法

public interface DistributedLocker {
	/**
     * @param lockKey
     * @param unit
     * @param waitTime
     * @param leaseTime
     * @param supplier 获取锁后要执行的业务逻辑
     * @param scene 业务逻辑的场景,用于打印日志
     * @param 
     * @return
     */
    <T> T tryLockAndRun(@Nonnull String lockKey, @Nonnull TimeUnit unit, long waitTime, long leaseTime, @Nonnull Supplier<T> supplier, String scene);
}

RedissonDistributedLocker实现tryLockAndRun()方法

public class RedissonDistributedLocker implements DistributedLocker {
	@Override
    public <T> T tryLockAndRun(@Nonnull String lockKey, @Nonnull TimeUnit unit, long waitTime, long leaseTime, @Nonnull Supplier<T> supplier, String scene) {
        final long start = SystemClock.now();
        // 获取分布式锁,最长等待时间:10秒,20秒后自动释放。注意锁与事务的顺序:获取分布式锁 -> 开启事务 -> 执行业务 -> 提交事务 -> 释放分布式锁!!!
        final boolean tryLock = this.tryLock(lockKey, unit, waitTime, leaseTime);
        final long end = SystemClock.now();
        if (!tryLock) {
            log.error("[{}]获取分布式锁失败,lockKey = {},耗时{}ms", scene, lockKey, end - start);
            throw new RequestResultException(JsonCommonCodeEnum.E0004);
        }
        
        // 注意:一定是获取锁成功后,才进行try{}finally{释放锁}
        try {
        	log.info("[{}]获取分布式锁成功,lockKey = {},耗时{}ms", scene, lockKey, end - start);
            return supplier.get();
        } finally {
            this.unlock(lockKey);
        }
    }
}

你可能感兴趣的:(Ehcache/Redis,SpringBoot,redisson,spring,boot)