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