目录
一、自研分布式锁核心
二、redlock红锁算法 Distributed locks with redis
2.1、设计理念
2.2、容错公式
2.3、单机案例实现
三、redisson源码分析
四、多机案例
4.1、启动三台docker的redis7
4.2、进入redis
一、自研分布式锁核心
1、按照JUC里面java.util.concurrent.locks.Lock接口规范编写
2、lock加锁:
①、加锁:在redis中给key设置一个值,为避免死锁,并给定过期时间
②、自旋:加锁失败,需要使用while进行重试并自旋
③、续期:自动续期,定期监控扫描
3、unlock解锁:将属于自己的key进行删除。
二、redlock红锁算法 Distributed locks with redis
2.1、设计理念

条件1:客户端从超过半数(大于等于N/2+1)的redis实例上成功获得了锁。
条件2:客户端获取锁的总耗时没有超过锁的有效时间。
2.2、容错公式
redis只支持AP,为了解决CP的风险,采用N个节点,N为奇数,三个master各自完全独立,不是主从或集群。
容错公式:
N = 2X + 1(N是最终部署机器数,X是容错机器数)
容错:
失败多少个机器实例后都可以容忍,即数据一致性还是OK的,CP数据一致性还是可以满足。
如:加入在集群环境中,redis失败1台,可以接受,2 * 1 + 1 = 3,部署3台,宕机1台剩2台可以正常工作,就部署3台。
为什是奇数:
最少的机器,最多的产生效果。
2.3、单机案例实现
1、导入pom依赖
<dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.13.4</version>
        </dependency> 
config配置类
 @Bean
    public Redisson redisson(){
        Config config = new Config();
        config.useSingleServer()
                .setAddress("redis://192.168.200.110:6379")
                .setDatabase(0)
                .setPassword("123456");
      return (Redisson) Redisson.create(config);
    } 
 @Autowired
    private Redisson redisson;
@Override
    public String saleRedisson() {
        String resultMessage = "";
        //加锁
        RLock redissonLock = redisson.getLock("redissonLock");
        redissonLock.lock();
        try {
            //查询redis里的库存
            String result = stringRedisTemplate.opsForValue().get(IN_KEY + 1);
            //判断库存是否足够
            Integer inventoryNumber = result == null ? 0 : Integer.parseInt(result);
            //卖出商品扣减库存
            if (inventoryNumber > 0){
                //存入redis
                stringRedisTemplate.opsForValue().set(IN_KEY + 1,String.valueOf(--inventoryNumber));
                resultMessage = "成功卖出商品,库存剩余:" + inventoryNumber;
                log.info(resultMessage + "\t服务端口号:{}",port);
            }else {
                resultMessage = "商品已售空";
            }
        } finally {
            //解锁,需要进行判断,只能删除属于当前线程的锁,不能删除别人的锁
            //必须加判断防止生成上出现BUG!!!
            if (redissonLock.isLocked() && redissonLock.isHeldByCurrentThread()){
                redissonLock.unlock();
            }
        }
        return resultMessage;
    } 
@ApiOperation("redisson扣减库存")
    @RequestMapping(value = "/saleRedisson",method = RequestMethod.GET)
    public String saleRedisson(){
        return inventoryService.saleRedisson();
    } 
三、redisson源码分析
redis分布式锁过期了,业务还在处理?续期
守护线程“续期”:
额外起一个线程,定期检查线程是否还持有锁,如果有则延长过期时间。
redisson里主要使用“看门狗”定期检查(每1/3的锁时间检查1次),如果线程还持有锁,则刷新过期时间。
在获取锁成功后,给锁加一个watchdong,watchdog会起一个定时任务,在锁没有被释放且快要过期的时侯会续期。
源码分析:
1、使用redisson新建的锁key,默认是30秒
2、
3、
①、通过exists判断,如果锁不存在,则设置值和过期时间,加锁成功。
②、通过hexists判断,如果锁已存在,并且锁的是当前线程,则证明是重入锁,加锁成功。
③、如果锁已存在,但锁的不是当前线程,则证明有其他线程持有锁。返回当前锁的过期时间(代表了锁key的剩余生存时间),加锁失败。
4、
初始化一个定时器,dely的时间是internalLockLeaseTime/3
在redisson中,internalLockLeaseTime是30s,每隔10s续期一次,每次30s
加锁成功就会启动一个watch dog看门狗,他是一个后台线程,会每隔10秒检查一下,如果客户端A还持有锁key,那么就会不断地延长锁key的生存时间,默认每次续命又从30秒新开始。
自动续期lua脚本
解锁:
四、多机案例

4.1、启动三台docker的redis7
docker run -p 6381:6379 --name redis_master1 -d redis:7.0.0
docker run -p 6382:6379 --name redis_master2 -d redis:7.0.0
docker run -p 6383:6379 --name redis_master3 -d redis:7.0.0

