基于Redisson 实现 Redis 分布式锁

news2024/9/20 10:50:06

代码示例:

    @GetMapping("/testJmeter")
    public void testJmeter() {

        synchronized (this){
            int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"))
            if (stock > 0) {
               int realStock = stock - 1;
               stringRedisTemplate.opsForValue().set("stock",realStock);
               System.out.println("扣减成功,剩余:"+ realStock);
            } else {
               System.out.println("扣减失败");
            }
        }
    }

问题:上面这种方法在单台Tomcat 服务中,可以实现 防止超卖问题,可是在实际项目开发中,我们部署项目一般不会只使用一台服务器 部署,一般都是多台服务器 通过 Nginx 实现服务代理,负载均衡。 这样简单使用 synchronized 是不可取的!!!

简单优化方法:

    @GetMapping("/testJmeter")
    public void testJmeter() {
      try {
      String lockKey = "lockKey";
      Boolean  result = stringRedisTempalte.opsForValue.setIfAbsent(lockKey,"zhuzhe");
      if (!result) {
         return "错误";
      } 
            int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"))
            if (stock > 0) {
               int realStock = stock - 1;
               stringRedisTemplate.opsForValue().set("stock",realStock);
               System.out.println("扣减成功,剩余:"+ realStock);
            } else {
               System.out.println("扣减失败");
            }
       } finaly{
        stringRedisTempalte.delete(lockKey);
       }
    }

问题:但是上述容易出现,某台机器突然宕机的现象,就无法走到finally 中去释放锁了。导师redis 中 的锁还没有释放。别的机器就无法获得锁了。 就会出现死锁问题。

优化方法:

可以实现对 给这把锁加上一个过期时间

    @GetMapping("/testJmeter")
    public void testJmeter() {
      try {
      String lockKey = "lockKey";
      Boolean  result = stringRedisTempalte.opsForValue
.setIfAbsent(lockKey,"zhuzhe",10,TimeUnit.SECONDS); // 设置过期时间
      if (!result) {
         return "错误";
      } 
      int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"))
      if (stock > 0) {
          int realStock = stock - 1;
          stringRedisTemplate.opsForValue().set("stock",realStock);
          System.out.println("扣减成功,剩余:"+ realStock);
      } else {
          System.out.println("扣减失败");
       }} finaly{
        stringRedisTempalte.delete(lockKey);
       }
    }

 依然会有点问题--->失效时间不好设置,其中业务逻辑出现 SQL 慢查询导致代码执行时间长,在高并发情况下,会出现删 错锁的情况。A服务把 B服务中的锁 删除了。

优化方法:

每个线程都生成一个唯一id

    @GetMapping("/testJmeter")
    public void testJmeter() {
      try {
      String lockKey = "lockKey";
      String clientId = UUID.randomUUID.totring();

      Boolean  result = stringRedisTempalte.opsForValue
.setIfAbsent(lockKey ,clientId ,10,TimeUnit.SECONDS); // 设置过期时间
      if (!result) {
         return "错误";
      } 
      int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"))
      if (stock > 0) {
          int realStock = stock - 1;
          stringRedisTemplate.opsForValue().set("stock",realStock);
          System.out.println("扣减成功,剩余:"+ realStock);
      } else {
          System.out.println("扣减失败");
       }} finaly{
        if (clientId.equals(stringRedisTempalte.opsForValue.getLockKey)){
           stringRedisTempalte.delete(lockKey);
        }
       
       }
    }

注意:其实还可以使用,创建一个定时任务,每10s 检查这个主线程的锁是否过期,如果快过期了,任务还没有结束,在延长30s.

最终解决方法:

引入 Redission 依赖

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.6.5</version>
</dependency>

配置:

@Configuration
public class RedissonConfig {
 
    @Value("${spring.redis.host}")
    private String address;
    @Value("${spring.redis.port}")
    private String port;
    @Value("${spring.redis.password}")
    private String password;
 
    @Bean
    public RedissonClient redissonClient() {
        Config config = new Config();
        //此处为单机模式  (注:有多种模式)
        config.useSingleServer()
                .setAddress("redis://" + address + ":"+port)
                .setPassword(password)
                .setTimeout(10000)
                .setRetryAttempts(5)
                .setRetryInterval(2000)
                .setConnectionPoolSize(64)
                .setConnectionMinimumIdleSize(24)
                // 保持连接活动
                .setKeepAlive(true)
                // 发送PING命令的间隔时间;
                .setPingConnectionInterval(30000);
 
        return Redisson.create(config);
    }
}

 代码示例:

