Springboot + Redis 分布式锁

一、版本与环境搭建:

服务器百度云(Centos7)、Redis(6.2.6)、Jdk(17.0.1)

详情如图(Redis单机部署):
在这里插入图片描述在这里插入图片描述

Centos7.9
Redis2.5.7
Redisson3.16.6
Jdk17.0.1
Springboot2.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 hongxu_1234@163.com
 * @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 hongxu_1234@163.com
 * @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 hongxu_1234@163.com
 * @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 hongxu_1234@163.com
 * @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 hongxu_1234@163.com
 * @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 hongxu_1234@163.com
 * @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文件

<?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 http://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.5.7</version>
        <relativePath/>
    </parent>
    <groupId>com.demo</groupId>
    <artifactId>redis</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson-spring-boot-starter</artifactId>
            <version>3.16.6</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.4.2</version>
        </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节点;此时锁丢失。。。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猪悟道

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值