云原生微服务之分布式锁框架 Redisson

发布时间:2024年01月15日

🌹作者主页:青花锁 🌹简介:Java领域优质创作者🏆、Java微服务架构公号作者😄
🌹简历模板、学习资料、面试题库、技术互助

🌹文末获取联系方式 📝

在这里插入图片描述

系列专栏目录

[Java项目实战] 介绍Java组件安装、使用;手写框架等

[Aws服务器实战] Aws Linux服务器上操作nginx、git、JDK、Vue等

[Java微服务实战] Java 微服务实战,Spring Cloud Netflix套件、Spring Cloud Alibaba套件、Seata、gateway、shadingjdbc等实战操作

[Java基础篇] Java基础闲聊,已出HashMap、String、StringBuffer等源码分析,JVM分析,持续更新中

[Springboot篇] 从创建Springboot项目,到加载数据库、静态资源、输出RestFul接口、跨越问题解决到统一返回、全局异常处理、Swagger文档

[Spring MVC篇] 从创建Spring MVC项目,到加载数据库、静态资源、输出RestFul接口、跨越问题解决到统一返回

[华为云服务器实战] 华为云Linux服务器上操作nginx、git、JDK、Vue等,以及使用宝塔运维操作添加Html网页、部署Springboot项目/Vue项目等

[Java爬虫] 通过Java+Selenium+GoogleWebDriver 模拟真人网页操作爬取花瓣网图片、bing搜索图片等

[Vue实战] 讲解Vue3的安装、环境配置,基本语法、循环语句、生命周期、路由设置、组件、axios交互、Element-ui的使用等

[Spring] 讲解Spring(Bean)概念、IOC、AOP、集成jdbcTemplate/redis/事务等


系列文章目录

第一章 Java线程池技术应用
第二章 CountDownLatch和Semaphone的应用
第三章 Spring Cloud 简介
第四章 Spring Cloud Netflix 之 Eureka
第五章 Spring Cloud Netflix 之 Ribbon
第六章 Spring Cloud 之 OpenFeign
第七章 Spring Cloud 之 GateWay
第八章 Spring Cloud Netflix 之 Hystrix
第九章 代码管理gitlab 使用
第十章 SpringCloud Alibaba 之 Nacos discovery
第十一章 SpringCloud Alibaba 之 Nacos Config
第十二章 Spring Cloud Alibaba 之 Sentinel
第十三章 JWT
第十四章 RabbitMQ应用
第十五章 RabbitMQ 延迟队列
Java锁的分类
分布式锁框架-Redisson


前言

Redisson 在基于 NIO 的 Netty 框架上,充分的利?了 Redis 键值数据库提供的?系列优势,在Java 实??具包中常?接?的基础上,为使?者提供了?系列具有分布式特性的常??具类。使得原本作为协调单机多线程并发程序的?具包获得了协调分布式多机多线程并发系统的能?,??降低了设计和研发?规模分布式系统的难度。同时结合各富特?的分布式服务,更进?步简化了分布式环境中程序相互之间的协作。

1、redisson工作原理

在这里插入图片描述

2、看门狗原理

A服务先运行,在运行B服务,还没释放A的锁,A就挂了,会不会死锁呢?
答:没有导致死锁,因为底层有看门狗机制
默认指定锁时间为30s(看门狗时间)
锁的自动续期:若是业务超长,运行期间自动给锁上新的 30s,不用担心业务时间过长,锁就自动过期
加锁的业务只要运行完成,就不会给当前锁续期,及时不手动解锁,锁默认在30s 后自动删除。

3、spring boot与redisson的整合

3.1、添加库存服务:

stock-service

3.2、添加依赖

Maven

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
   <!-- <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        <version>2021.0.4.0</version>
    </dependency>-->
    <dependency>
        <groupId>org.redisson</groupId>
        <artifactId>redisson</artifactId>
        <version>3.19.1</version>
    </dependency>

Gradle

