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

 更新时间:2024年03月05日 14:38:32   作者:喵喵@香菜  
这篇文章主要介绍了redis哨兵模式分布式锁实现与实践方式(redisson),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
(福利推荐:【腾讯云】服务器最新限时优惠活动,云服务器1核2G仅99元/年、2核4G仅768元/3年,立即抢购>>>:9i0i.cn/qcloud

(福利推荐:你还在原价购买阿里云服务器?现在阿里云0.8折限时抢购活动来啦!4核8G企业云服务器仅2998元/3年,立即抢购>>>:9i0i.cn/aliyun

一、前言

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

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

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

环境:

  • 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();
            }
        }
    }
}

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持程序员之家。

相关文章

  • Redis持久化方式之RDB和AOF的原理及优缺点

    Redis持久化方式之RDB和AOF的原理及优缺点

    在Redis中,数据可以分为两类,即内存数据和磁盘数据,Redis?提供了两种不同的持久化方式,其中?RDB?是快照备份机制,AOF?则是追加写操作机制,本文将详细给大家介绍Redis?持久化方式RDB和AOF的原理及优缺点,感兴趣的同学可以跟着小编一起来学习
    2023-06-06
  • redis中使用bloomfilter的白名单功能解决缓存穿透问题

    redis中使用bloomfilter的白名单功能解决缓存穿透问题

    本文主要介绍了redis中使用bloomfilter的白名单功能解决缓存穿透问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-07-07
  • Redis三种集群模式详解

    Redis三种集群模式详解

    redis有三种集群模式,其中主从是最常见的模式,今天通过本文给大家分享Redis三种集群模式介绍,感兴趣的朋友一起看看吧
    2021-10-10
  • 面试常问:如何保证Redis缓存和数据库的数据一致性

    面试常问:如何保证Redis缓存和数据库的数据一致性

    在实际开发过程中,缓存的使用频率是非常高的,只要使用缓存和数据库存储,就难免会出现双写时数据一致性的问题,那我们又该如何解决呢
    2021-09-09
  • Redis存取序列化与反序列化性能问题详解

    Redis存取序列化与反序列化性能问题详解

    这篇文章主要给大家介绍了关于Redis存取序列化与反序列化性能问题的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-12-12
  • Redis的KEYS 命令千万不能乱用

    Redis的KEYS 命令千万不能乱用

    这篇文章主要介绍了Redis的KEYS 命令千万不能乱用,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-07-07
  • Redisson如何解决Redis分布式锁提前释放问题

    Redisson如何解决Redis分布式锁提前释放问题

    本文主要介绍了Redisson如何解决Redis分布式锁提前释放问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-05-05
  • redis服务如何启动

    redis服务如何启动

    这篇文章主要介绍了redis服务如何启动问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-08-08
  • redis通过lua脚本,获取满足key pattern的所有值方式

    redis通过lua脚本,获取满足key pattern的所有值方式

    这篇文章主要介绍了redis通过lua脚本,获取满足key pattern的所有值方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-03-03
  • Redis+拦截器实现接口防刷

    Redis+拦截器实现接口防刷

    接口防刷有很多种实现思路,例如:拦截器/AOP+Redis、拦截器/AOP+本地缓存、前端限制等等很多种实现思路,本文主要来讲一下?拦截器+Redis?的实现方式,需要的可以参考下
    2023-08-08

最新评论

?


http://www.vxiaotou.com