一、前言
在某个线程操作数据库中的某条数据时,我们需要确保当前时刻只有一个线程在操作这条记录,如果有两个线程竞争同一个数据,就需要在考虑先后执行顺序以后,那么怎样在一个线程拿到这条数据时,阻塞其他线程操作呢?
分布式锁就可以解决上述难题。
以下演示是利用分布式锁,确保同一时间只有一个线程在操作数据库,阻塞其他线程。
环境:
- 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();
}
}
}
}
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论