创建工程 redis_distributed_lock2
POM
<?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.7.10</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.xfcy</groupId>
<artifactId>redis_distributed_lock2</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>redis_distributed_lock2</name>
<description>redis_distributed_lock2</description>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<lombok.version>1.16.18</lombok.version>
</properties>
<dependencies>
<!-- redisson-->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.13.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--SpringBoot与Redis整合依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!--swagger2-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<!--通用基础配置lombok/hutool-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.8</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
YML
server.port=7777
spring.application.name=redis_distributed_lock2
# ========================swagger2=====================
# http://localhost:7777/swagger-ui.html
swagger2.enabled=true
spring.mvc.pathmatch.matching-strategy=ant_path_matcher
# ========================redis单机=====================
spring.redis.database=0
spring.redis.host=192.168.238.111
spring.redis.port=6379
spring.redis.password=123456
spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.max-wait=-1ms
spring.redis.lettuce.pool.max-idle=8
spring.redis.lettuce.pool.min-idle=0
主启动类
@SpringBootApplication
public class RedisDistributedLock2Application {
public static void main(String[] args) {
SpringApplication.run(RedisDistributedLock2Application.class, args);
}
}
业务类
Swagger2Config
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
/**
* @author 晓风残月Lx
* @date 2023/4/1 10:25
*/
@Configuration
@EnableSwagger2
public class Swagger2Config {
@Value("${swagger2.enabled}")
private Boolean enabled;
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.enable(enabled)
.select()
.apis(RequestHandlerSelectors.basePackage("com.xfcy"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("springboot利用swagger2构建api接口文档 "+"\t"+ DateTimeFormatter.ofPattern("yyyy-MM-dd").format(LocalDateTime.now()))
.description("springboot+redis整合")
.version("1.0")
.termsOfServiceUrl("https://www.baidu.com/")
.build();
}
}
RedisConfig
import org.redisson.Redisson;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* @author 晓风残月Lx
* @date 2023/4/1 10:31
*/
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory)
{
RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(lettuceConnectionFactory);
//设置key序列化方式string
redisTemplate.setKeySerializer(new StringRedisSerializer());
//设置value的序列化方式json
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
InventoryService
import cn.hutool.core.util.IdUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author 晓风残月Lx
* @date 2023/4/1 10:34
*/
@Service
@Slf4j
public class InventoryService
{
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Value("${server.port}")
private String port;
private Lock lock = new ReentrantLock();
public String sale()
{
String retMessage = "";
lock.lock();
try
{
//1 查询库存信息
String result = stringRedisTemplate.opsForValue().get("inventory001");
//2 判断库存是否足够
Integer inventoryNumber = result == null ? 0 : Integer.parseInt(result);
//3 扣减库存
if(inventoryNumber > 0) {
stringRedisTemplate.opsForValue().set("inventory001",String.valueOf(--inventoryNumber));
retMessage = "成功卖出一个商品,库存剩余: "+inventoryNumber;
System.out.println(retMessage);
}else{
retMessage = "商品卖完了,o(╥﹏╥)o";
}
}finally {
lock.unlock();
}
return retMessage+"\t"+"服务端口号:"+port;
}
}
InvetoryController
import com.xfcy.service.InventoryService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author 晓风残月Lx
* @date 2023/4/1 10:32
*/
@RestController
@Api(tags = "redis分布式锁测试")
public class InvetoryController {
@Autowired
private InventoryService inventoryService;
@ApiOperation("扣减库存sale,一次卖一个")
@GetMapping(value = "/inventory/sale")
public String sale()
{
return inventoryService.sale();
}
@ApiOperation("扣减库存saleByRedisson,一次卖一个")
@GetMapping(value = "/inventory/saleByRedisson")
public String saleByRedisson()
{
return inventoryService.saleByRedisson();
}
}
InventoryService 实现的一个简单版本的 分布式锁
import cn.hutool.core.util.IdUtil;
import com.xfcy.mylock.DistributedLockFactory;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author 晓风残月Lx
* @date 2023/4/1 10:34
*/
@Service
@Slf4j
public class InventoryService {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Value("${server.port}")
private String port;
/**
* v6.0 使用 Lua 脚本 将 final的判断 + del 弄成原子操作
* 问题: 兼顾锁的可重入性
* 但是基本 v6.0 版本已经够用
* @return
*/
public String sale() {
String retMessage = "";
String key = "xfcyRedisLock";
String value = IdUtil.simpleUUID() + ":" + Thread.currentThread().getId();
while (!stringRedisTemplate.opsForValue().setIfAbsent(key, value, 30L, TimeUnit.SECONDS)) {
// 暂停20毫秒,进行递归重试
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 抢锁成功的请求线程,进行正常的业务逻辑操作,扣减库存
try {
//1 查询库存信息
String result = stringRedisTemplate.opsForValue().get("inventory001");
//2 判断库存是否足够
Integer inventoryNumber = result == null ? 0 : Integer.parseInt(result);
//3 扣减库存
if (inventoryNumber > 0) {
stringRedisTemplate.opsForValue().set("inventory001", String.valueOf(--inventoryNumber));
retMessage = "成功卖出一个商品,库存剩余: " + inventoryNumber;
System.out.println(retMessage);
} else {
retMessage = "商品卖完了,o(╥﹏╥)o";
}
}finally {
// 改进点,修改为Lua脚本的Redis分布式锁调用,必须保证原子性
String luaScript =
"if (redis.call('get',KEYS[1]) == ARGV[1]) then " +
"return redis.call('del',KEYS[1]) " +
"else " +
"return 0 " +
"end";
stringRedisTemplate.execute(new DefaultRedisScript<>(luaScript, Boolean.class), Arrays.asList(key), value);
}
return retMessage + "\t" + "服务端口号:" + port;
}
/**
* v5.0 存在问题: final的判断 + del 不是一行原子操作,需要lua脚本进行修改
* @return
*/
// public String sale() {
// String retMessage = "";
// String key = "xfcyRedisLock";
// String value = IdUtil.simpleUUID() + ":" + Thread.currentThread().getId();
// while (!stringRedisTemplate.opsForValue().setIfAbsent(key, value, 30L, TimeUnit.SECONDS)) {
// // 暂停20毫秒,进行递归重试
// try {
// Thread.sleep(20);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
// // 抢锁成功的请求线程,进行正常的业务逻辑操作,扣减库存
// try {
// //1 查询库存信息
// String result = stringRedisTemplate.opsForValue().get("inventory001");
// //2 判断库存是否足够
// Integer inventoryNumber = result == null ? 0 : Integer.parseInt(result);
// //3 扣减库存
// if (inventoryNumber > 0) {
// stringRedisTemplate.opsForValue().set("inventory001", String.valueOf(--inventoryNumber));
// retMessage = "成功卖出一个商品,库存剩余: " + inventoryNumber;
// System.out.println(retMessage);
// } else {
// retMessage = "商品卖完了,o(╥﹏╥)o";
// }
// }finally {
// // 改进点,只能删除自己的key,而不是别的客户端
// // 问题:不能保证原子操作
// if (stringRedisTemplate.opsForValue().get(key).equalsIgnoreCase(value)) {
// stringRedisTemplate.delete(key);
// }
// }
// return retMessage + "\t" + "服务端口号:" + port;
// }
/**
* v4.0 加了过期时间 stringRedisTemplate.opsForValue().setIfAbsent(key, value, 30L, TimeUnit.SECONDS)
* 问题:误删锁,如果线程A运行时间超出了过期时间
* 在线程A运行时,xfcyRedisLock这个key过期,另一个线程B进来加了key
* 线程A结束后,把线程B的锁删了
* stringRedisTemplate.delete(key); 只能自己删除自己的,需要添加判断是否是自己的锁
* @return
*/
// public String sale() {
// String retMessage = "";
// String key = "xfcyRedisLock";
// String value = IdUtil.simpleUUID() + ":" + Thread.currentThread().getId();
//
// // 不用递归了,高并发下容易出错,我们用自旋替代递归方法调用;也不用if,用while来替代
// while (!stringRedisTemplate.opsForValue().setIfAbsent(key, value, 30L, TimeUnit.SECONDS)) {
// // 暂停20毫秒,进行递归重试
// try {
// Thread.sleep(20);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
// // 无法保证原子性
stringRedisTemplate.expire(key, 30L, TimeUnit.SECONDS);
//
// // 抢锁成功的请求线程,进行正常的业务逻辑操作,扣减库存
// try {
// //1 查询库存信息
// String result = stringRedisTemplate.opsForValue().get("inventory001");
// //2 判断库存是否足够
// Integer inventoryNumber = result == null ? 0 : Integer.parseInt(result);
// //3 扣减库存
// if (inventoryNumber > 0) {
// stringRedisTemplate.opsForValue().set("inventory001", String.valueOf(--inventoryNumber));
// retMessage = "成功卖出一个商品,库存剩余: " + inventoryNumber;
// System.out.println(retMessage);
// } else {
// retMessage = "商品卖完了,o(╥﹏╥)o";
// }
// }finally {
// stringRedisTemplate.delete(key);
// }
// return retMessage + "\t" + "服务端口号:" + port;
// }
/**
* 3.2版 setnx 用while判断
* 问题: setnx过后,正在进行业务逻辑操作时,没有走到finally之前,整个微服务down机了,导致锁一直存在
* 不是程序出了问题,如果程序问题,最后还是会执行finally
* 没办法保证解锁(没有过期时间,该key一直存在),需要加入过期时间限定key
*/
// public String sale() {
// String retMessage = "";
// String key = "xfcyRedisLock";
// String value = IdUtil.simpleUUID() + ":" + Thread.currentThread().getId();
//
// // 不用递归了,高并发下容易出错,我们用自旋替代递归方法调用;也不用if,用while来替代
// while (!stringRedisTemplate.opsForValue().setIfAbsent(key, value)) {
// // 暂停20毫秒,进行递归重试
// try {
// Thread.sleep(20);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
// // 抢锁成功的请求线程,进行正常的业务逻辑操作,扣减库存
// try {
// //1 查询库存信息
// String result = stringRedisTemplate.opsForValue().get("inventory001");
// //2 判断库存是否足够
// Integer inventoryNumber = result == null ? 0 : Integer.parseInt(result);
// //3 扣减库存
// if (inventoryNumber > 0) {
// stringRedisTemplate.opsForValue().set("inventory001", String.valueOf(--inventoryNumber));
// retMessage = "成功卖出一个商品,库存剩余: " + inventoryNumber;
// System.out.println(retMessage);
// } else {
// retMessage = "商品卖完了,o(╥﹏╥)o";
// }
// }finally {
// stringRedisTemplate.delete(key);
// }
// return retMessage + "\t" + "服务端口号:" + port;
// }
/**
* 3.1版 setnx 递归调用 容易导致 StackOverflowError
* 高并发唤醒后推荐用while判断而不是if
* @return
*/
// public String sale() {
// String retMessage = "";
// String key = "xfcyRedisLock";
// String value = IdUtil.simpleUUID() + ":" + Thread.currentThread().getId();
//
// Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, value);
// // flag = false 抢不到的线程要继续重试 。。。
// if (!flag) {
// // 暂停20毫秒,进行递归重试
// try {
// Thread.sleep(20);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// sale();
// } else {
// try {
// //1 查询库存信息
// String result = stringRedisTemplate.opsForValue().get("inventory001");
// //2 判断库存是否足够
// Integer inventoryNumber = result == null ? 0 : Integer.parseInt(result);
// //3 扣减库存
// if (inventoryNumber > 0) {
// stringRedisTemplate.opsForValue().set("inventory001", String.valueOf(--inventoryNumber));
// retMessage = "成功卖出一个商品,库存剩余: " + inventoryNumber;
// System.out.println(retMessage);
// } else {
// retMessage = "商品卖完了,o(╥﹏╥)o";
// }
// } finally {
// stringRedisTemplate.delete(key);
// }
// }
// return retMessage + "\t" + "服务端口号:" + port;
// }
/**
* v2.0 单机版加锁配合nginx和jmeter压测后,不满足高并发分布式锁的性能要求,出现超卖
*/
// private Lock lock = new ReentrantLock();
//
// public String sale() {
// String retMessage = "";
// lock.lock();
// try {
// //1 查询库存信息
// String result = stringRedisTemplate.opsForValue().get("inventory001");
// //2 判断库存是否足够
// Integer inventoryNumber = result == null ? 0 : Integer.parseInt(result);
// //3 扣减库存
// if (inventoryNumber > 0) {
// stringRedisTemplate.opsForValue().set("inventory001", String.valueOf(--inventoryNumber));
// retMessage = "成功卖出一个商品,库存剩余: " + inventoryNumber;
// System.out.println(retMessage);
// } else {
// retMessage = "商品卖完了,o(╥﹏╥)o";
// }
// } finally {
// lock.unlock();
// }
// return retMessage + "\t" + "服务端口号:" + port;
// }
}
v 8.0 其实面对不是特别高的并发场景足够用了,单机redis也够用了
InventoryService
@Autowired
private DistributedLockFactory distributedLockFactory;
/**
* v8.0 实现自动续期功能的完善,后台自定义扫描程序,如果规定时间内没有完成业务逻辑,会调用加钟自动续期的脚本
*
* @return
*/
public String sale() {
String retMessage = "";
Lock redisLock = distributedLockFactory.getDistributedLock("redis");
redisLock.lock();
try {
//1 查询库存信息
String result = stringRedisTemplate.opsForValue().get("inventory001");
//2 判断库存是否足够
Integer inventoryNumber = result == null ? 0 : Integer.parseInt(result);
//3 扣减库存
if (inventoryNumber > 0) {
stringRedisTemplate.opsForValue().set("inventory001", String.valueOf(--inventoryNumber));
retMessage = "成功卖出一个商品,库存剩余: " + inventoryNumber;
// 演示自动续期的的功能
// try {
// TimeUnit.SECONDS.sleep(120);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
} else {
retMessage = "商品卖完了,o(╥﹏╥)o";
}
} finally {
redisLock.unlock();
}
return retMessage + "\t" + "服务端口号:" + port;
}
/**
* v7.0 兼顾锁的可重入性 setnx不满足了,需要hash结构的hset
* 上锁和解锁都用 Lua 脚本实现原子性
* 引入工厂模式 DistributedLockFactory 实现Lock接口 ,实现 redis的可重入锁
*
* @return
*/
// //private Lock redisDistributedLock = new RedisDistributedLock(stringRedisTemplate, "xfcyRedisLock");
//
// public String sale() {
// String retMessage = "";
//
// Lock redisLock = distributedLockFactory.getDistributedLock("redis");
// redisLock.lock();
//
// //redisDistributedLock.lock();
// try {
// //1 查询库存信息
// String result = stringRedisTemplate.opsForValue().get("inventory001");
// //2 判断库存是否足够
// Integer inventoryNumber = result == null ? 0 : Integer.parseInt(result);
// //3 扣减库存
// if (inventoryNumber > 0) {
// stringRedisTemplate.opsForValue().set("inventory001", String.valueOf(--inventoryNumber));
// retMessage = "成功卖出一个商品,库存剩余: " + inventoryNumber;
// System.out.println(retMessage);
//
// // 测试可重入性
// //testReEntry();
//
// } else {
// retMessage = "商品卖完了,o(╥﹏╥)o";
// }
// } finally {
// redisLock.unlock();
// //redisDistributedLock.unlock();
// }
// return retMessage + "\t" + "服务端口号:" + port;
// }
//
// private void testReEntry() {
// Lock redisLock = distributedLockFactory.getDistributedLock("redis");
// redisLock.lock();
//
// //redisDistributedLock.lock();
// try {
// System.out.println("测试可重入锁");
// } finally {
// redisLock.unlock();
// //redisDistributedLock.unlock();
// }
// }
mylock/DistributedLockFactory
package com.xfcy.mylock;
import cn.hutool.core.util.IdUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.util.concurrent.locks.Lock;
/**
* @author 晓风残月Lx
* @date 2023/4/1 22:14
*/
@Component
public class DistributedLockFactory {
@Autowired
private StringRedisTemplate stringRedisTemplate;
private String lockName;
private String uuidValue;
public DistributedLockFactory() {
this.uuidValue = IdUtil.simpleUUID();
}
public Lock getDistributedLock(String lockType) {
if (lockType == null) {
return null;
}
if (lockType.equalsIgnoreCase("REDIS")) {
this.lockName = "xfcyRedisLock";
return new RedisDistributedLock(stringRedisTemplate, lockName, uuidValue);
}else if (lockType.equalsIgnoreCase("ZOOKEEPER")) {
this.lockName = "xfcyZookeeperLock";
// TODO zoookeeper 版本的分布式锁
return null;
}else if (lockType.equalsIgnoreCase("MYSQL")){
this.lockName = "xfcyMysqlLock";
// TODO MYSQL 版本的分布式锁
return null;
}
return null;
}
}
mylock/RedisDistributedLock
package com.xfcy.mylock;
import cn.hutool.core.util.IdUtil;
import com.sun.org.apache.xpath.internal.operations.Bool;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import java.util.Arrays;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
/**
* @author 晓风残月Lx
* @date 2023/4/1 21:38
* 自研的redis分布式锁,实现 Lock 接口
*/
// @Component 引入DistributedLockFactory工厂模式,从工厂获得即可
public class RedisDistributedLock implements Lock {
@Autowired
private StringRedisTemplate stringRedisTemplate;
private String lockName; // KEYS[1]
private String uuidValue; // ARGV[1]
private long expireTime; // ARGV[2]
public RedisDistributedLock(StringRedisTemplate stringRedisTemplate, String lockName, String uuidValue) {
this.stringRedisTemplate = stringRedisTemplate;
this.lockName = lockName;
this.uuidValue = uuidValue + ":" + Thread.currentThread().getId();
this.expireTime = 30L;
}
@Override
public void lock() {
tryLock();
}
@Override
public boolean tryLock() {
try {
tryLock(-1L, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
return false;
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
if (time == -1L) {
String script = "if redis.call('exists',KEYS[1]) == 0 or redis.call('hexists',KEYS[1],ARGV[1]) == 1 then " +
"redis.call('hincrby',KEYS[1],ARGV[1],1) " +
"redis.call('expire',KEYS[1],ARGV[2]) " +
"return 1 " +
"else " +
"return 0 " +
"end";
System.out.println("lockName = " + lockName +"\t" + "uuidValue = " + uuidValue);
while (!stringRedisTemplate.execute(new DefaultRedisScript<>(script, Boolean.class), Arrays.asList(lockName), uuidValue, String.valueOf(expireTime))) {
// 暂停 60ms
Thread.sleep(60);
}
// 新建一个后台扫描程序,来监视key目前的ttl,是否到我们规定的 1/2 1/3 来实现续期
resetExpire();
return true;
}
return false;
}
@Override
public void unlock() {
String script = "if redis.call('HEXISTS',KEYS[1],ARGV[1]) == 0 then " +
"return nil " +
"elseif redis.call('HINCRBY',KEYS[1],ARGV[1],-1) == 0 then " +
"return redis.call('del',KEYS[1]) " +
"else " +
"return 0 " +
"end";
// nil = false 1 = true 0 = false
Long flag = stringRedisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Arrays.asList(lockName), uuidValue, String.valueOf(expireTime));
if (null == flag) {
throw new RuntimeException("this lock doesn't exists0");
}
}
private void resetExpire() {
String script = "if redis.call('HEXISTS',KEYS[1],ARGV[1]) == 1 then " +
"return redis.call('expire',KEYS[1],ARGV[2]) " +
"else " +
"return 0 " +
"end";
new Timer().schedule(new TimerTask() {
@Override
public void run() {
if (stringRedisTemplate.execute(new DefaultRedisScript<>(script, Boolean.class), Arrays.asList(lockName), uuidValue, String.valueOf(expireTime))) {
resetExpire();
}
}
}, (this.expireTime * 1000) / 3);
}
// 下面两个用不上
// 下面两个用不上
// 下面两个用不上
@Override
public void lockInterruptibly() throws InterruptedException {
}
@Override
public Condition newCondition() {
return null;
}
}
如果我们的分布式锁的服务器挂了,也就是单点故障,我们添加一个从机,但是复制是异步的。