《微服务实战》 第二十八章 分布式锁框架-Redisson

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: 《微服务实战》 第二十八章 分布式锁框架-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、添加依赖

<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>

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)


相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
1月前
|
XML JSON API
ServiceStack:不仅仅是一个高性能Web API和微服务框架,更是一站式解决方案——深入解析其多协议支持及简便开发流程,带您体验前所未有的.NET开发效率革命
【10月更文挑战第9天】ServiceStack 是一个高性能的 Web API 和微服务框架,支持 JSON、XML、CSV 等多种数据格式。它简化了 .NET 应用的开发流程,提供了直观的 RESTful 服务构建方式。ServiceStack 支持高并发请求和复杂业务逻辑,安装简单,通过 NuGet 包管理器即可快速集成。示例代码展示了如何创建一个返回当前日期的简单服务,包括定义请求和响应 DTO、实现服务逻辑、配置路由和宿主。ServiceStack 还支持 WebSocket、SignalR 等实时通信协议,具备自动验证、自动过滤器等丰富功能,适合快速搭建高性能、可扩展的服务端应用。
100 3
|
2月前
|
安全 应用服务中间件 API
微服务分布式系统架构之zookeeper与dubbo-2
微服务分布式系统架构之zookeeper与dubbo-2
|
2月前
|
负载均衡 Java 应用服务中间件
微服务分布式系统架构之zookeeper与dubbor-1
微服务分布式系统架构之zookeeper与dubbor-1
|
14天前
|
运维 NoSQL Java
后端架构演进:微服务架构的优缺点与实战案例分析
【10月更文挑战第28天】本文探讨了微服务架构与单体架构的优缺点,并通过实战案例分析了微服务架构在实际应用中的表现。微服务架构具有高内聚、低耦合、独立部署等优势,但也面临分布式系统的复杂性和较高的运维成本。通过某电商平台的实际案例,展示了微服务架构在提升系统性能和团队协作效率方面的显著效果,同时也指出了其带来的挑战。
54 4
|
14天前
|
机器学习/深度学习 自然语言处理 并行计算
DeepSpeed分布式训练框架深度学习指南
【11月更文挑战第6天】随着深度学习模型规模的日益增大,训练这些模型所需的计算资源和时间成本也随之增加。传统的单机训练方式已难以应对大规模模型的训练需求。
58 3
|
1月前
|
缓存 NoSQL Java
大数据-50 Redis 分布式锁 乐观锁 Watch SETNX Lua Redisson分布式锁 Java实现分布式锁
大数据-50 Redis 分布式锁 乐观锁 Watch SETNX Lua Redisson分布式锁 Java实现分布式锁
59 3
大数据-50 Redis 分布式锁 乐观锁 Watch SETNX Lua Redisson分布式锁 Java实现分布式锁
|
18天前
|
机器学习/深度学习 并行计算 Java
谈谈分布式训练框架DeepSpeed与Megatron
【11月更文挑战第3天】随着深度学习技术的不断发展,大规模模型的训练需求日益增长。为了应对这种需求,分布式训练框架应运而生,其中DeepSpeed和Megatron是两个备受瞩目的框架。本文将深入探讨这两个框架的背景、业务场景、优缺点、主要功能及底层实现逻辑,并提供一个基于Java语言的简单demo例子,帮助读者更好地理解这些技术。
42 2
|
30天前
|
Dubbo Java 应用服务中间件
Dubbo学习圣经:从入门到精通 Dubbo3.0 + SpringCloud Alibaba 微服务基础框架
尼恩团队的15大技术圣经,旨在帮助开发者系统化、体系化地掌握核心技术,提升技术实力,从而在面试和工作中脱颖而出。本文介绍了如何使用Dubbo3.0与Spring Cloud Gateway进行整合,解决传统Dubbo架构缺乏HTTP入口的问题,实现高性能的微服务网关。
|
30天前
|
消息中间件 存储 负载均衡
微服务与分布式系统设计看这篇就够了!
【10月更文挑战第12天】 在现代软件架构中,微服务和分布式系统设计已经成为构建可扩展、灵活和可靠应用程序的主流方法。本文将深入探讨微服务架构的核心概念、设计原则和挑战,并提供一些关于如何在分布式系统中实现微服务的实用指导。
48 2
|
30天前
|
人工智能 文字识别 Java
SpringCloud+Python 混合微服务,如何打造AI分布式业务应用的技术底层?
尼恩,一位拥有20年架构经验的老架构师,通过其深厚的架构功力,成功指导了一位9年经验的网易工程师转型为大模型架构师,薪资逆涨50%,年薪近80W。尼恩的指导不仅帮助这位工程师在一年内成为大模型架构师,还让他管理起了10人团队,产品成功应用于多家大中型企业。尼恩因此决定编写《LLM大模型学习圣经》系列,帮助更多人掌握大模型架构,实现职业跃迁。该系列包括《从0到1吃透Transformer技术底座》、《从0到1精通RAG架构》等,旨在系统化、体系化地讲解大模型技术,助力读者实现“offer直提”。此外,尼恩还分享了多个技术圣经,如《NIO圣经》、《Docker圣经》等,帮助读者深入理解核心技术。
SpringCloud+Python 混合微服务,如何打造AI分布式业务应用的技术底层?

热门文章

最新文章