@Rescourse
private Redisson redisson;

    @GetMapping("/testJmeter")
    public void testJmeter() {
      try {
      String lockKey = "lockKey";
      String clientId = UUID.randomUUID.totring();
      
      //获取锁
      Rlock redissonLock = redisson.getLock(lockkey);


      //Boolean  result = stringRedisTempalte.opsForValue
.setIfAbsent(lockKey ,clientId ,10,TimeUnit.SECONDS); // 设置过期时间
      //if (!result) {
       //  return "错误";
     // } 
      //加锁
      redissonLock.lock()
       
      int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"))
      if (stock > 0) {
          int realStock = stock - 1;
          stringRedisTemplate.opsForValue().set("stock",realStock);
          System.out.println("扣减成功,剩余:"+ realStock);
      } else {
          System.out.println("扣减失败");
       }} finaly{
          //释放锁
          redissonLock.unlock()
       // if (clientId.equals(stringRedisTempalte.opsForValue.getLockKey)){
           
      
           //stringRedisTempalte.delete(lockKey);
        }
       
       }
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1923652.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【组件库】element-plus组件库

文章目录 0. 启动项目1. gc.sh 新增组件2. 本地验证(组件注册的方式)3. 官方文档修改3-1. 左侧菜单3-2 . 配置md文档3-3. 代码问题:文档修改----------------------------------------------4. 将naiveui的split 分割组件【 复制、迁移】到 element-ui-plus组件库4.1 naiveu…

Science Advances 仿生双模态触觉感知

研究背景 触觉感知在人类收集信息和接收周围环境反馈中扮演着至关重要的角色。随着人工智能的发展&#xff0c;具有类似人类感知能力的智能机器人受到越来越多的关注。现有的触觉传感器能够感知接触前的刺激和压力大小&#xff0c;但它们在区分物体类型、评估柔软度和量化杨氏…

go-高效处理应用程序数据

一、背景 大型的应用程序为了后期的排障、运营等&#xff0c;会将一些请求、日志、性能指标等数据保存到存储系统中。为了满足这些需求&#xff0c;我们需要进行数据采集&#xff0c;将数据高效的传输到存储系统 二、问题 采集服务仅仅针对某个需求开发&#xff0c;需要修改…

Docker容器的生命周期

引言 Docker 容器作为一种轻量级虚拟化技术&#xff0c;在现代应用开发和部署中扮演着重要角色。理解容器的生命周期对于有效地管理和运维容器化应用至关重要。本文将深入探讨 Docker 容器的生命周期&#xff0c;从创建到销毁的各个阶段&#xff0c;帮助读者更好地掌握容器管理…

分手后如何走出夜晚的抑郁,告别失眠困扰?

在这个快速变化的世界里&#xff0c;分手成为了许多人生活中不得不面对的现实。而每当夜幕降临&#xff0c;那种难以言表的孤独感和深深的抑郁往往让人倍感煎熬&#xff0c;甚至陷入失眠的漩涡。那么&#xff0c;分手后我们该如何应对这种情绪困扰&#xff0c;重新找回自己的宁…

防火墙NAT和智能选路实验详解(华为)

目录 实验概述实验拓扑实验要求要求一要求二要求三要求四要求五 实验概述 从我上面一个博客能够了解到NAT和防火墙选路原理 ——>防火墙nat和智能选路&#xff0c;这一章我通过实验来详解防火墙关于nat和智能选路从而能熟练使用和配置防火墙&#xff0c;这里使用的是华为US…

lvs集群、NAT模式和DR模式、keepalive

目录 lvs集群概念 集群的类型&#xff1a;三种类型 系统可靠性指标 lvs集群中的术语 lvs的工作方式 NAT模式 lvs的工具 算法 实验 数据流向 步骤 一 、调度器配置&#xff08;test1 192.168.233.10&#xff09; 二、RS配置&#xff08;nginx1和nginx2&#xff09;…

Android:如何绘制View

点击查看Android 如何绘制视图官网 一、简介 Android 框架会在 Activity 获得焦点时请求 Activity 绘制其布局。Android 框架会处理绘制流程&#xff0c;但该 Activity 必须提供其布局层次结构的根节点。 Android 框架会绘制布局的根节点&#xff0c;并测量和绘制布局树。它会…

【每日一练】python类和对象现实举例详细讲解

