服务器百度云(Centos7)、Redis(6.2.6)、Jdk(17.0.1)
Centos | 7.9 |
---|---|
Redis | 2.5.7 |
Redisson | 3.16.6 |
Jdk | 17.0.1 |
Springboot | 2.5.7 |
写在前面:Redis是单线程,I/O多路复用
1.单机模式下简单设置:
问题:如果最后多个请求同时到来,同时获取库存;则均进入扣减环节,最后库存为0 订单超额
解决:加入synchronized锁,包裹扣减代码,变为单线程执行
package com.demo.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* Description: 减库存
*
* @Author: zhx & moon [email protected]
* @Date: 2021-12-19 14:09
* @version: V1.0.0
*/
@RestController
@RequestMapping("/reduce")
@Slf4j
public class IndexLearn {
@Resource
StringRedisTemplate stringRedisTemplate;
/**
* 获取库存
* 库存为 1 如果3个请求同时获取,则在减库存时数据异常
* 加入synchronized锁 则牺牲性能解决 单机 情况问题
*/
@GetMapping("deductStock")
public String deductStock(){
//获取剩余库存
int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
//如果有库存 则扣减
if (stock>0){
int realStock = stock - 1;
stringRedisTemplate.opsForValue().set("stock",realStock + "");
log.info("剩余库存:{}",realStock);
return "success";
}else{
log.error("库存不足");
return "error";
}
}
}
2.双机部署,两台服务,通过Nginx代理进行均衡配置
问题:此时,synchronized锁无法保证两台机器之间的同步,可能两个机器同时获取同一个库存,则扣减后,库存值
减一,但是实际生成了两条订单,出现超卖情况
解决:在扣减前,通过Redis加锁,谁扣减库存谁加锁,扣减完成在释放
package com.demo.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* Description: 减库存
*
* @Author: zhx & moon [email protected]
* @Date: 2021-12-19 14:09
* @version: V1.0.0
*/
@RestController
@RequestMapping("/reduce")
@Slf4j
public class IndexLearn {
@Resource
StringRedisTemplate stringRedisTemplate;
/**
* 获取库存
* 1.获取库存前,通过Redis模拟锁操作,如key:product_0 value:pod_1
* 则节点1在对产品0加锁,申请库存,进行扣减
* 2.setIfAbsent 键值不存在时成功,存在时失败
*/
@GetMapping("deductStock")
public String deductStock(){
String locKey = "product_0";
String clientId = "pod_1";
try {
boolean result = stringRedisTemplate.opsForValue().setIfAbsent(locKey,clientId);
//判断是否存在锁
if (!result){
return "error";
}
//获取库存
int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
//扣减
if (stock>0){
int realStock = stock - 1;
stringRedisTemplate.opsForValue().set("stock",realStock + "");
log.info("剩余库存:{}",realStock);
return "success";
}else{
log.error("库存不足");
return "error";
}
}finally {
//删除锁
stringRedisTemplate.delete(locKey);
}
}
}
三、问题来了:
1.如果节点1加锁后,宕机,则锁无法释放,此时系统将死锁,无法进行秒杀:为锁设置超时时间
stringRedisTemplate.opsForValue().setIfAbsent(locKey,clientId,10, TimeUnit.SECONDS);
2.设置超时时间后,节点1服务器异常卡顿,锁超时,自动释放;节点2服务器获取锁,进行扣减,此时节点1执行完成,将锁释放 -----:加判断,只有自己可以释放自己的锁,引入雪花算法用于生成ID
if (clientId.equals(stringRedisTemplate.opsForValue().get(locKey))){
stringRedisTemplate.delete(locKey);
}
3.代码本身正常耗时超过超时时间,锁释放,引发异常:加锁后,起一个子线程去监督执行情况,每1/3超时周期检测一次,未执行完成则重置锁时间
使用Redisson解决上述问题(支持续期和互斥,可重入):
Redisson续期机制:加锁成功,就会启动一个看门狗(源码没注释,大概看下吧):
private <T> RFuture<Long> tryAcquireAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId) {
RFuture ttlRemainingFuture;
if (leaseTime != -1L) {
ttlRemainingFuture = this.tryLockInnerAsync(waitTime, leaseTime, unit, threadId, RedisCommands.EVAL_LONG);
} else {
ttlRemainingFuture = this.tryLockInnerAsync(waitTime, this.internalLockLeaseTime, TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);
}
ttlRemainingFuture.onComplete((ttlRemaining, e) -> {
if (e == null) {
if (ttlRemaining == null) {
if (leaseTime != -1L) {
this.internalLockLeaseTime = unit.toMillis(leaseTime);
} else {
this.scheduleExpirationRenewal(threadId);
}
}
}
});
return ttlRemainingFuture;
}
protected void scheduleExpirationRenewal(long threadId) {
ExpirationEntry entry = new ExpirationEntry();
ExpirationEntry oldEntry = EXPIRATION_RENEWAL_MAP.putIfAbsent(getEntryName(), entry);
if (oldEntry != null) {
oldEntry.addThreadId(threadId);
} else {
entry.addThreadId(threadId);
try {
renewExpiration();
} finally {
if (Thread.currentThread().isInterrupted()) {
cancelExpirationRenewal(threadId);
}
}
}
}
private void renewExpiration() {
ExpirationEntry ee = EXPIRATION_RENEWAL_MAP.get(getEntryName());
if (ee == null) {
return;
}
Timeout task = commandExecutor.getConnectionManager().newTimeout(new TimerTask() {
@Override
public void run(Timeout timeout) throws Exception {
ExpirationEntry ent = EXPIRATION_RENEWAL_MAP.get(getEntryName());
if (ent == null) {
return;
}
Long threadId = ent.getFirstThreadId();
if (threadId == null) {
return;
}
RFuture<Boolean> future = renewExpirationAsync(threadId);
future.onComplete((res, e) -> {
if (e != null) {
log.error("Can't update lock " + getRawName() + " expiration", e);
EXPIRATION_RENEWAL_MAP.remove(getEntryName());
return;
}
if (res) {
// reschedule itself
renewExpiration();
} else {
cancelExpirationRenewal(null);
}
});
}
}, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);
ee.setTimeout(task);
}
扣减库存:
package com.demo.controller;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
/**
* Description: 减库存
*
* @Author: zhx & moon [email protected]
* @Date: 2021-12-19 14:09
* @version: V1.0.0
*/
@RestController
@RequestMapping("/reduce")
@Slf4j
public class IndexLearn {
@Resource
StringRedisTemplate stringRedisTemplate;
@Resource
Redisson redisson;
@GetMapping("deductStock")
public String deductStock(){
String locKey = "product_0";
RLock rLock = redisson.getLock(locKey);
try {
/**
* 阻塞三十秒 每10秒检测线程状态
* 如果线程未完成,则自动续期
*/
rLock.lock();
int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
if (stock>0){
int realStock = stock - 1;
stringRedisTemplate.opsForValue().set("stock",realStock + "");
log.info("剩余库存:{}",realStock);
return "success";
}else{
log.error("库存不足");
return "error";
}
}finally {
rLock.unlock();
}
}
}
Redis数据和Redisson初始化
package com.demo.config;
import org.redisson.Redisson;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.DependsOn;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
/**
* Description: 测试数据初始化
*
* @Author: zhx & moon [email protected]
* @Date: 2021-12-19 14:15
* @version: V1.0.0
*/
@Component
public class InitRedis {
@Resource
private StringRedisTemplate stringRedisTemplate;
@PostConstruct
public void init(){
stringRedisTemplate.opsForValue().set("stock","1");
}
}
package com.demo.config;
import org.redisson.Redisson;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
/**
* Description: 初始化Redisson
*
* @Author: zhx & moon [email protected]
* @Date: 2021-12-20 17:26
* @version: V1.0.0
*/
@Component
public class InitRedisson {
@Value("${spring.redis.host}")
private String redisIp;
@Value("${spring.redis.port}")
private int redisPort;
@Value("${spring.redis.password}")
private String password;
private int dataBase = 1;
@Bean
public Redisson redisson(){
/**
* 单机模式启动
*/
String redisUrl = "redis://" + redisIp + ":" + redisPort;
Config config = new Config();
config.useSingleServer().setAddress(redisUrl).setDatabase(dataBase).setPassword(password);
return (Redisson) Redisson.create(config);
}
}
四、利用Hutool实现雪花算法工具类
String clientId = SnowFlakeUtil.getIdStr();
package com.demo.util;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.net.NetUtil;
import cn.hutool.core.util.IdUtil;
import org.springframework.stereotype.Component;
/**
* Description: 雪花算法
*
* 64bit大小的整数
* 1bit,保留,因为二进制中最高位是符号位,1表示负数,0表示正数 默认0
* 41bit-毫秒级时间戳
* 10bit-服务器设备id 5位 datacenterId 和5位 workerId
* 12bit-序列号,同毫秒内产生的不同Id
*
* @Author: zhx & moon [email protected]
* @Date: 2021-12-19 12:45
* @version: V1.0.0
*/
@Component
public class SnowFlakeUtil {
private long workerId = 0;
private long datacenterId = 0;
private Snowflake snowflake = IdUtil.createSnowflake(workerId,datacenterId);
public SnowFlakeUtil(){
workerId = NetUtil.ipv4ToLong(NetUtil.getLocalhostStr());
}
public synchronized long snowflakeId(){
return snowflake.nextId();
}
public synchronized String snowflakeIdStr(){
return snowflake.nextIdStr();
}
/**
* 成员类,SnowFlakeUtil的实例对象的保存域
*/
private static class IdGenHolder {
private static final SnowFlakeUtil instance = new SnowFlakeUtil();
}
/**
* 可直接调用
*/
public static SnowFlakeUtil get() {
return IdGenHolder.instance;
}
public static Long getId() {
return SnowFlakeUtil.get().snowflakeId();
}
public static String getIdStr() {
return SnowFlakeUtil.get().snowflakeIdStr();
}
}
Pom文件
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.5.7version>
<relativePath/>
parent>
<groupId>com.demogroupId>
<artifactId>redisartifactId>
<version>1.0-SNAPSHOTversion>
<properties>
<maven.compiler.source>17maven.compiler.source>
<maven.compiler.target>17maven.compiler.target>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
<dependency>
<groupId>org.redissongroupId>
<artifactId>redisson-spring-boot-starterartifactId>
<version>3.16.6version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
<dependency>
<groupId>cn.hutoolgroupId>
<artifactId>hutool-allartifactId>
<version>5.4.2version>
dependency>
dependencies>
project>
配置文件
server:
port: 8899
spring:
application: hongxu_moon
redis:
# Redis数据库索引(默认为0)
database: 0
# Redis服务器地址
host: 106.12.148.211
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码(默认为空)
password: 123456
timeout: 100
pool:
# 连接池最大连接数(使用负值表示没有限制)
max-active: 8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1
# 连接池中的最大空闲连接
max-idle: 8
# 连接池中的最小空闲连接
min-idle: 0
# 连接超时时间(毫秒)
timeout: 100
五、结果
调用:http://127.0.0.1:8899/reduce/deductRedisson
返回:
"C:\Program Files\Java\jdk-17.0.1\bin\java.exe" -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:50309,suspend=y,server=n -XX:TieredStopAtLevel=1 -noverify -Dspring.output.ansi.enabled=always -javaagent:C:\Users\Administrator\AppData\Local\JetBrains\IntelliJIdea2021.2\captureAgent\debugger-agent.jar -Dcom.sun.management.jmxremote -Dspring.jmx.enabled=true -Dspring.liveBeansView.mbeanDomain -Dspring.application.admin.enabled=true -Dfile.encoding=UTF-8 -classpath "E:\IdeaProjects\redis\target\classes;D:\apache-maven-space\org\springframework\boot\spring-boot-starter-web\2.5.7\spring-boot-starter-web-2.5.7.jar;D:\apache-maven-space\org\springframework\boot\spring-boot-starter\2.5.7\spring-boot-starter-2.5.7.jar;D:\apache-maven-space\org\springframework\boot\spring-boot\2.5.7\spring-boot-2.5.7.jar;D:\apache-maven-space\org\springframework\boot\spring-boot-autoconfigure\2.5.7\spring-boot-autoconfigure-2.5.7.jar;D:\apache-maven-space\org\springframework\boot\spring-boot-starter-logging\2.5.7\spring-boot-starter-logging-2.5.7.jar;D:\apache-maven-space\ch\qos\logback\logback-classic\1.2.7\logback-classic-1.2.7.jar;D:\apache-maven-space\ch\qos\logback\logback-core\1.2.7\logback-core-1.2.7.jar;D:\apache-maven-space\org\apache\logging\log4j\log4j-to-slf4j\2.14.1\log4j-to-slf4j-2.14.1.jar;D:\apache-maven-space\org\apache\logging\log4j\log4j-api\2.14.1\log4j-api-2.14.1.jar;D:\apache-maven-space\org\slf4j\jul-to-slf4j\1.7.32\jul-to-slf4j-1.7.32.jar;D:\apache-maven-space\jakarta\annotation\jakarta.annotation-api\1.3.5\jakarta.annotation-api-1.3.5.jar;D:\apache-maven-space\org\springframework\spring-core\5.3.13\spring-core-5.3.13.jar;D:\apache-maven-space\org\springframework\spring-jcl\5.3.13\spring-jcl-5.3.13.jar;D:\apache-maven-space\org\yaml\snakeyaml\1.28\snakeyaml-1.28.jar;D:\apache-maven-space\org\springframework\boot\spring-boot-starter-json\2.5.7\spring-boot-starter-json-2.5.7.jar;D:\apache-maven-space\com\fasterxml\jackson\core\jackson-databind\2.12.5\jackson-databind-2.12.5.jar;D:\apache-maven-space\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.12.5\jackson-datatype-jdk8-2.12.5.jar;D:\apache-maven-space\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.12.5\jackson-datatype-jsr310-2.12.5.jar;D:\apache-maven-space\com\fasterxml\jackson\module\jackson-module-parameter-names\2.12.5\jackson-module-parameter-names-2.12.5.jar;D:\apache-maven-space\org\springframework\boot\spring-boot-starter-tomcat\2.5.7\spring-boot-starter-tomcat-2.5.7.jar;D:\apache-maven-space\org\apache\tomcat\embed\tomcat-embed-core\9.0.55\tomcat-embed-core-9.0.55.jar;D:\apache-maven-space\org\apache\tomcat\embed\tomcat-embed-el\9.0.55\tomcat-embed-el-9.0.55.jar;D:\apache-maven-space\org\apache\tomcat\embed\tomcat-embed-websocket\9.0.55\tomcat-embed-websocket-9.0.55.jar;D:\apache-maven-space\org\springframework\spring-web\5.3.13\spring-web-5.3.13.jar;D:\apache-maven-space\org\springframework\spring-beans\5.3.13\spring-beans-5.3.13.jar;D:\apache-maven-space\org\springframework\spring-webmvc\5.3.13\spring-webmvc-5.3.13.jar;D:\apache-maven-space\org\springframework\spring-aop\5.3.13\spring-aop-5.3.13.jar;D:\apache-maven-space\org\springframework\spring-context\5.3.13\spring-context-5.3.13.jar;D:\apache-maven-space\org\springframework\spring-expression\5.3.13\spring-expression-5.3.13.jar;D:\apache-maven-space\org\springframework\boot\spring-boot-starter-data-redis\2.5.7\spring-boot-starter-data-redis-2.5.7.jar;D:\apache-maven-space\org\springframework\data\spring-data-redis\2.5.7\spring-data-redis-2.5.7.jar;D:\apache-maven-space\org\springframework\data\spring-data-keyvalue\2.5.7\spring-data-keyvalue-2.5.7.jar;D:\apache-maven-space\org\springframework\data\spring-data-commons\2.5.7\spring-data-commons-2.5.7.jar;D:\apache-maven-space\org\springframework\spring-tx\5.3.13\spring-tx-5.3.13.jar;D:\apache-maven-space\org\springframework\spring-oxm\5.3.13\spring-oxm-5.3.13.jar;D:\apache-maven-space\org\springframework\spring-context-support\5.3.13\spring-context-support-5.3.13.jar;D:\apache-maven-space\org\slf4j\slf4j-api\1.7.32\slf4j-api-1.7.32.jar;D:\apache-maven-space\io\lettuce\lettuce-core\6.1.5.RELEASE\lettuce-core-6.1.5.RELEASE.jar;D:\apache-maven-space\io\netty\netty-common\4.1.70.Final\netty-common-4.1.70.Final.jar;D:\apache-maven-space\io\netty\netty-handler\4.1.70.Final\netty-handler-4.1.70.Final.jar;D:\apache-maven-space\io\netty\netty-transport\4.1.70.Final\netty-transport-4.1.70.Final.jar;D:\apache-maven-space\io\projectreactor\reactor-core\3.4.12\reactor-core-3.4.12.jar;D:\apache-maven-space\org\redisson\redisson-spring-boot-starter\3.16.6\redisson-spring-boot-starter-3.16.6.jar;D:\apache-maven-space\org\springframework\boot\spring-boot-starter-actuator\2.5.7\spring-boot-starter-actuator-2.5.7.jar;D:\apache-maven-space\org\springframework\boot\spring-boot-actuator-autoconfigure\2.5.7\spring-boot-actuator-autoconfigure-2.5.7.jar;D:\apache-maven-space\org\springframework\boot\spring-boot-actuator\2.5.7\spring-boot-actuator-2.5.7.jar;D:\apache-maven-space\io\micrometer\micrometer-core\1.7.6\micrometer-core-1.7.6.jar;D:\apache-maven-space\org\hdrhistogram\HdrHistogram\2.1.12\HdrHistogram-2.1.12.jar;D:\apache-maven-space\org\latencyutils\LatencyUtils\2.0.3\LatencyUtils-2.0.3.jar;D:\apache-maven-space\org\redisson\redisson\3.16.6\redisson-3.16.6.jar;D:\apache-maven-space\io\netty\netty-codec\4.1.70.Final\netty-codec-4.1.70.Final.jar;D:\apache-maven-space\io\netty\netty-buffer\4.1.70.Final\netty-buffer-4.1.70.Final.jar;D:\apache-maven-space\io\netty\netty-resolver\4.1.70.Final\netty-resolver-4.1.70.Final.jar;D:\apache-maven-space\io\netty\netty-resolver-dns\4.1.70.Final\netty-resolver-dns-4.1.70.Final.jar;D:\apache-maven-space\io\netty\netty-codec-dns\4.1.70.Final\netty-codec-dns-4.1.70.Final.jar;D:\apache-maven-space\javax\cache\cache-api\1.1.1\cache-api-1.1.1.jar;D:\apache-maven-space\org\reactivestreams\reactive-streams\1.0.3\reactive-streams-1.0.3.jar;D:\apache-maven-space\io\reactivex\rxjava3\rxjava\3.0.12\rxjava-3.0.12.jar;D:\apache-maven-space\org\jboss\marshalling\jboss-marshalling\2.0.11.Final\jboss-marshalling-2.0.11.Final.jar;D:\apache-maven-space\org\jboss\marshalling\jboss-marshalling-river\2.0.11.Final\jboss-marshalling-river-2.0.11.Final.jar;D:\apache-maven-space\com\fasterxml\jackson\core\jackson-annotations\2.12.5\jackson-annotations-2.12.5.jar;D:\apache-maven-space\com\fasterxml\jackson\dataformat\jackson-dataformat-yaml\2.12.5\jackson-dataformat-yaml-2.12.5.jar;D:\apache-maven-space\com\fasterxml\jackson\core\jackson-core\2.12.5\jackson-core-2.12.5.jar;D:\apache-maven-space\net\bytebuddy\byte-buddy\1.10.22\byte-buddy-1.10.22.jar;D:\apache-maven-space\org\jodd\jodd-bean\5.1.6\jodd-bean-5.1.6.jar;D:\apache-maven-space\org\jodd\jodd-core\5.1.6\jodd-core-5.1.6.jar;D:\apache-maven-space\org\redisson\redisson-spring-data-25\3.16.6\redisson-spring-data-25-3.16.6.jar;D:\apache-maven-space\org\projectlombok\lombok\1.18.22\lombok-1.18.22.jar;D:\apache-maven-space\cn\hutool\hutool-all\5.4.2\hutool-all-5.4.2.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2020.2.3\lib\idea_rt.jar" com.demo.RedisApplication
Java HotSpot(TM) 64-Bit Server VM warning: Options -Xverify:none and -noverify were deprecated in JDK 13 and will likely be removed in a future release.
Connected to the target VM, address: '127.0.0.1:50309', transport: 'socket'
Java HotSpot(TM) 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.5.7)
2021-12-20 17:27:47.371 INFO 20436 --- [ main] com.demo.RedisApplication : Starting RedisApplication using Java 17.0.1 on zhx_yue with PID 20436 (E:\IdeaProjects\redis\target\classes started by Administrator in E:\IdeaProjects\redis)
2021-12-20 17:27:47.372 INFO 20436 --- [ main] com.demo.RedisApplication : No active profile set, falling back to default profiles: default
2021-12-20 17:27:47.780 INFO 20436 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode!
2021-12-20 17:27:47.781 INFO 20436 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data Redis repositories in DEFAULT mode.
2021-12-20 17:27:47.792 INFO 20436 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 3 ms. Found 0 Redis repository interfaces.
2021-12-20 17:27:48.007 INFO 20436 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8899 (http)
2021-12-20 17:27:48.012 INFO 20436 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2021-12-20 17:27:48.012 INFO 20436 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.55]
2021-12-20 17:27:48.053 INFO 20436 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2021-12-20 17:27:48.054 INFO 20436 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 660 ms
2021-12-20 17:27:51.063 INFO 20436 --- [ main] org.redisson.Version : Redisson 3.16.6
2021-12-20 17:27:51.248 INFO 20436 --- [sson-netty-2-32] o.r.c.pool.PubSubConnectionPool : 1 connections initialized for 106.12.148.211/106.12.148.211:6379
2021-12-20 17:27:51.246 INFO 20436 --- [sson-netty-2-31] o.r.c.pool.MasterPubSubConnectionPool : 1 connections initialized for 106.12.148.211/106.12.148.211:6379
2021-12-20 17:27:51.278 INFO 20436 --- [isson-netty-2-3] o.r.c.pool.MasterConnectionPool : 24 connections initialized for 106.12.148.211/106.12.148.211:6379
2021-12-20 17:27:51.279 INFO 20436 --- [isson-netty-2-5] o.r.connection.pool.SlaveConnectionPool : 24 connections initialized for 106.12.148.211/106.12.148.211:6379
2021-12-20 17:27:52.260 INFO 20436 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 1 endpoint(s) beneath base path '/actuator'
2021-12-20 17:27:52.286 INFO 20436 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8899 (http) with context path ''
2021-12-20 17:27:52.295 INFO 20436 --- [ main] com.demo.RedisApplication : Started RedisApplication in 5.124 seconds (JVM running for 5.551)
2021-12-20 17:27:52.599 INFO 20436 --- [6)-192.168.18.4] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2021-12-20 17:27:52.599 INFO 20436 --- [6)-192.168.18.4] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2021-12-20 17:27:52.600 INFO 20436 --- [6)-192.168.18.4] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 ms
2021-12-20 17:29:11.385 INFO 20436 --- [nio-8899-exec-1] com.demo.controller.IndexController : 剩余库存:0
六、遗留问题
当Redis为集群部署时,Redisson在Master主节点设置锁后,如果在Master节点同步锁到分机之前,Master宕机,则
分机会通过选举产生新的Master节点;此时锁丢失。。。