当前位置: 代码网 > it编程>数据库>Redis > redis哨兵模式分布式锁实现与实践方式(redisson)

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

2024年05月18日 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)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com