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