4.2、进入redis
docker exec -it redis_master1 redis-cli
4.3、案例实现
server.port=8090
spring.application.name=redissonTest
#swagger2
spring.swagger2.enabled=true
spring.mvc.pathmatch.matching-strategy=ant_path_matcher
#redis配置
spring.redis.database=0
spring.redis.password=
spring.redis.timeout=3000
spring.redis.mode=single
spring.redis.pool.conn-timeout=3000
spring.redis.pool.so-timeout=3000
spring.redis.pool.size=10
spring.redis.single.address1=192.168.200.110:6381
spring.redis.single.address2=192.168.200.110:6382
spring.redis.single.address3=192.168.200.110:6383 
@Data
public class RedisSingleProperties {
    private String address1;
    private String address2;
    private String address3;
} 
@Data
@ConfigurationProperties(prefix = "spring.redis",ignoreInvalidFields = false)
public class RedisProperties {
    private int database;
    //等待节点回复命令的时间,该时间从命令发送成功时开始计时
    private int timeout = 3000;
    private String password;
    private String mode;
    //池配置
    private RedisPoolProperties pool;
    //单机信息配置
    private RedisSingleProperties single;
} 
@Data
public class RedisPoolProperties {
    private int maxIdle;
    private int minIdle;
    private int maxActive;
    private int maxWait;
    private int connTimeout = 10000;
    private int soTimeout;
    //池大小
    private int size;
} 
@Configuration
@EnableConfigurationProperties(RedisProperties.class)
public class CacheConfiguration {
    @Autowired
    private RedisProperties redisProperties;
    @Bean
    public RedissonClient redissonClient1(){
        Config config = new Config();
        String node = redisProperties.getSingle().getAddress1();
        node = node.startsWith("redis://") ? node : "redis://" + node;
        SingleServerConfig singleServerConfig = config.useSingleServer()
                .setAddress(node)
                .setTimeout(redisProperties.getPool().getConnTimeout())
                .setConnectionPoolSize(redisProperties.getPool().getSize())
                .setConnectionMinimumIdleSize(redisProperties.getPool().getMinIdle());
        if (StringUtils.isNotBlank(redisProperties.getPassword())){
            singleServerConfig.setPassword(redisProperties.getPassword());
        }
        return Redisson.create(config);
    }
    @Bean
    public RedissonClient redissonClient2(){
        Config config = new Config();
        String node = redisProperties.getSingle().getAddress2();
        node = node.startsWith("redis://") ? node : "redis://" + node;
        SingleServerConfig singleServerConfig = config.useSingleServer()
                .setAddress(node)
                .setTimeout(redisProperties.getPool().getConnTimeout())
                .setConnectionPoolSize(redisProperties.getPool().getSize())
                .setConnectionMinimumIdleSize(redisProperties.getPool().getMinIdle());
        if (StringUtils.isNotBlank(redisProperties.getPassword())){
            singleServerConfig.setPassword(redisProperties.getPassword());
        }
        return Redisson.create(config);
    }
    @Bean
    public RedissonClient redissonClient3(){
        Config config = new Config();
        String node = redisProperties.getSingle().getAddress3();
        node = node.startsWith("redis://") ? node : "redis://" + node;
        SingleServerConfig singleServerConfig = config.useSingleServer()
                .setAddress(node)
                .setTimeout(redisProperties.getPool().getConnTimeout())
                .setConnectionPoolSize(redisProperties.getPool().getSize())
                .setConnectionMinimumIdleSize(redisProperties.getPool().getMinIdle());
        if (StringUtils.isNotBlank(redisProperties.getPassword())){
            singleServerConfig.setPassword(redisProperties.getPassword());
        }
        return Redisson.create(config);
    }
}
 
@RestController
@Slf4j
public class RedLockController {
    public static final String CACHE_KEY_REDLOCK = "CJC_REDLOCK";
    @Autowired
    private RedissonClient redissonClient1;
    @Autowired
    private RedissonClient redissonClient2;
    @Autowired
    private RedissonClient redissonClient3;
    @GetMapping("/getMultiLock")
    public String  getMultiLock(){
        String threadName = Thread.currentThread().getId()+"";
        RLock lock1 = redissonClient1.getLock(CACHE_KEY_REDLOCK);
        RLock lock2 = redissonClient2.getLock(CACHE_KEY_REDLOCK);
        RLock lock3 = redissonClient3.getLock(CACHE_KEY_REDLOCK);
        RedissonMultiLock redissonMultiLock = new RedissonMultiLock(lock1, lock2, lock3);
        redissonMultiLock.lock();
        try {
            log.info("com in getMultiLock",threadName);
            try {
                TimeUnit.SECONDS.sleep(30);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            log.info("com over getMultiLock",threadName);
        } catch (Exception e){
            e.printStackTrace();
            log.info("getMultiLock出现异常:{}",e.getCause() + "\t"+ e.getMessage());
        } finally{
            redissonMultiLock.unlock();
            log.info("释放分布式锁成功:{}",CACHE_KEY_REDLOCK);
        }
        return "multilock task is over " + threadName;
    }
}
 
                
























![[数据结构 -- C语言] 栈(Stack)](https://img-blog.csdnimg.cn/img_convert/269e8d78fcd347b39d32a09c206d46a5.png)

