redis哨兵模式分布式锁实现与实践方式(redisson)

2024-04-16 0 442
目录
  • 一、前言
  • 二、redis的配置(注意是哨兵模式)
  • 三、代码实战
  • 四、使用
  • 总结

一、前言

在某个线程操作数据库中的某条数据时,我们需要确保当前时刻只有一个线程在操作这条记录,如果有两个线程竞争同一个数据,就需要在考虑先后执行顺序以后,那么怎样在一个线程拿到这条数据时,阻塞其他线程操作呢?

分布式锁就可以解决上述难题。

以下演示是利用分布式锁,确保同一时间只有一个线程在操作数据库,阻塞其他线程。

环境:

  • redis(哨兵模式)
  • spring boot

二、redis的配置(注意是哨兵模式)

1)依赖

<?xml version=\”1.0\” encoding=\”UTF-8\”?>
<project xmlns=\”http://maven.apache.org/POM/4.0.0\” xmlns:xsi=\”http://www.w3.org/2001/XMLSchema-instance\”
xsi:schemaLocation=\”http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd\”>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.3.RELEASE</version>
<relativePath/> <!– lookup parent from repository –>
</parent>
<groupId>com.zlc</groupId>
<artifactId>distributedlock-a</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>distributedlock-a</name>
<description>分布式锁(哨兵模式)</description>

<properties>
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.3.2</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>

2)redis配置

server:
port: 8081
spring:
redis:
sentinel:
master: testmaster
nodes: 127.0.0.1:26379,127.0.0.1:36379,127.0.0.1:16379
timeout: 3000 # 超时时间(数据处理超时时间,不是连接超时时间)
lettuce:
pool:
max-active: 200 #连接池最大连接数(使用负值表示没有限制)
max-idle: 20 #连接池中的最大空闲连接
min-idle: 5 #连接池中的最小空闲连接
max-wait: -1 #连接池最大阻塞等待时间(使用负值表示没有限制)
password: 123456 #redis 密码
database: 1 # 使用的是库1,如果不配置,则使用默认的0

三、代码实战

根据上面的配置文件,将redis的各个配置转换为实体对象

1)sentinel 节点

package com.zlc.distributedlocka.model.redis;

import lombok.Data;
import lombok.ToString;

/**
* @author : 追到乌云的尽头找太阳-(Jacob)
* @date : 2020/1/20 11:13
**/
@Data
@ToString
public class RedisSentinelModel {

private String master;

private String nodes;
}

2)pool节点

package com.zlc.distributedlocka.model.redis;

import lombok.Data;
import lombok.ToString;

/**
* @author : 追到乌云的尽头找太阳-(Jacob)
* @date : 2020/1/20 11:16
**/
@Data
@ToString
public class RedisPoolModel {

private int maxIdle;

private int minIdle;

private int maxActive;

private int maxWait;

}

3)Lettuce 节点

package com.zlc.distributedlocka.model.redis;

import lombok.Data;

/**
* @author : 追到乌云的尽头找太阳-(Jacob)
* @date : 2020/1/20 11:18
**/
@Data
public class RedisLettuceConfig {

private RedisPoolModel redisPoolModel;

}

4)redis

package com.zlc.distributedlocka.model.redis;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

/**
* @author : 追到乌云的尽头找太阳-(Jacob)
* @date : 2020/1/20 11:21
**/
@Data
@Configuration
@ConfigurationProperties(prefix = \”spring.redis\”)
public class RedisModel {

private int database;
/**
* 等待节点回复命令的时间。该时间从命令发送成功时开始计时
**/
private int timeout;

private String password;

/**
* 池配置
*/
private RedisLettuceModel lettuce;

/**
* 哨兵配置
*/
private RedisSentinelModel sentinel;
}

5)redisson

package com.zlc.distributedlocka.config;

import com.zlc.distributedlocka.model.redis.RedisModel;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.config.ReadMode;
import org.redisson.config.SentinelServersConfig;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
* @author : 追到乌云的尽头找太阳-(Jacob)
* @date : 2020/1/20 11:27
**/
@Configuration
@EnableConfigurationProperties(RedisModel.class)
public class RedissonConfig {

private final RedisModel redisModel;

public RedissonConfig(RedisModel redisModel) {
this.redisModel = redisModel;
}

@Bean
public RedissonClient redissonClient(){
Config config = new Config();
String [] nodes = redisModel.getSentinel().getNodes().split(\”,\”);
List<String> newNodes = new ArrayList<>(nodes.length);
newNodes.addAll(Arrays.asList(nodes));

SentinelServersConfig serverConfig = config.useSentinelServers()
.addSentinelAddress(newNodes.toArray(new String[0]))
.setMasterName(redisModel.getSentinel().getMaster())
.setReadMode(ReadMode.SLAVE)
.setTimeout(redisModel.getTimeout());
// 设置密码
if(StringUtils.isNotBlank(redisModel.getPassword())){
serverConfig.setPassword(redisModel.getPassword());
}
// 设置database
if (redisModel.getDatabase()!=0){
serverConfig.setDatabase(redisModel.getDatabase());
}
return Redisson.create(config);
}
}

四、使用

一个简单的使用方法示例

package com.zlc.distributedlocka;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import java.util.concurrent.TimeUnit;

@SpringBootApplication
public class DistributedlockAApplication {

@Autowired
private RedissonClient redissonClient;

public static void main(String[] args) {
SpringApplication.run(DistributedlockAApplication.class, args);
}

// 这里的锁是对哨兵模式下的database生效的,
// 需要分布式锁的两个系统一定要使用同一哨兵模式的database
// 如果一个使用默认0,一个使用1或者其他,是锁不住的
private void TestLock(){
boolean lockFlag = false;
RLock rLock = null;
try {
// 使用redis中的某个key值作为获取分布式锁
rLock = redissonClient.getLock(\”redisKey\”);
// 第一个参数为等待时间,第二个参数为占有时间(单位都为毫秒)
// 等待时间为如果没有通过redisKey获取到锁,则等待1s,1s后还没获取到锁,则tryLock返回false,表明有人正在使用
// 如果直接获取到锁了,则表明没有人使用,设置了你占有他的时间为5s
lockFlag = rLock.tryLock(1000, 5000, TimeUnit.MILLISECONDS);
if (lockFlag){
// 获取到锁,进行数据处理或者其他操作
}else {
// 没有获取到锁,进行一些操作
}
}catch (Exception e){
e.printStackTrace();
}finally {
// 如果锁没有释放,手动释放锁
// 注意是使用isHeldByCurrentThread
if (lockFlag && rLock.isHeldByCurrentThread()){
rLock.unlock();
}
}
}
}

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持悠久资源。

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

悠久资源 Redis redis哨兵模式分布式锁实现与实践方式(redisson) https://www.u-9.cn/database/redis/186128.html

常见问题

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务