springboot整合redission分布式锁

文章介绍了如何在SpringBoot项目中集成Redisson库,包括依赖引入、配置文件设置以及单节点和集群配置方法。接着展示了Redisson锁的使用,如加锁、解锁,以及看门狗机制确保锁的自动释放。最后探讨了读写锁的概念,保证了数据读写的并发控制。
摘要由CSDN通过智能技术生成

一、项目搭建

1、坐标引入

        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson-spring-boot-starter</artifactId>
            <version>3.15.5</version>
        </dependency>

2、配置

  • 1、application.yml
spring:
  redis:
    database: 1
    timeout: 60000
    password: admin123
    host: 127.0.0.1
    port: 6379
  • 2、单节点配置
package com.redission.demo.config;

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.config.SingleServerConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author DeyouKong
 * @description TODO
 * @date 2023/3/23 23:34
 */

@Configuration
public class MyRedissonConfig {

    @Value(value = "${spring.redis.host}")
    private String host;
    @Value(value = "${spring.redis.port}")
    private int port;
    @Value(value = "${spring.redis.database}")
    private int database;
    @Value(value = "${spring.redis.password}")
    private String password;

    /**
     * 单Redis节点模式配置方法
     * 其他配置參數,看:
     * <a href = "https://github.com/redisson/redisson/wiki/2.-%E9%85%8D%E7%BD%AE%E6%96%B9%E6%B3%95#26-%E5%8D%95redis%E8%8A%82%E7%82%B9%E6%A8%A1%E5%BC%8F">
     * 单Redis节点模式配置方法
     * </a>
     *
     * @return {@link RedissonClient}
     */
    @Bean(destroyMethod = "shutdown")
    RedissonClient redisson() {
        Config config = new Config();
        //Redis多节点
        // config.useClusterServers()
        //     .addNodeAddress("redis://127.0.0.1:6379", "redis://127.0.0.1:7001");


        //Redis单节点
        SingleServerConfig singleServerConfig = config.useSingleServer();
        //可以用"rediss://"来启用SSL连接
        String address = "redis://" + host + ":" + port;
        singleServerConfig.setAddress(address);
        //设置 数据库编号
        singleServerConfig.setDatabase(database);
        singleServerConfig.setPassword(password);
        //连接池大小:默认值:64
        // singleServerConfig.setConnectionPoolSize()
        return Redisson.create(config);
    }

}

3、以下两种为官网提供的参考:

1、单节点

// 默认连接地址 127.0.0.1:6379
RedissonClient redisson = Redisson.create();

Config config = new Config();
config.useSingleServer().setAddress("myredisserver:6379");
RedissonClient redisson = Redisson.create(config);

2、集群

Config config = new Config();
config.useClusterServers()
    .setScanInterval(2000) // 集群状态扫描间隔时间,单位是毫秒
    //可以用"rediss://"来启用SSL连接
    .addNodeAddress("redis://127.0.0.1:7000", "redis://127.0.0.1:7001")
    .addNodeAddress("redis://127.0.0.1:7002");

RedissonClient redisson = Redisson.create(config);

二、Demo

@RestController
@RequestMapping("/redission")
public class RedissionController {
    
    @Resource
    private RedissonClient redissonClient;

    private final static String LOCK="I_AM_LOCK";

    @GetMapping("/list")
    public String getString(){
        // 1、获取一把锁,只要锁的名字一样,既是同一把锁
        RLock lock = redissonClient.getLock(LOCK);
        // 2、加锁
        lock.lock();     // // 阻塞式等待
        try {
            System.out.println(Thread.currentThread().getName()+":\t 获得锁");
            Thread.sleep(5000);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            // 3、解锁
            lock.unlock();
            System.out.println(Thread.currentThread().getName()+":\t 释放锁锁");
        }
        return "123";
    }
}

打开两个浏览器页面,同时访问接口,结果打印如下:
在这里插入图片描述

三、看门狗原理

1、模拟两个服务,A服务闪掉还没释放锁的情况

A服务先运行,在运行B服务,还没释放A的是否,A就挂了,锁也还没释放,会不会死锁呢??
在这里插入图片描述
启动两台,8080,8081(闪断–手动关闭)

发现80没有因为81没有释放而导致死锁,因为底层有看门狗机制。

追溯源码:自旋,不断尝试获取锁

2、默认 lock() 小结

lock.lock ();
(1)默认指定锁时间为30s(看门狗时间)
(2)锁的自动续期:若是业务超长,运行期间自动给锁上新的 30s,不用担心业务时间过长,锁就自动过期
(3)加锁的业务只要运行完成,就不会给当前锁续期,及时不手动解锁,锁默认在30s 后自动删除。

3、指定时间 lock(10, TimeUnit.SECONDS) — 【推荐写法】

// 10s自动解锁,指定时间一定要大于业务时间(不然会报错,没把握就不要用)
lock.lock (10, TimeUnit.SECONDS);

4、指定时间 lock() 小结

问题:在锁到期的时候,不会自动续期。
(1)如果我们传递了锁的超时时间,就发送给 redis执行脚本,进行占锁,默认的超时时间既我们指的时间
(2)若是未指定锁的超时时间,就使用 30*1000【LockWatchdogTimeout看门狗的默认时间】
(3)只要占锁成功,就会启动一个定时任务【重新给锁设置过期时间,新的过期时间就是看门狗的默认时间】,每隔 10 s都会自动再次续到30s, internallockLeaseTime【看门狗时间/3s】

四、读写锁

1、代码

    @Autowired
    RedissonClient redisson;
    @Autowired
    RedisTemplate redisTemplate;


    // 并发写测试
    @GetMapping("write")
    @ResponseBody
    public String writeValue(){
        RReadWriteLock myLock = redisson.getReadWriteLock ("my_lock");
        RLock rLock = myLock.writeLock ( );
        rLock.lock ();
        String s = "";
        try {
            System.out.println ("写锁加锁成功..."+Thread.currentThread ().getId () );
            s= UUID.randomUUID ().toString ();
            TimeUnit.SECONDS.sleep (30);
            redisTemplate.opsForValue ().set ("writeValue",s);
        } catch (Exception e) {
            e.printStackTrace ( );
        }
        finally {
            System.out.println ("写锁解锁成功..."+Thread.currentThread ().getId () );
            rLock.unlock ();
        }
        return s;
    }
    
    // 并发读测试
    @GetMapping("read")
    @ResponseBody
    public String readValue(){
        RReadWriteLock myLock = redisson.getReadWriteLock ("my_lock");
        RLock rLock = myLock.readLock ( );
        rLock.lock ();
        String s = "";
        try {
            System.out.println ("读锁加锁成功..."+Thread.currentThread ().getId () );
            s = (String) redisTemplate.opsForValue ().get ("writeValue");
        } catch (Exception e) {
            e.printStackTrace ( );
        }finally {
            System.out.println ("读锁解锁成功..."+Thread.currentThread ().getId () );
            rLock.unlock ();
        }
        return s;
    }

2、结论

保证一定能读到最新数据,修改期间,写锁是一个排它锁(互斥锁)。读锁是一个共享锁
(1)读+读:相当于无锁,并发读情况下,只会在 redis 中记录好,所有当前的读锁,他们都会加锁成功。
(2)写+读:等待写锁释放
(3)写+写:阻塞方式
(4)读+写:有读锁,写也需要等待

只要有写的存在,都必须等待

摘自:https://www.jianshu.com/p/b544aa1e46f7

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值