基于Redisson分布式系统实战案列

序言

  1. 什么是分布式锁?
    分布式锁的由来,是紧随着分布式架构的出现而产生的,在之前的单体架构中 , 面对线程安全的问题可能使用 JUC 提供的锁即可,但是随着业务不断发展,这时单机满足不了,于是采用分布式集群部署的方式,虽然一定程度解决了性能的瓶颈 , 但是也带来了许多分布式相关的问题。
  2. 为什么需要分布式锁(解决了什么问题)?
    避免分布式系统高并发请求时,造成线程不安全等问题。
  3. 怎么去实现分布式锁
    1)基于 Redis 实现分布式锁.
    2)基于 Zookeeper 实现.
    3)基于数据库 实现

一 Redisson简介和SpringBoot的整合

1、什么是Redisson

Redisson是Redis官方推荐的Java版的Redis客户端, Redisson API 侧重于分布式开发,并提供了大量的锁。

2、SpringBoot的整合

1)pom.xml 文件中引入依赖包,基于redis包的整合
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.12.5</version>
</dependency>
2)设置配置bean
@Configuration
public class MyRedisConfig {
	//从配置当中拿取
    @Value("${ipAddr}")
    private String ipAddr;

    @Bean(destroyMethod = "shutdown")
    public RedissonClient redisson() {
        Config config = new Config();
        // 创建单例模式的配置
        config.useSingleServer().setAddress("redis://" + ipAddr + ":6379").setPassword("");
        return Redisson.create(config);
    }
}

二 Redisson 分布式锁-lock锁和看门狗 测试

// 注入实例 RedissonClient 
@Resource
private RedissonClient redissonClient;

