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

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

(0)

相关推荐

  • 详解SpringBoot优雅编码之Lombok加持

    本文介绍了SpringBoot优雅编码之Lombok加持,分享给大家,具体如下: 概述 Lombok 通过提供简单的语法注解形式来帮助简化消除一些必须有但显得很臃肿的 java 代码.典型的是对于 POJO对象的简化(如自动帮我们生成Setter和Getter等),有了Lombok的加持,开发人员可以免去很多重复且臃肿的操作,极大地提高java代码的信噪比,因此我们必须尝试并应用起来! IntelliJ IDEA上配置 方法一:直接在IDEA界面中配置 首先进入Plugins界面: 然后搜索并安

  • SpringBoot 2.x 整合Lombok的方法示例

    简介 lombok是一个编译级别的插件,它可以在项目编译的时候生成一些代码.比如日常开发过程中需要生产大量的JavaBean文件,每个JavaBean都需要提供大量的get和set方法,如果字段较多且发生变动的话修改起来相对繁琐,相应的lombok可以通过注解(@getter,@setter)为我们省去手动创建getter和setter方法的麻烦,它能够在我们编译源码的时候自动帮我们生成getter和setter方法. 即它最终能够达到的效果是:在源码中没有getter和setter方法,但是在

  • 详解Lombok安装及Spring Boot集成Lombok

    Lombok有什么用 在我们实体Bean中有大量的Getter/Setter方法以及toString, hashCode等可能不会用到,但是某些时候仍然需要复写:在使用Lombok之后,将由其来自动帮你实现代码生成.注意,其是在编译源码过程中,帮你自动生成的.就是说,将极大减少你的代码总量. Lombok的官方地址: https://projectlombok.org/ 使用Lombok时需要注意的点 在类需要序列化.反序列化时或者需要详细控制字段时,应该谨慎考虑是否要使用Lombok,因为在这

  • Spring Boot利用Lombok减少Java中样板代码的方法示例

    前言 Lombok想要解决了的是在我们实体Bean中大量的Getter/Setter方法,以及toString, hashCode等可能不会用到,但是某些时候仍然需要复写,以期方便使用的方法:在使用Lombok之后,将由其来自动帮你实现代码生成,注意,其是在运行过程中,帮你自动生成的.就是说,将极大减少你的代码总量. Lombok的官方地址: https://projectlombok.org/ 其实说实话第一次听到Lombok的时候,是从一个带我的匈牙利老师傅那里学来的.那个时候他给了我一套J

  • Spring Boot中lombok的安装与使用详解

    前言 众所周知Spring Boot是非常高效的开发框架,lombok是一套代码模板解决方案,将极大提升开发的效率,这里介绍给大家使用.文中详细介绍了lombok的安装与使用教程,话不多说了,来一起看看详细的介绍吧. 1. Lombok Lombok想要解决了的是在我们实体Bean中大量的Getter/Setter方法,以及toString, hashCode等可能不会用到,但是某些时候仍然需要复写,以期方便使用的方法:在使用Lombok之后,将由其来自动帮你实现代码生成,注意,其是在运行过程中

  • SpringBoot使用LomBok的示例代码

    Lombok是什么?它是一个能帮我们消除那些必须要写但是重复的代码,比如setter,getter,构造函数之类的方法. 首先先简单说一下idea安装lombok,有2种方法: 1.直接从http://plugins.jetbrains.com/下载,然后放到idea安装文件下面的plugins,然后重启idea 2.在idea的settings(windows)或者Preferences(mac),下找到plugins菜单,点击Browse repositories,如图 然后搜索lombok

  • Springboot 集成 lombok.jar过程解析

    这篇文章主要介绍了Springboot 集成 lombok.jar过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 介绍 Spring Boot是非常高效的开发框架,lombok是一套代码模板解决方案,将极大提升开发的效率,这里介绍给大家使用. Lombok想要解决了的是在我们实体Bean中大量的Getter/Setter方法,以及toString, hashCode等可能不会用到,但是某些时候仍然需要复写,以期方便使用的方法:在使用Lo

  • SpringBoot集成WebSocket实现前后端消息互传的方法

    什么是WebSocket? WebSocket 协议是基于 TCP 的一种新的网络协议.它实现了浏览器与服务器全双工 (full-duplex) 通信-允许服务器主动发送信息给客户端. 为什么需要WebSocket? 大家都知道以前客户端想知道服务端的处理进度,要不停地使用 Ajax 进行轮询,让浏览器隔个几秒就向服务器发一次请求,这对服务器压力较大.另外一种轮询就是采用 long poll 的方式,这就跟打电话差不多,没收到消息就一直不挂电话,也就是说,客户端发起连接后,如果没消息,就一直不返

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

    上篇 <SpringBoot 集成 redis 分布式锁优化>对死锁的问题进行了优化,今天介绍的是 redis 官方推荐使用的 Redisson ,Redisson 架设在 redis 基础上的 Java 驻内存数据网格(In-Memory Data Grid),基于NIO的 Netty 框架上,利用了 redis 键值数据库.功能非常强大,解决了很多分布式架构中的问题. Github的wiki地址: https://github.com/redisson/redisson/wiki 官方文档

  • springboot 集成redission 以及分布式锁的使用详解

    目录 springboot集成redission及分布式锁的使用 1.引入jar包 2.增加Configuration类 3.使用redission分布式锁 Springboot整合Redisson 锁 一.依赖 二.配置文件 三.锁的使用 四.分布式秒杀 五.redis锁 单机版可用,分布式用Redisson springboot集成redission及分布式锁的使用 1.引入jar包 <dependency> <groupId>org.redisson</groupId&

  • SpringBoot整合Redisson实现分布式锁

    目录 一.添加依赖 二.redis配置文件 三.新建配置类 四.使用分布式锁 可重入锁 读写锁 信号量(Semaphore) 闭锁(CountDownLatch) Redisson是架设在redis基础上的一个Java驻内存数据网格(In-Memory Data Grid).充分的利用了Redis键值数据库提供的一系列优势,基于Java实用工具包中常用接口,为使用者提供了一系列具有分布式特性的常用工具类.使得原本作为协调单机多线程并发程序的工具包获得了协调分布式多机多线程并发系统的能力,大大降低

  • SpringBoot集成redis实现分布式锁的示例代码

    1.准备 使用redis实现分布式锁,需要用的setnx(),所以需要集成Jedis 需要引入jar,jar最好和redis的jar版本对应上,不然会出现版本冲突,使用的时候会报异常redis.clients.jedis.Jedis.set(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)Ljava/lang/String; 我使用的redis版本是2.3.0,Jedis使用的是3.3.0 <de

  • SpringBoot使用Redisson实现分布式锁(秒杀系统)

    前面讲完了Redis的分布式锁的实现,接下来讲Redisson的分布式锁的实现,一般提及到Redis的分布式锁我们更多的使用的是Redisson的分布式锁,Redis的官方也是建议我们这样去做的.Redisson点我可以直接跳转到Redisson的官方文档. 1.1.引入Maven依赖 <dependency> <groupId>org.redisson</groupId> <artifactId>redisson-spring-boot-starter&l

  • Spring Boot 集成Redisson实现分布式锁详细案例

    目录 前言 分布式锁实现 引入jar包 Redisson的配置 application.yml中引入redisson.yml配置 redisson.yml配置 封装Redisson工具类 模拟秒杀扣减库存 测试代码 总结 前言 Spring Boot集成Redis实现单机分布式锁针对单机分布式锁还是存在锁定续期.可重入的问题,本文将采用Spring Boot 集成Ression实现分布式锁进行详细讲解. 分布式锁实现 引入jar包 <dependency> <groupId>org

  • Redis实现分布式锁的方法示例

    之前我们使用的定时任务都是只部署在了单台机器上,为了解决单点的问题,为了保证一个任务,只被一台机器执行,就需要考虑锁的问题,于是就花时间研究了这个问题.到底怎样实现一个分布式锁呢? 锁的本质就是互斥,保证任何时候能有一个客户端持有同一个锁,如果考虑使用redis来实现一个分布式锁,最简单的方案就是在实例里面创建一个键值,释放锁的时候,将键值删除.但是一个可靠完善的分布式锁需要考虑的细节比较多,我们就来看看如何写一个正确的分布式锁. 单机版分布式锁 SETNX 所以我们直接基于 redis 的 s

  • ZooKeeper 实现分布式锁的方法示例

    ZooKeeper 是一个典型的分布式数据一致性解决方案,分布式应用程序可以基于 ZooKeeper 实现诸如数据发布/订阅.负载均衡.分布式协调/通知.集群管理.Master 选举.分布式锁等功能. 节点 在介绍 ZooKeeper 分布式锁前需要先了解一下 ZooKeeper 中节点(Znode),ZooKeeper 的数据存储数据模型是一棵树(Znode Tree),由斜杠(/)的进行分割的路径,就是一个 Znode(如 /locks/my_lock).每个 Znode 上都会保存自己的数

  • Springboot中如何使用Redisson实现分布式锁浅析

    目录 前言 1. 概述 2. Redisson 在 Springboot 中的使用 2.1 引入依赖 2.2 在 Springboot 配置中配置Redis 2.3 Demo代码 3. 综述 前言 在分布式场景下为了保证数据最终一致性.在单进程的系统中,存在多个线程可以同时改变某个变量(可变共享变量)时,就需要对变量或代码块做同步(lock-synchronized),使其在修改这种变量时能够线性执行消除并发修改变量.但分布式系统是多部署.多进程的,开发语言提供的并发处理API在此场景下就无能为

  • 基于Redis分布式锁Redisson及SpringBoot集成Redisson

    目录 - 分布式锁需要具备的条件和刚需 - Redisson使用 - SpringBoot集成Redisson - 分布式锁需要具备的条件和刚需 独占性:OnlyOne,任何时刻只能有且仅有一个线程持有 高可用:若redis集群环境下,不能因为某一个节点挂了而出现获取锁和释放锁失败的情况 防死锁:杜绝死锁,必须有超时控制机制或者撤销操作,有个兜底终止跳出方案 不乱抢:防止张冠李戴,不能私下unlock别人的锁,只能自己加锁自己释放 重入性:同一个节点的同一个线程如果获得锁之后,它也可以再次获取这

随机推荐