SpringBoot集成Redisson实现分布式锁的方法示例

 更新时间:2019年10月13日 15:09:46   作者:Joyu_chen  
这篇文章主要介绍了SpringBoot集成Redisson实现分布式锁的方法示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

上篇 《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 分布式锁类似。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • Java实现文件读取和写入过程解析

    Java实现文件读取和写入过程解析

    这篇文章主要介绍了Java实现文件读取和写入过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值。,需要的朋友可以参考下
    2019-10-10
  • Java怎么获取当前时间、计算程序运行时间源码详解(超详细!)

    Java怎么获取当前时间、计算程序运行时间源码详解(超详细!)

    有的时候,我们需要查看某一段代码的性能如何,最为简单的方式,可以通过计算该段代码执行的耗时,来进行简单的判断,这篇文章主要给大家介绍了关于Java怎么获取当前时间、计算程序运行时间的相关资料,需要的朋友可以参考下
    2024-07-07
  • Mybatis如何实现关联属性懒加载

    Mybatis如何实现关联属性懒加载

    这篇文章主要介绍了Mybatis如何实现关联属性懒加载的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • Spring Cloud 的 Hystrix.功能及实践详解

    Spring Cloud 的 Hystrix.功能及实践详解

    这篇文章主要介绍了Spring Cloud 的 Hystrix.功能及实践详解,Hystrix 具备服务降级、服务熔断、线程和信号隔离、请求缓存、请求合并以及服务监控等强大功能,需要的朋友可以参考下
    2019-07-07
  • 深入剖析java中的集合框架

    深入剖析java中的集合框架

    下面小编就为大家带来一篇深入剖析java中的集合框架。小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-05-05
  • Spring Boot缓存实战之Redis 设置有效时间和自动刷新缓存功能(时间支持在配置文件中配置)

    Spring Boot缓存实战之Redis 设置有效时间和自动刷新缓存功能(时间支持在配置文件中配置)

    这篇文章主要介绍了Spring Boot缓存实战 Redis 设置有效时间和自动刷新缓存,时间支持在配置文件中配置,需要的朋友可以参考下
    2023-05-05
  • 在IDEA使用中directory和package的操作

    在IDEA使用中directory和package的操作

    这篇文章主要介绍了在IDEA使用中directory和package的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-02-02
  • Spring Boot 异步框架的使用详解

    Spring Boot 异步框架的使用详解

    这篇文章主要介绍了Spring Boot 异步框架的使用详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-01-01
  • Java实现PDF转图片的三种方法

    Java实现PDF转图片的三种方法

    有些时候我们需要在项目中展示PDF,所以我们可以将PDF转为图片,然后已图片的方式展示,效果很好,Java使用各种技术将pdf转换成图片格式,并且内容不失帧,本文给大家介绍了三种方法实现PDF转图片的案例,需要的朋友可以参考下
    2023-10-10
  • Spring Boot项目打包指定包名实现示例

    Spring Boot项目打包指定包名实现示例

    这篇文章主要为大家介绍了Spring Boot项目打包指定包名实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-11-11

最新评论