""" 本节课程目的&#xff1a; 1.掌握类描述现实世界实物思想 2.掌握类和对象的关系 3.理解什么事面向对象 """ #比如设计一个闹钟&#xff0c;在这里就新建一个类 class Clock:idNone #闹钟的序列号&#xff0c;也就是类的属性priceNone #闹…

Redis学习笔记(个人向)

Redis学习笔记(个人向) 1. 概述 是一个高性能的 key-value 数据库&#xff1b;其具有以下三个特点&#xff1a; Redis支持数据的持久化&#xff0c;可以将内存中的数据保存在磁盘中&#xff0c;重启的时候可以再次加载进行使用。Redis不仅仅支持简单的key-value类型的数据&…

Nginx+Keepalive调度的高可用

nginxkeepalive: 调度器的高可用 vip地址主备之间的切换&#xff0c;主在工作时&#xff0c;p地址只在主上&#xff0c;主停止工作&#xff0c;p飘移到备服务器。 在主备的优先级不变的情况下&#xff0c;主恢复工作&#xff0c;vp会飘回到主服务器。 1、配优先级 2、配置…

EventBus学习

视频&#xff1a;05_尚硅谷_EventBus_粘性事件案例_哔哩哔哩_bilibili 1.整体框架 2.demo下载地址&#xff1a;https://github.com/greenrobot/EventBus 3.实现非粘性时间流程&#xff1a; 3.1导入架包eventbus-3.0.0.jar和eventbus-3.0.0-sources.jar 3.2在接受数据页面注…

k8s(五)---名称空间

五、名称空间 名称空间是k8s划分不同工作空间的逻辑单位,是k8s资源逻辑隔离的机&#xff0c;。可以给不同的租户&#xff0c;不同的环境、不同的项目创建对应的命名空间。 1、查看名称空间 kubectl get ns kubectl get namespaces 此处展示了四个命名空间 2、管理名称空间 1…

更新商品前端接口编写

文章目录 新增页面书写写表单价格符号的显示然后状态的书写后端枚举书写时间书写使用组件 新增页面书写 书写直接复制页面 写表单的绑定信息 然后绑定表单 表单绑定还有表单数据的绑定 标签中ref的作用就是将 该组件注册到vue对象的ref属性中 那么在vue运行的时候,会加载所…

IOC、DI<4> Unity、AOP、MVCAOP、UnityAOP 区别

IOC&#xff08;&#xff09;&#xff1a;控制反转&#xff0c;把程序上层对下层的依赖&#xff0c;转移到第三方的容器来装配 是程序设计的目标&#xff0c;实现方式包含了依赖注入和依赖查找&#xff08;.net里面只有依赖注入&#xff09; DI&#xff1a;依赖注入&#xff0c…

【网络文明】关注网络安全

在这个数字化时代&#xff0c;互联网已成为我们生活中不可或缺的一部分&#xff0c;它极大地便利了我们的学习、工作、娱乐乃至日常生活。然而&#xff0c;随着网络空间的日益扩大&#xff0c;网络安全问题也日益凸显&#xff0c;成为了一个不可忽视的全球性挑战。认识到网络安…

Gitee简易使用流程(后期优化)

目录 1.修改用户名 2.文件管理 新建文件/文件夹流程如下&#xff1a; 上传文件流程如下&#xff1a; 以主页界面为起点 1.修改用户名 点解右上角的头像--> 点击“账号设置” 点击左边栏里的“个人资料“ 直接修改用户名即可 2.文件管理 选择一个有修改权限仓库&#…

【轻松拿捏】Java-final关键字(面试)

目录 1. 定义和基本用法 回答要点&#xff1a; 示例回答&#xff1a; 2. final 变量 回答要点&#xff1a; 示例回答&#xff1a; 3. final 方法 回答要点&#xff1a; 示例回答&#xff1a; 4. final 类 回答要点&#xff1a; 示例回答&#xff1a; 5. final 关键…

yolov8预测

yoloV8 官方地址 预测 -Ultralytics YOLO 文档 1.图片预测 from ultralytics import YOLO #### 图片预测1 ### https://www.youtube.com/watch?vneBZ6huolkg ### https://github.com/ultralytics/ultralytics ### https://github.com/abdullahtarek/football_analysis…

Linux C语言基础 day10

目录 学习目标&#xff1a; 学习内容&#xff1a; 1.指针指向数组 1.1 指针与数组的关系 1.2 指针与一维数组关系实现 1.2.1 指针与一维数组的关系 1.2.2 指针指向一维整型数组作为函数参数传递 课外作业&#xff1a; 学习目标&#xff1a; 一周掌握 C基础知识 学习内…