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

作者:Joyu_chen 时间:2021-10-30 16:59:38 

上篇 《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/目录

项目代码结构图:

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

导入依赖


<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 指定的时间。

来源:https://juejin.im/post/5da16f0ee51d4578331cbd2d

标签:SpringBoot,Redisson,分布式锁
0
投稿

猜你喜欢

  • SQLServer批量插入数据的三种方式及性能对比

    2022-07-01 12:05:07
  • Spring Security基本原理详解

    2022-12-24 15:57:54
  • java实现单机版五子棋

    2022-12-20 20:15:07
  • Spring MVC4.1服务器端推送实现过程解析

    2021-07-24 14:53:16
  • Spring JDBC的使用方法详解

    2021-12-08 14:05:42
  • C# 解析XML和反序列化的示例

    2023-02-20 01:16:52
  • JSON序列化Redis读取出错问题解决方案

    2022-10-13 18:57:50
  • Mapper批量插入Oracle数据@InsertProvider注解

    2023-02-11 15:13:40
  • IDEA集成JProfiler11可视化工具的详细流程(安装、集成、测试)

    2021-12-12 04:05:35
  • Winform控件优化Paint事件实现圆角组件及提取绘制圆角的方法

    2022-04-04 15:41:13
  • javaweb项目如何实现手机短信登录

    2021-05-27 03:09:25
  • 基于Mybatis plus 自动代码生成器的实现代码

    2023-11-24 10:40:51
  • Java 异步实现的几种方式小结

    2022-09-23 00:26:56
  • Java 实现网络爬虫框架详细代码

    2021-12-11 05:15:43
  • 基于Java的guava开源库工具类

    2022-04-07 05:37:53
  • java实现FTP文件上传与文件下载

    2023-08-16 08:28:38
  • Java中匿名类的两种实现方式

    2022-06-17 23:57:05
  • JavaBean和SpringBean的区别及创建SpringBean方式

    2022-05-23 03:32:14
  • 使用java为pdf添加书签的方法(pdf书签制作)

    2022-03-07 14:59:11
  • Java 方法的定义与调用详解

    2023-11-04 13:52:58
  • asp之家 软件编程 m.aspxhome.com