上篇 《SpringBoot 集成 redis 分布式锁优化》对死锁的问题进行了优化,今天介绍的是 redis 官方推荐使用的 Redisson ,Redisson 架设在 redis 基础上的 Java 驻内存数据网格(In-Memory Data Grid),基于NIO的 Netty 框架上,利用了 redis 键值数据库。功能非常强大,解决了很多分布式架构中的问题。
Github的wiki地址: https://github.com/redisson/redisson/wiki
官方文档: https://github.com/redisson/redisson/wiki/目录
项目代码结构图:
导入依赖
<dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.8.0</version> </dependency>
属性配置
在 application.properites 资源文件中添加单机&哨兵相关配置
server.port=3000 # redisson lock 单机模式 redisson.address=redis://127.0.0.1:6379 redisson.password= #哨兵模式 #redisson.master-name= master #redisson.password= #redisson.sentinel-addresses=10.47.91.83:26379,10.47.91.83:26380,10.47.91.83:26381
注意:
这里如果不加 redis:// 前缀会报 URI 构建错误
Caused by: java.net.URISyntaxException: Illegal character in scheme name at index 0
更多的配置信息可以去官网查看
定义Lock的接口定义类
package com.tuhu.thirdsample.service; import org.redisson.api.RLock; import java.util.concurrent.TimeUnit; /** * @author chendesheng * @create 2019/10/12 10:48 */ public interface DistributedLocker { RLock lock(String lockKey); RLock lock(String lockKey, int timeout); RLock lock(String lockKey, TimeUnit unit, int timeout); boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime); void unlock(String lockKey); void unlock(RLock lock); }
Lock接口实现类
package com.tuhu.thirdsample.service; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import java.util.concurrent.TimeUnit; /** * @author chendesheng * @create 2019/10/12 10:49 */ public class RedissonDistributedLocker implements DistributedLocker{ private RedissonClient redissonClient; @Override public RLock lock(String lockKey) { RLock lock = redissonClient.getLock(lockKey); lock.lock(); return lock; } @Override public RLock lock(String lockKey, int leaseTime) { RLock lock = redissonClient.getLock(lockKey); lock.lock(leaseTime, TimeUnit.SECONDS); return lock; } @Override public RLock lock(String lockKey, TimeUnit unit ,int timeout) { RLock lock = redissonClient.getLock(lockKey); lock.lock(timeout, unit); return lock; } @Override public boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime) { RLock lock = redissonClient.getLock(lockKey); try { return lock.tryLock(waitTime, leaseTime, unit); } catch (InterruptedException e) { return false; } } @Override public void unlock(String lockKey) { RLock lock = redissonClient.getLock(lockKey); lock.unlock(); } @Override public void unlock(RLock lock) { lock.unlock(); } public void setRedissonClient(RedissonClient redissonClient) { this.redissonClient = redissonClient; } }
redisson属性装配类
package com.tuhu.thirdsample.common; import lombok.Data; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; /** * @author chendesheng * @create 2019/10/11 20:04 */ @Configuration @ConfigurationProperties(prefix = "redisson") @ConditionalOnProperty("redisson.password") @Data public class RedissonProperties { private int timeout = 3000; private String address; private String password; private int database = 0; private int connectionPoolSize = 64; private int connectionMinimumIdleSize=10; private int slaveConnectionPoolSize = 250; private int masterConnectionPoolSize = 250; private String[] sentinelAddresses; private String masterName; }
SpringBoot自动装配类
package com.tuhu.thirdsample.configuration; import com.tuhu.thirdsample.common.RedissonProperties; import com.tuhu.thirdsample.service.DistributedLocker; import com.tuhu.thirdsample.service.RedissonDistributedLocker; import com.tuhu.thirdsample.util.RedissonLockUtil; import org.apache.commons.lang3.StringUtils; import org.redisson.Redisson; import org.redisson.api.RedissonClient; import org.redisson.config.Config; import org.redisson.config.SentinelServersConfig; import org.redisson.config.SingleServerConfig; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @author chendesheng * @create 2019/10/12 10:50 */ @Configuration @ConditionalOnClass(Config.class) @EnableConfigurationProperties(RedissonProperties.class) public class RedissonAutoConfiguration { @Autowired private RedissonProperties redissonProperties; /** * 哨兵模式自动装配 * @return */ @Bean @ConditionalOnProperty(name="redisson.master-name") RedissonClient redissonSentinel() { Config config = new Config(); SentinelServersConfig serverConfig = config.useSentinelServers().addSentinelAddress(redissonProperties.getSentinelAddresses()) .setMasterName(redissonProperties.getMasterName()) .setTimeout(redissonProperties.getTimeout()) .setMasterConnectionPoolSize(redissonProperties.getMasterConnectionPoolSize()) .setSlaveConnectionPoolSize(redissonProperties.getSlaveConnectionPoolSize()); if(StringUtils.isNotBlank(redissonProperties.getPassword())) { serverConfig.setPassword(redissonProperties.getPassword()); } return Redisson.create(config); } /** * 单机模式自动装配 * @return */ @Bean @ConditionalOnProperty(name="redisson.address") RedissonClient redissonSingle() { Config config = new Config(); SingleServerConfig serverConfig = config.useSingleServer() .setAddress(redissonProperties.getAddress()) .setTimeout(redissonProperties.getTimeout()) .setConnectionPoolSize(redissonProperties.getConnectionPoolSize()) .setConnectionMinimumIdleSize(redissonProperties.getConnectionMinimumIdleSize()); if(StringUtils.isNotBlank(redissonProperties.getPassword())) { serverConfig.setPassword(redissonProperties.getPassword()); } return Redisson.create(config); } /** * 装配locker类,并将实例注入到RedissLockUtil中 * @return */ @Bean DistributedLocker distributedLocker(RedissonClient redissonClient) { DistributedLocker locker = new RedissonDistributedLocker(); ((RedissonDistributedLocker) locker).setRedissonClient(redissonClient); RedissonLockUtil.setLocker(locker); return locker; } }
Lock帮助类
package com.tuhu.thirdsample.util; import com.tuhu.thirdsample.service.DistributedLocker; import org.redisson.api.RLock; import java.util.concurrent.TimeUnit; /** * @author chendesheng * @create 2019/10/12 10:54 */ public class RedissonLockUtil { private static DistributedLocker redissLock; public static void setLocker(DistributedLocker locker) { redissLock = locker; } /** * 加锁 * @param lockKey * @return */ public static RLock lock(String lockKey) { return redissLock.lock(lockKey); } /** * 释放锁 * @param lockKey */ public static void unlock(String lockKey) { redissLock.unlock(lockKey); } /** * 释放锁 * @param lock */ public static void unlock(RLock lock) { redissLock.unlock(lock); } /** * 带超时的锁 * @param lockKey * @param timeout 超时时间 单位:秒 */ public static RLock lock(String lockKey, int timeout) { return redissLock.lock(lockKey, timeout); } /** * 带超时的锁 * @param lockKey * @param unit 时间单位 * @param timeout 超时时间 */ public static RLock lock(String lockKey, TimeUnit unit , int timeout) { return redissLock.lock(lockKey, unit, timeout); } /** * 尝试获取锁 * @param lockKey * @param waitTime 最多等待时间 * @param leaseTime 上锁后自动释放锁时间 * @return */ public static boolean tryLock(String lockKey, int waitTime, int leaseTime) { return redissLock.tryLock(lockKey, TimeUnit.SECONDS, waitTime, leaseTime); } /** * 尝试获取锁 * @param lockKey * @param unit 时间单位 * @param waitTime 最多等待时间 * @param leaseTime 上锁后自动释放锁时间 * @return */ public static boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime) { return redissLock.tryLock(lockKey, unit, waitTime, leaseTime); } }
控制层
package com.tuhu.thirdsample.task; import com.tuhu.thirdsample.common.KeyConst; import lombok.extern.slf4j.Slf4j; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.concurrent.TimeUnit; /** * @author chendesheng * @create 2019/10/12 11:03 */ @RestController @RequestMapping("/lock") @Slf4j public class LockController { @Autowired RedissonClient redissonClient; @GetMapping("/task") public void task(){ log.info("task start"); RLock lock = redissonClient.getLock(KeyConst.REDIS_LOCK_KEY); boolean getLock = false; try { if (getLock = lock.tryLock(0,5,TimeUnit.SECONDS)){ //执行业务逻辑 System.out.println("拿到锁干活"); }else { log.info("Redisson分布式锁没有获得锁:{},ThreadName:{}",KeyConst.REDIS_LOCK_KEY,Thread.currentThread().getName()); } } catch (InterruptedException e) { log.error("Redisson 获取分布式锁异常,异常信息:{}",e); }finally { if (!getLock){ return; } //如果演示的话需要注释该代码;实际应该放开 //lock.unlock(); //log.info("Redisson分布式锁释放锁:{},ThreadName :{}", KeyConst.REDIS_LOCK_KEY, Thread.currentThread().getName()); } } }
RLock 继承自 java.util.concurrent.locks.Lock
,可以将其理解为一个重入锁,需要手动加锁和释放锁 。
来看它其中的一个方法:tryLock(long waitTime, long leaseTime, TimeUnit unit)
getLock = lock.tryLock(0,5,TimeUnit.SECONDS)
通过 tryLock() 的参数可以看出,在获取该锁时如果被其他线程先拿到锁就会进入等待,等待 waitTime 时间,如果还没用机会获取到锁就放弃,返回 false;若获得了锁,除非是调用 unlock 释放,那么会一直持有锁,直到超过 leaseTime 指定的时间。
以上就是 Redisson 实现分布式锁的核心方法,有人可能要问,那怎么确定拿的是同一把锁,分布式锁在哪?
这就是 Redisson 的强大之处,其底层还是使用的 Redis 来作分布式锁,在我们的RedissonManager中已经指定了 Redis 实例,Redisson 会进行托管,其原理与我们手动实现 Redis 分布式锁类似。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持自由互联。