ext {
  //set('redissonVersion', "3.19.1") 
  redissonVersion = "3.19.1"
}
dependencies {
  implementation('org.springframework.boot:spring-boot-starter-web') {
    exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat'
  }
  implementation("org.redisson:redisson:${redissonVersion}")
  testImplementation('org.springframework.boot:spring-boot-starter-test')
  
}

3.3、添加配置

3.3.1、单机

redisson:
  addr:
    singleAddr:
      host: redis://localhost:6379
      password: 123456
      database: 0
      pool-size: 10

3.3.2、集群

redisson:
  addr:
    cluster:
      hosts: redis://47.96.11.185: 6370,...,redis://47.96.11.185:6373
      password : 123456

3.3.3、主从

redisson:
  addr:
    masterAndSlave:
      masterhost: redis : //47.96.11.185 : 6370
      slavehosts: redis://47.96.11.185: 6371,redis://47.96.11.185:6372
      password : 123456
      database : 0

3.4、配置RedissonClient

3.4.1、单机

/**
 * 配置RedissonClient
 */
@Configuration
public class RedissonConfig {
    @Value("${redisson.addr.singleAddr.host}")
    private String host;
    @Value("${redisson.addr.singleAddr.password}")
    private String password;
    @Value("${redisson.addr.singleAddr.database}")
    private int database;
    @Value("${redisson.addr.singleAddr.pool-size}")
    private int poolSize;
    @Bean
    public RedissonClient redissonClient(){
        Config config = new Config();
        config.useSingleServer().setAddress(host)
                .setPassword(password)
                .setDatabase(database)
                .setConnectionPoolSize(poolSize)
                .setConnectionMinimumIdleSize(poolSize);
        return Redisson.create(config);
    }
}

3.4.2、集群

/**
 * 配置RedissonClient
 */
@Configuration
public class RedissonConfig {
    @Value("${redisson.addr.cluster.hosts}")
    private String hosts;
    @Value("${redisson.addr.cluster.password}")
    private String password;

    /**
     * 集群模式
     *
     * @return
     */
    @Bean
    public RedissonClient redissonClient() {
        Config config = new Config();
        config.useClusterServers().addNodeAddress(hosts.split("[,]"))
                .setPassword(password)
                .setScanInterval(2000)
                .setMasterConnectionPoolSize(10000)
                .setSlaveConnectionPoolSize(10000);
        return Redisson.create(config);
    }
}

3.4.3、集群

/**
 * 配置RedissonClient
 */
@Configuration
public class RedissonConfig {
    @Value("${redisson.addr.masterAndSlave.masterhost}")
    private String masterhost;
    @Value("${redisson.addr.masterAndSlave.slavehosts}")
    private String slavehosts;
    @Value("${redisson.addr.masterAndSlave.password}")
    private String password;
    @Value("${redisson.addr.masterAndSlave.database}")
    private int database;

    /**
     * 主从模式
     *
     * @return
     */
    @Bean
    public RedissonClient redissonClient() {
        Config config = new Config();
        config.useMasterSlaveServers()
                .setMasterAddress(masterhost)
                .addSlaveAddress(slavehosts.split("[,]"))
                .setPassword(password)
                .setDatabase(database)
                .setMasterConnectionPoolSize(10000)
                .setSlaveConnectionPoolSize(10000);
        return Redisson.create(config);
    }
}

