缓存-分布式锁框架-Redisson

1.Redisson简介

Distributed Locks with Redis | Docs

Redisson 是 Redis Java 客户端和实时数据平台。它提供了更方便、最简单的 Redis 工作方式。Redisson 对象提供了关注点分离,允许您专注于数据建模和应用程序逻辑。

1. Overview · redisson/redisson Wiki · GitHub

2.使用Redisson

现在虽然有 springboot 启动场景

但是为了学习,我们还是手动去注入所需要地对象,才知道里面,springboot到底帮我们装了哪些对象

1.导入依赖

  <!-- 以后使用redisson作为所有分布式锁,分布式对象等功能框架 -->
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.12.0</version>
        </dependency>

2.配置Redisson

根据官方文档 需要放入这个对象

package com.jmj.gulimall.product.config;

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.IOException;

@Configuration
public class MyRedissonConfig {


    /**
     * 所有对Redisson地使用都是通过 RedissonClient对象
     * @return
     * @throws IOException
     */
    @Bean(destroyMethod="shutdown")
    public RedissonClient redisson() throws IOException {
        Config config = new Config();
        //用rediss://来启用ssl连接
        config.useSingleServer().setAddress("redis://192.168.232.209:6379");
        RedissonClient redissonClient = Redisson.create(config);
        return redissonClient;
    }








}

3.使用锁做个测试,最普通的分布式锁

 @Autowired
    private RedissonClient redisson;

    private final String jmjLock = "jmjLock";
    private int i = 0;

    @GetMapping("/hello")
    @ResponseBody
    public String test() {
        //1、获取一把锁,只要锁的名字一样,就是同一把锁
        RLock lock = redisson.getLock(jmjLock);
        try {
            //2、加锁
            lock.lock();

            int i1 = i + 1;

            i=i1;

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //解锁
            lock.unlock();
        }


        return "good";
    }

    @GetMapping("/result")
    @ResponseBody
    public String test1() {
        return i+"";
    }

 原理就是,同一个Key用setnx创建一个值,值设置一个随机的数不重复,当前线程存入,然后业务执行完毕,释放锁的时候,就判断是否是同一把锁,是同一把锁就解锁,创建锁和释放锁都是原子性操作。

1.redission解决了的问题 使用看门狗

阻塞式等待,默认加的锁都是30s,如果时间不够会在业务时间会被续期

1.解决了锁的自动续期,如果业务超长,运行期间自动给所续上新的30s,不用担心业务时间长,锁自动过期被删掉

2.如果在业务期间,程序断电了,没有执行释放锁的操作,看门狗也就不自动续期了,锁超时过后,也会自动释放

while true 阻塞式 无限循环尝试获取锁

 4.看门狗lock原理  

     lock.lock(10, TimeUnit.SECONDS);
            // 1、10秒钟自动解锁 自动解锁时间一定要大于业务的执行时间。
            // 2、问题 在锁时间到了以后,不会自动续期

      // 1、10秒钟自动解锁 自动解锁时间一定要大于业务的执行时间。
            // 2、问题 在锁时间到了以后,不会自动续期
            // 3、 如果我们传递了锁的超时时间,就发送给redis执行脚本,进行占锁,默认超时就是我们指定的时间
            // 4、 如果我们未指定了超时时间 , 就使用 30*1000   private long lockWatchdogTimeout = 30 * 1000;
            // 只要占锁成功,就会启动一个定时任务[ 重新给锁设置过期时间,新的过期时间就是看门狗的默认时间],每个一个3分之1看门狗时间自动续期
            // internalLockLeaseTime[看门狗时间] / 3 ,10s

 最佳实战

// lock.lock(30, TimeUnit.SECONDS);一般用这个,手动解锁,不自动续期

因为不可能让一个业务执行30秒之久的,要不然业务就崩溃了。

 5.读写锁测试

