redis哨兵模式分布式鎖實(shí)現(xiàn)與實(shí)踐方式(redisson)
一、前言
在某個(gè)線程操作數(shù)據(jù)庫中的某條數(shù)據(jù)時(shí),我們需要確保當(dāng)前時(shí)刻只有一個(gè)線程在操作這條記錄,如果有兩個(gè)線程競爭同一個(gè)數(shù)據(jù),就需要在考慮先后執(zhí)行順序以后,那么怎樣在一個(gè)線程拿到這條數(shù)據(jù)時(shí),阻塞其他線程操作呢?
分布式鎖就可以解決上述難題。
以下演示是利用分布式鎖,確保同一時(shí)間只有一個(gè)線程在操作數(shù)據(jù)庫,阻塞其他線程。
環(huán)境:
- 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 # 超時(shí)時(shí)間(數(shù)據(jù)處理超時(shí)時(shí)間,不是連接超時(shí)時(shí)間)
lettuce:
pool:
max-active: 200 #連接池最大連接數(shù)(使用負(fù)值表示沒有限制)
max-idle: 20 #連接池中的最大空閑連接
min-idle: 5 #連接池中的最小空閑連接
max-wait: -1 #連接池最大阻塞等待時(shí)間(使用負(fù)值表示沒有限制)
password: 123456 #redis 密碼
database: 1 # 使用的是庫1,如果不配置,則使用默認(rèn)的0
三、代碼實(shí)戰(zhàn)
根據(jù)上面的配置文件,將redis的各個(gè)配置轉(zhuǎn)換為實(shí)體對(duì)象
1)sentinel 節(jié)點(diǎn)
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節(jié)點(diǎn)
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 節(jié)點(diǎn)
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;
/**
* 等待節(jié)點(diǎn)回復(fù)命令的時(shí)間。該時(shí)間從命令發(fā)送成功時(shí)開始計(jì)時(shí)
**/
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());
// 設(shè)置密碼
if(StringUtils.isNotBlank(redisModel.getPassword())){
serverConfig.setPassword(redisModel.getPassword());
}
// 設(shè)置database
if (redisModel.getDatabase()!=0){
serverConfig.setDatabase(redisModel.getDatabase());
}
return Redisson.create(config);
}
}
四、使用
一個(gè)簡單的使用方法示例
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);
}
// 這里的鎖是對(duì)哨兵模式下的database生效的,
// 需要分布式鎖的兩個(gè)系統(tǒng)一定要使用同一哨兵模式的database
// 如果一個(gè)使用默認(rèn)0,一個(gè)使用1或者其他,是鎖不住的
private void TestLock(){
boolean lockFlag = false;
RLock rLock = null;
try {
// 使用redis中的某個(gè)key值作為獲取分布式鎖
rLock = redissonClient.getLock("redisKey");
// 第一個(gè)參數(shù)為等待時(shí)間,第二個(gè)參數(shù)為占有時(shí)間(單位都為毫秒)
// 等待時(shí)間為如果沒有通過redisKey獲取到鎖,則等待1s,1s后還沒獲取到鎖,則tryLock返回false,表明有人正在使用
// 如果直接獲取到鎖了,則表明沒有人使用,設(shè)置了你占有他的時(shí)間為5s
lockFlag = rLock.tryLock(1000, 5000, TimeUnit.MILLISECONDS);
if (lockFlag){
// 獲取到鎖,進(jìn)行數(shù)據(jù)處理或者其他操作
}else {
// 沒有獲取到鎖,進(jìn)行一些操作
}
}catch (Exception e){
e.printStackTrace();
}finally {
// 如果鎖沒有釋放,手動(dòng)釋放鎖
// 注意是使用isHeldByCurrentThread
if (lockFlag && rLock.isHeldByCurrentThread()){
rLock.unlock();
}
}
}
}
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
CentOS7.5使用mysql_multi方式安裝MySQL5.7.28多實(shí)例(詳解)
這篇文章主要介紹了CentOS7.5使用mysql_multi方式安裝MySQL5.7.28多實(shí)例,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-01-01
Redis集群的三種部署方式及三種應(yīng)用問題的處理
這篇文章主要介紹了Redis集群的三種部署方式及三種應(yīng)用問題的處理,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-04-04
基于Redis實(shí)現(xiàn)短信驗(yàn)證碼登錄項(xiàng)目示例(附源碼)
手機(jī)登錄驗(yàn)證在很多網(wǎng)頁上都得到使用,本文主要介紹了基于Redis實(shí)現(xiàn)短信驗(yàn)證碼登錄項(xiàng)目示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-05-05
Redis6.2.6生產(chǎn)環(huán)境redis.conf單機(jī)配置
在實(shí)際生產(chǎn)環(huán)境中,為了保障 Redis 的穩(wěn)定性和高性能,我們往往需要對(duì)默認(rèn)配置進(jìn)行一系列優(yōu)化,本文主要介紹了Redis6.2.6生產(chǎn)環(huán)境redis.conf單機(jī)配置,感興趣的可以了解一下2025-04-04