3.5、Redisson的使用

  • 获取锁 —— 公平锁和?公平锁
    // 获取公平锁
    RLock lock = redissonClient . getFairLock ( skuId );
    // 获取?公平锁
    RLock lock = redissonClient . getLock ( skuId );
  • 加锁 —— 阻塞锁和?阻塞锁
    // 阻塞锁(如果加锁成功之后,超时时间为 30s ;加锁成功开启看?狗,剩 5s 延?过期时间)
    lock . lock ();
    // 阻塞锁(如果加锁成功之后,设置?定义 20s 的超时时间)
    lock . lock ( 20 , TimeUnit . SECONDS );
    // ?阻塞锁(设置等待时间为 3s ;如果加锁成功默认超时间为 30s )
    boolean b = lock . tryLock ( 3 , TimeUnit . SECONDS );
    // ?阻塞锁(设置等待时间为 3s ;如果加锁成功设置?定义超时间为 20s )
    boolean b = lock . tryLock ( 3 , 20 , TimeUnit . SECONDS );
  • 释放锁
    lock . unlock ();
  • 应?示例
    // 公平?阻塞锁
    RLock lock = redissonClient . getFairLock ( skuId );
    boolean b = lock . tryLock ( 3 , 20 , TimeUnit . SECONDS );
  • 减库存加锁案例

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("/stock")
public class StockController {
    @Autowired
    private RedissonClient redissonClient;
    @GetMapping("/reduceStock")
    public void reduceStock(@RequestParam String productId){
        // 获取?公平锁
        RLock lock = this.redissonClient.getLock("stock:" + productId);
        // 阻塞锁(如果加锁成功之后,设置?定义 20s 的超时时间)
        lock.lock(30, TimeUnit.SECONDS);
        System.out.println("加锁成功." + Thread.currentThread().getName());

        try {
            TimeUnit.SECONDS.sleep(25);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            System.out.println("解锁成功." + Thread.currentThread().getName());
            lock.unlock();
        }

    }
}

测试:浏览器发起两次两次减库存
http://localhost:8099/stock/reduceStock?productId=001

3.6、aop实现分布式锁

3.6.1、定义注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.concurrent.TimeUnit;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DistributeLock {
    /**
     * 参数下标
     * @return
     */
    int[] lockIndex() default {-1} ;

    /**
     * 锁的等待时间
     * @return
     */
    long waitTime() default 3000;

    /**
     * 时间单位
     * @return
     */
    TimeUnit timeUnit() default TimeUnit.MILLISECONDS;
}

3.6.2、定义切面

/**
 * 定义分布式锁的切面
 */
@Component
@Aspect
public class DistributeLockAspect {
    @Autowired
    private RedissonClient redissonClient;

    @Around(value = "@annotation(lock)")
    public void distibuteLock(ProceedingJoinPoint proceedingJoinPoint, DistributeLock lock){
        Signature signature = proceedingJoinPoint.getSignature();
        StringBuilder stringBuilder = new StringBuilder();
        //方法所属的类
        String declaringTypeName = signature.getDeclaringTypeName();
        String name = signature.getName();

        stringBuilder.append(declaringTypeName);
        stringBuilder.append(name);

        //获取调用方法的参数
        Object[] args = proceedingJoinPoint.getArgs();

        int[] ints = lock.lockIndex();

        if(args != null) {
            final int length = args.length;
            if (length >0) {
                //考虑下标越界
                for (int anInt : ints) {
                    //把合法下标值放到sb
                    if (anInt >= 0 && anInt < length){
                        stringBuilder.append(JSON.toJSONString(args[anInt]));
                    }
                }
            }
        }

        //将方法的信息转成md5,作为锁的标识
        String key = SecureUtil.md5(stringBuilder.toString());


        //获取锁
        RLock rLock = redissonClient.getLock(key);
        //从注解获取时间单位
        TimeUnit timeUnit = lock.timeUnit();
        //从注解等待时间
        long waitTime = lock.waitTime();

        //执行业务代码
        try {
            //加锁
            rLock.tryLock(waitTime,timeUnit);
            System.out.println("成功加锁。" + Thread.currentThread().getName());
            proceedingJoinPoint.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
        }finally {
            //解锁
            rLock.unlock();
            System.out.println("成功解锁。" + Thread.currentThread().getName());
        }
    }
}

注解的使用:

@DistributeLock(lockIndex = {0,1},waitTime = 3,timeUnit = TimeUnit.SECONDS)

联系方式

微信公众号:Java微服务架构

在这里插入图片描述

文章来源:https://blog.csdn.net/s445320/article/details/135592454
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。