@Autowired
    RedisTemplate redisTemplate;
    @Autowired
    private RedissonClient redisson;
    @ResponseBody
    @GetMapping("/write")
    public String writeValue() {
        RReadWriteLock readWriteLock = redisson.getReadWriteLock("rw-lock");
        RLock writeLock = readWriteLock.writeLock();
        String string = "";
        try {
            writeLock.lock(35,TimeUnit.SECONDS);
            System.out.println("正在写数据...");
            string = UUID.randomUUID().toString();
            TimeUnit.SECONDS.sleep(30);
            redisTemplate.opsForValue().set("writeValue", string);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            System.out.println("释放写锁");
            writeLock.unlock();
        }

        return string;
    }
    //保证一定能读到最新数据,修改期间,写锁是一个排他锁(互斥锁)  读锁是一个共享锁
    //写锁没释放,读就会等待,读没释放,写就会等待
    @ResponseBody
    @GetMapping("/read")
    public String readValue() {
        RReadWriteLock readWriteLock = redisson.getReadWriteLock("rw-lock");
        RLock readLock = readWriteLock.readLock();
        String string = "";
        try {
            readLock.lock(30,TimeUnit.SECONDS);
            System.out.println("正在读取数据...");

            string = (String) redisTemplate.opsForValue().get("writeValue");
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            System.out.println("释放读锁");
            readLock.unlock();
        }

        return string;
    }

6.信号量

模拟车库停车

private static final String semaphoreLock = "park";
    @GetMapping("/park")
    @ResponseBody
    public String park() throws InterruptedException {

        //信号量 就是一个 队列的 锁
        RSemaphore park = redisson.getSemaphore(semaphoreLock);

//        park.acquire();//获取一个信号,获取一个值  占用一个车位 阻塞式


        boolean b = park.tryAcquire(3, TimeUnit.SECONDS);//尝试十秒
        boolean c = park.tryAcquire();//可以立马停
        if (b){
            Thread.sleep(10000);
            return "acquire ok";
        }else {
            return "acquire fail";
        }


    }

    @GetMapping("/go")
    @ResponseBody
    public String go() throws InterruptedException {

        //信号量 就是一个 队列的 锁
        RSemaphore park = redisson.getSemaphore(semaphoreLock);

        park.release();//释放一个信号, 释放一个车位 *********** 阻塞式


        return "release ok";
    }

 

 虽然分布式锁满,但是一致性强, 符合 AP 原则

信号量 业务场景 :

车库停车

信号量限流 分布式限流

7.闭锁(CountDownLatch)

基于Redisson的Redisson分布式闭锁(CountDownLatch) Java 对象 RCountDownLatch  采用了与 JUC CountDownLatch相似的接口和用法

闭锁,就是等待,例如十个线程全部做完了,倒计数为0 才算结束

    /**
     * 放假锁门
     * 5个班全部走完,我们可以锁大门
     */
    @GetMapping("/lockDoor")
    @ResponseBody
    public String lockDoor() throws InterruptedException {
        RCountDownLatch door = redisson.getCountDownLatch("door");
        door.trySetCount(5);
        door.await();//等待闭锁都完成
        return "放假了,锁门成功";
    }

    @GetMapping("/gogogo/{id}")
    @ResponseBody
    public String gogogo(@PathVariable("id") Long id) {
        RCountDownLatch door = redisson.getCountDownLatch("door");
        door.countDown();//计数减一
        return id+"班的人2都走了...";
    }

8.分布式锁的 缓存一致性解决

1.锁的名字 锁的粒度越细  越快

2. 约定: 具体缓存的是某个数据 11-号商品 :  product-11-lock

缓存一致性

1.双写模式 

2.失效模式

并发问题

都可以加锁解决

经常修改,不用缓存 ,直接数据库

使用Canal解决缓存一致性

 我们系统的一致性解决方案:

1.缓存的所有数据都有过期时间,数据过期下一次查询触发主动更新

2.读写数据的时候,加上分布式的读写锁 适用于读多写少

  • 6
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值