@RequestMapping("test/hello")
    public String hello() {
        //相当于在redis中,将my-lock 插入redis成功后 可执行加锁
        // 可查看redis中是否含有 key=my-lock  注意分布式锁名称要做好约定,颗粒度尽量要细
        RLock lock = redissonClient.getLock("my-lock");
        /**
         * lock 默认采用看门狗 加锁时长 30s
         * 1:锁的自动续期,如果执行业务代码十分耗时,在运行期间会自动给锁续上新的 30s ,避免造成因业务代码执行耗时而误删锁
         * 2:当某个线程执行业务代码异常,而无法解锁  那么便会触发看门狗机制 自动解锁,
         * 并且当业务代码执行完成后,没有手动解锁,看门狗也会自动解锁  避免造成死锁问题
         */
        //lock.lock();
        
        /**
         * 自定义设置解锁时间,不会自动续期,需要自定义设置时长 > 业务执行时长  否则容易造成死锁问题
         *  比较:
         *      如果传了锁的超时时长,那么便给redis传了执行脚本(LUA),并且默认时长就是我们设置的时长
         *      如果没有传超时时长,那么会启用看门狗【getLockWatchdogTimeout】默认时长30s,并且能够自动续期30s,
         *      this.internalLockLeaseTime[看门狗时间] / 3L: 每隔(看门狗时长/3)=10s 的时间重置时长,即每当 key 的 ttl(剩余时间)为 20s 的时候,则进行续命操作,重新将 key 的过期时间设置为默认时间 30s
         * 默认情况下,看门狗的检查锁的超时时间是30秒钟,也可以通过修改Config.lockWatchdogTimeout来另行指定
         */
        //推荐使用
        lock.lock(10, TimeUnit.SECONDS);
        try {
            System.out.println(Thread.currentThread().getName()+"拿到了锁。。。。。。"+Thread.currentThread().getId());
            Thread.sleep(30000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println(Thread.currentThread().getName()+"释放了锁。。。。。。"+Thread.currentThread().getId());
            //相当于删除redis 中key
            lock.unlock();
        }
        return "hello";
    }

自定义设置超时时间和默认超时时间的源码:
基于Redisson分布式系统实战案列_第1张图片
基于Redisson分布式系统实战案列_第2张图片

三 Redisson 分布式锁-读写锁 测试

    /**
     * 写锁 --> 排他锁(相当于只能有一个锁的存在 即:独享锁)
     */
    @GetMapping("/test/write")
    public String writeValue() {
        // 读写锁
        RReadWriteLock lock = redissonClient.getReadWriteLock("rw-lock");
        RLock rLock = lock.writeLock();
        String s = "";
        try {
            rLock.lock();
            s = UUID.randomUUID().toString();
            Thread.sleep(3000);
            stringRedisTemplate.opsForValue().set("writeValue", s);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            rLock.unlock();
        }
        return s;
    }

    /**
     * 读锁 --> 共享锁
     * 当在write时,read无法读取。必须等待,
     * 当write完成后,便可read最新的值(write是排他锁)
     * 
     * 读+读:相当于无锁状态,并发读 只会在redis中进行记录,所有读锁都会 同步一起加锁然后解锁
     * 读+写:只有正在执行的读完成后,才能进行写
     * 写+读:只有写完成后,才能读取
     * 写+写:堵塞方式
     */
    @GetMapping("/test/read")
    public String readValue() {
        RReadWriteLock lock = redissonClient.getReadWriteLock("rw-lock");
        logger.info("lock==>"+lock);
        RLock rLock = lock.readLock();
        String s = "";
        rLock.lock();
        try {
            System.out.println("读锁。。。。 加锁");
            s = stringRedisTemplate.opsForValue().get("writeValue");
            Thread.sleep(20000);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            System.out.println("读锁。。。。 解锁");
            rLock.unlock();
        }
        return s;
    }

四 Redisson 分布式锁-闭锁 测试

    /**
     * 闭锁 只有设定的人全通过才关门
     */
    @GetMapping("/test/lockDoor")
    public String lockDoor() throws InterruptedException {
    	// 获取redis中 key=door的值
    	//倘若redis中没有 door 那么就会set到redis中,成功后则会执行
        RCountDownLatch door = redissonClient.getCountDownLatch("door");
        // 设置这里有5个人
        door.trySetCount(5);
        // door 不为0 则持续等待
        door.await();
        return "5个人全部通过了...可以闭锁了";
    }

    @GetMapping("/test/go/{id}")
    public String go(@PathVariable("id") Long id) {
        RCountDownLatch door = redissonClient.getCountDownLatch("door");
        // 每访问一次相当于出去一个人 -1
        door.countDown();
        return id + "走了";
    }

五 Redisson 分布式锁-信号量 测试

    /**
     * 尝试获取杯子中的水 [信号量]  ,每次调用 redis park的值会减1,直到0停止
     * 
     * 信号量:也可以用作限流  《场景:请求进入 则调用release方法,每次调用会加1,当达到最大并发数量请求时,禁止调用此方法;					
     * 						      acquire 拿取信号量,调用一次acquire方法时则减少1》
     */
    @GetMapping("/test/acquire ")
    public String acquire () throws InterruptedException {
        RSemaphore park = redissonClient.getSemaphore("park");
        //堵塞等待式拿取信号量,前端请求会持续进行(转圈)
        //park.acquire();

        //尝试拿取信号(水) 拿不到返回false
        boolean acquire = park.tryAcquire();
        return "获取水 =>" + acquire;
    }

    /**
     * 尝试往杯子中倒入水,每次调用 redis  park的值会加1
     */
    @GetMapping("/test/release")
    public String release() {
        RSemaphore park = redissonClient.getSemaphore("park");
        //释放信号(水量)
        park.release();
        return "ok => 水量+1";
    }

六 分布式 -缓存一致性问题

以下举例两种模式解决分布式缓存一致性问题的弊端: 双写模式 失效模式

容易造成 脏数据:1同步缓存时比较慢 2比较快
基于Redisson分布式系统实战案列_第3张图片
容易造成 脏数据:
1正常更新并删除了缓存
2写入新的缓存比较慢,导致3快速的读取了1的缓存,如果说3更新缓存比较快,那么2会进行删除缓存,无影响;否则3会更新成1的数据缓存
基于Redisson分布式系统实战案列_第4张图片
解决方案:一般情况对于频繁更新或者实时性较高的数据,不放入缓存,,普通数据可采用以下:

  1. 设置redis过期时间
  2. 加入分布式读写锁 RReadWriteLock lock = redissonClient.getReadWriteLock(“rw-lock”);

你可能感兴趣的:(java,微服务架构,java,redis,redisson,分布式,微服务架构)