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

时间:2024-10-01 17:24:59

文章目录

  • 前言
    • Redisson简介
    • Redisson的优势
      • 提供了看门狗
      • 提供了多种锁
  • 实战
    • 加依赖
    • 分析源码
    • 改配置
      • 方式一()
      • 方式二()
      • 方式三()
    • 简单使用
    • 优化一下
    • 再优化一下

前言

Redisson简介

省略…

Redisson的优势

提供了看门狗

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

提供了多种锁

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

public interface RedissonClient {
	/**
	 * 获取锁(默认的非公平锁)
	 */
	RLock getLock(String name);
	
	/**
	 * 获取公平锁
	 */
	RLock getFairLock(String name);
	
	/**
	 * 获取读写锁
	 */
	RReadWriteLock getReadWriteLock(String name);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
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();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

实战

加依赖

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

<dependency>
    <groupId></groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

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

分析源码

redisson-spring-boot-starter这个包下只有2个类:RedissonPropertiesRedissonAutoConfiguration。阅读源码发现RedissonAutoConfiguration会自动注册RedissonConnectionFactoryRedissonClient
在这里插入图片描述

改配置

redisson-spring-boot-starter支持三种配置方式

  • 方式一:完全兼容spring-boot-starter-data-redis的配置,即只需在中使用来配置redisredisson-spring-boot-starter会根据Redis的配置自动配置redisson
  • 方式二:redisson方式,即在中使用=classpath:来指定redisson的配置文件路径,然后在中配置redisson
  • 方式三:redisson方式,即在中使用=xxx来直接配置Redisson

多种方式的优先级: > > 。详细参考#redisson()

方式一(spring.redis

文件:

spring:
	# ====================================================================================
    # spring-redis
    # ====================================================================================
    redis:
        password: null
        # 连接超时时间
        timeout: 10000

        # 单机
        # host: 10.210.10.154
        # port: 7004

        # 集群
        cluster:
            nodes: 10.210.13.203:6381,10.210.13.203:6382,10.210.13.203:6383,10.210.13.203:6384,10.210.13.203:6385,10.210.13.203:6386
            maxRedirects: 3

        # 连接池
        lettuce:
            pool:
                # 最大连接数
                max-active: 8
                # 最大阻塞等待时间(负数表示没限制)
                max-wait: -1
                # 最小空闲
                min-idle: 0
                # 最大空闲
                max-idle: 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

方式二()

方式三()

文件:

spring:
	# ====================================================================================
    # spring-redis
    # ====================================================================================
    redis:
        password: null
        # 连接超时时间
        timeout: 10000

        # 单机
        # host: 10.210.10.154
        # port: 7004

        # 集群
        cluster:
            nodes: 10.210.13.203:6381,10.210.13.203:6382,10.210.13.203:6383,10.210.13.203:6384,10.210.13.203:6385,10.210.13.203:6386
            maxRedirects: 3

        # 连接池
        lettuce:
            pool:
                # 最大连接数
                max-active: 8
                # 最大阻塞等待时间(负数表示没限制)
                max-wait: -1
                # 最小空闲
                min-idle: 0
                # 最大空闲
                max-idle: 8
        # ====================================================================================
        # spring-redis-redisson
        # ====================================================================================
        redisson:
            # file: classpath:
            config: |
                # 单机
                # singleServerConfig:
                    # password: null
                    # timeout: 10000
                    # address: redis://10.210.10.154:7004
                
                # 集群
                clusterServersConfig:
                    nodeAddresses: 
                    - "redis://10.210.13.203:6381"
                    - "redis://10.210.13.203:6382"
                    - "redis://10.210.13.203:6383"
                    - "redis://10.210.13.203:6384"
                    - "redis://10.210.13.203:6385"
                    - "redis://10.210.13.203:6386"
                    password: null
                    loadBalancer: !<> {}
                    slaveConnectionMinimumIdleSize: 8
                    slaveConnectionPoolSize: 16
                    sslEnableEndpointIdentification: false
                    timeout: 3000
                
                # 所有Redis节点客户端之间共享的线程数量,默认值: 当前处理核数量 * 2
                threads: 24
                # Netty线程池数量,默认值: 当前处理核数量 * 2
                nettyThreads: 12
                # 传输模式,默认值:NIO
                transportMode: NIO
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63

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

这种方式使用了yaml语法中的|,然后使用的是解析该配置项的值,此时需要注意:

  • 该配置项下的所有行,即使开头是"#",对于该配置项来说,都不会被认为是注释(都是该配置项的值的一部分),只有在后续解析它们时才会被认为是注释。
  • 该配置项下的第一行(即使开头是"#"),一定要缩进,否则该配置项的值直接为null
  • 该配置项下的第二行开始,任何一行(即使开头是"#")都不能超过第一行,否则该配置项的值直接为null
  • 该配置项下所有行,key必须与对应Properties类的对应属性名完全一致,不支持使用"-"代替驼峰,否则解析时报错
  • 该配置项下所有行,list类型的配置必须使用"- xxx",不支持单行逗号分隔,否则解析时报错
  • 该配置项下所有行,不支持使用${xxx}读取上下文的配置,否则解析后的值还是"${xxx}"

简单使用

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

  • 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 "已解锁";
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

优化一下

封装一下对redissonClient的使用。

package com.xx.core.redisson.lock;

/**
 * Redisson分布式锁接口
 * <p>
 * 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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
/**
 * 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) {}
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61

再优化一下

DistributedLocker 接口新增tryLockAndRun()方法

public interface DistributedLocker {
	/**
     * @param lockKey
     * @param unit
     * @param waitTime
     * @param leaseTime
     * @param supplier 获取锁后要执行的业务逻辑
     * @param scene 业务逻辑的场景,用于打印日志
     * @param <T>
     * @return
     */
    <T> T tryLockAndRun(@Nonnull String lockKey, @Nonnull TimeUnit unit, long waitTime, long leaseTime, @Nonnull Supplier<T> supplier, String scene);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

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);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21