Redis 分布式锁测试

一、前提依赖(除去SpringBoot项目基本依赖外):

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-test</artifactId>
</dependency>

<!-- 配置使用redis启动器 -->
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<!-- 引入阿里fastjson2依赖 -->
<dependency>
   <groupId>com.alibaba.fastjson2</groupId>
   <artifactId>fastjson2</artifactId>
   <version>2.0.42</version>
</dependency>

<!--junit 测试-->
<dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
   <version>4.13.2</version>
</dependency>

二、我这里用到的实体类(Orderinfo ):

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
public class Orderinfo implements Serializable {
    private String onum;
    private Integer eid;
    private BigDecimal price;
    private String fromname;
    private String fromaddress;
    private String fromtel;
    private String toname;
    private String toaddress;
    private String totel;
    private String fromcardnum;
    private Integer state;
    private Date createtime;
    private static final long serialVersionUID = 1L;
    public String getOnum() {
        return onum;
    }
    public void setOnum(String onum) {
        this.onum = onum == null ? null : onum.trim();
    }
    public Integer getEid() {
        return eid;
    }
    public void setEid(Integer eid) {
        this.eid = eid;
    }
    public BigDecimal getPrice() {
        return price;
    }
    public void setPrice(BigDecimal price) {
        this.price = price;
    }
    public String getFromname() {
        return fromname;
    }
    public void setFromname(String fromname) {
        this.fromname = fromname == null ? null : fromname.trim();
    }
    public String getFromaddress() {
        return fromaddress;
    }
    public void setFromaddress(String fromaddress) {
        this.fromaddress = fromaddress == null ? null : fromaddress.trim();
    }
    public String getFromtel() {
        return fromtel;
    }
    public void setFromtel(String fromtel) {
        this.fromtel = fromtel == null ? null : fromtel.trim();
    }
    public String getToname() {
        return toname;
    }
    public void setToname(String toname) {
        this.toname = toname == null ? null : toname.trim();
    }
    public String getToaddress() {
        return toaddress;
    }
    public void setToaddress(String toaddress) {
        this.toaddress = toaddress == null ? null : toaddress.trim();
    }
    public String getTotel() {
        return totel;
    }
    public void setTotel(String totel) {
        this.totel = totel == null ? null : totel.trim();
    }
    public String getFromcardnum() {
        return fromcardnum;
    }
    public void setFromcardnum(String fromcardnum) {
        this.fromcardnum = fromcardnum == null ? null : fromcardnum.trim();
    }
    public Integer getState() {
        return state;
    }
    public void setState(Integer state) {
        this.state = state;
    }
    public Date getCreatetime() {
        return createtime;
    }
    public void setCreatetime(Date createtime) {
        this.createtime = createtime;
    }
}

三、场景-抢单

        1. 添加RedisLockUtil:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
@Component
public class RedisLockUtil {
   @Autowired
   private RedisTemplate<String, String> redisTemplate;
   /**
    * 加锁
    * @param key   键
    * @param value 当前时间 + 超时时间
    * @return 是否拿到锁
    */

   public boolean lock(String key, String value) {
      if (redisTemplate.opsForValue().setIfAbsent(key, value)) {
         return true;
      }
      String currentValue = redisTemplate.opsForValue().get(key);
      //如果锁过期
      if (!StringUtils.isEmpty(currentValue)
            && Long.parseLong(currentValue) < System.currentTimeMillis()) {
//设置新值,返回旧值
         String oldValue = redisTemplate.opsForValue().getAndSet(key, value);
         //是否已被别人抢占 比对currentValue 和oldValue 是否一致 确保未被其他人抢占
         return !StringUtils.isEmpty(oldValue) && oldValue.equals(currentValue);
      }
      return false;
   }

   /**
    * 解锁
    *
    * @param key   键
    * @param value 当前时间 + 超时时间
    */
   public void unlock(String key, String value) {
      try {
         String currentValue = redisTemplate.opsForValue().get(key);
         if (!StringUtils.isEmpty(currentValue) && currentValue.equals(value)) {
            redisTemplate.opsForValue().getOperations().delete(key);
         }
      } catch (Exception e) {
         System.out.println("redis解锁异常");
      }
   }
}

        2. 新建RedisLockTest类: 

import com.alibaba.fastjson2.JSON;
import com.logistics.order.entity.Orderinfo;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.ArrayList;
import java.util.List;
@SpringBootTest
@RunWith(SpringJUnit4ClassRunner.class)
@Controller
public class RedisLockTest {
    @Autowired
    public StringRedisTemplate stringRedisTemplate;

    @Autowired
    public RedisLockUtil redisLock;

    //模拟入库
    @Test
    public void insertRedis(){

        Orderinfo orderInfo1 = new Orderinfo();
        orderInfo1.setOnum("1");
        orderInfo1.setEid(1);

        Orderinfo orderInfo2 = new Orderinfo();
        orderInfo2.setOnum("2");
        orderInfo2.setEid(2);

        Orderinfo orderInfo3 = new Orderinfo();
        orderInfo3.setOnum("3");
        orderInfo3.setEid(3);

        List<Orderinfo> orderinfoList = new ArrayList<Orderinfo>();
        orderinfoList.add(orderInfo1);
        orderinfoList.add(orderInfo2);
        orderinfoList.add(orderInfo3);

        orderinfoList.forEach(x -> {
            stringRedisTemplate.boundHashOps("order").put(x.getOnum(), JSON.toJSONString(x));
        });

        System.out.println("入库成功。");
    }

    //压测
    @GetMapping("/getOrder")
    public void getOrder(String onum){

        //定义redis锁的key
        String lockkey = "orderkey";

        //定义锁的超时时间 1s
        Long ex = 1000L;
        String valueTimeout = System.currentTimeMillis()+ex+"";

        //判断锁是否加成功
        boolean lock = redisLock.lock(lockkey, valueTimeout);

        if(lock){
            String orderJson = (String)stringRedisTemplate.boundHashOps("order").get(onum);
            Orderinfo order = JSON.parseObject(orderJson, Orderinfo.class);
            System.out.println("订单:"+order.getOnum() +" 被抢到。");
            stringRedisTemplate.boundHashOps("order").delete(onum);

            //释放锁
            redisLock.unlock(lockkey,valueTimeout);
        }
    }
}

        3. 进入 Redis 的可视化客户端工具内查看添加信息:

        4. 模拟抢单:

@GetMapping("/getOrder")
    public void getOrder(String onum){
        //定义redis锁的key
        String lockkey = "orderkey";
        //定义锁的超时时间 1s
        Long ex = 1000L;
        String valueTimeout = System.currentTimeMillis()+ex+"";

        //判断锁是否加成功
        boolean lock = redisLock.lock(lockkey, valueTimeout);
        if(lock){
            String orderJson = (String)stringRedisTemplate.boundHashOps("order").get(onum);
            Orderinfo order = JSON.parseObject(orderJson, Orderinfo.class);
            System.out.println("订单:"+order.getOnum() +" 被抢到。");
            stringRedisTemplate.boundHashOps("order").delete(onum);
            //释放锁
            redisLock.unlock(lockkey,valueTimeout);
        }
    }

四、Jmeter压测:

        1. 创建线程组:

        2. 添加 HTTP 请求:

        3. 给一个 Linstener 监听的结果树:

        4. 模拟每秒 50 个请求:

        5. 设置请求及请求参数:

        6. 点击 5 图中的绿色小三角启动压测:

        Idea控制台:

Redis分布式锁:

分布式锁,是一种思想,它的实现方式有很多。比如,我们将沙滩当做分布式锁的组件,那么它看起来应该是这样的:

加锁

在沙滩上踩一脚,留下自己的脚印,就对应了加锁操作。其他进程或者线程,看到沙滩上已经有脚印,证明锁已被别人持有,则等待。

解锁

把脚印从沙滩上抹去,就是解锁的过程。

锁超时

为了避免死锁,我们可以设置一阵风,在单位时间后刮起,将脚印自动抹去。

分布式锁的实现有很多,比如基于数据库、memcached、Redis、系统文件、zookeeper等。它们的核心的理念跟上面的过程大致相同。

在这里我们通过单节点Redis实现一个简单的分布式锁。

1、加锁

加锁实际上就是在redis中,给Key键设置一个值,如果设置值成功,则表示客户端获得了锁。

2、解锁

解锁的过程就是将Key键删除

Redis分布式锁实现原理:

利用redis在同一时刻操作一个键的值只能有一个进程的特性,如果能设值成功就获取到锁。解锁,就是删除指定的键。为防止死锁可以设置锁超时时间,如果锁超时就释放锁。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

移动端APP自动化测试框架-UiAutomator2基础

很早以前&#xff0c;我用uiautomatorjava实践过Android APP自动化测试&#xff0c;不过今天要提的不是uiautomator&#xff0c;而是uiautomator2。听起来uiautomator2像是uiautomator的升级版&#xff0c;但是这两款框架仅仅是名字上比较相似&#xff0c;实际上没有任何关联。…

BiseNet实现遥感影像地物分类

遥感地物分类通过对遥感图像中的地物进行准确识别和分类&#xff0c;为资源管理、环境保护、城市规划、灾害监测等领域提供重要信息&#xff0c;有助于实现精细化管理和科学决策&#xff0c;提升社会治理和经济发展水平。深度学习遥感地物分类在提高分类精度、自动化程度、处理…

最长连续序列代码中的细节解读

最长连续序列 一、题目概述 给定一个未排序的整数数组 nums &#xff0c;找出数字连续的最长序列&#xff08;不要求序列元素在原数组中连续&#xff09;的长度。 请你设计并实现时间复杂度为 O(n) 的算法解决此问题。 原题地址&#xff1a;https://leetcode.cn/problems/l…

前端开发学习 (四) 自定义按键修饰符

一、 v-on的按键修饰符 按键修饰符&#xff0c;通俗的来说就是监听键盘输入的事件&#xff0c; 在Vue 中允许为 v-on 在监听键盘事件时添加按键修饰符 修饰符用途.enter当在输入框按下回车时触发.tab当按下tab时触发.delete当按下删除键&#xff08;通常是键盘上的Delete键&am…

软件工程 课后题 选择 查缺补漏

在一张状态图中只能有一个初态&#xff0c;而终态则可以没有&#xff0c;也可以有多个 所有的对象可以成为各种对象类&#xff0c;每个对象类都定义了一组 方法 通过执行对象的操作可以改变对象的属性&#xff0c;但它必须经过 消息 的传递 UML应用于 基于对象的面向对象的方…

主动张罗,持续改善!震坤行客服团队再获「全国青年文明号」殊荣

主动张罗&#xff0c;持续改善&#xff01;震坤行客服团队再获「全国青年文明号」殊荣 近日&#xff0c;第21届全国青年文明号集体评选结果揭晓。由共青团中央、最高人民法院、国家发展改革委、工业和信息化部等23家全国创建青年文明号活动组委会成员单位联合印发《关于命名第2…

升辉清洁IPO:广东清洁服务“一哥”还需要讲好全国化的故事

近日&#xff0c;广东物业清洁服务“一哥”升辉清洁第四次冲击IPO成功&#xff0c;拟于12月5日在香港主板挂牌上市。自2021年4月第一次递交招股书&#xff0c;时隔两年半&#xff0c;升辉清洁终于拿到了上市的门票。 天眼查显示&#xff0c;升辉清洁成立于2000年&#xff0c;主…

读SAM代码

def add_decomposed_rel_pos(attn: torch.Tensor,q: torch.Tensor,rel_pos_h: torch.Tensor, 27,80的全零训练参数rel_pos_w: torch.Tensor,q_size: Tuple[int, int], (14,14)k_size: Tuple[int, int], ) -> torch.Tensor:计算相对位置嵌入"""Calculate deco…

Taro 学习教程 - - - - - 开发环境的安装 helloworld

一、Taro脚手架安装 npm install tarojs/cli -g // or yarn add tarojs/cli -g // or cnpm install tarojs/cli -g1.1 如何判断taro安装成功 taro -v正常安装成功之后显示如图&#xff1a; 1.2 环境变量配置(自行判断是否需要手动配置) 如果遇到如下问题&#xff0c;则是需要…

基于stm32的LCD1602与无线蓝牙温湿度显示

这一篇博客是为了实现温湿度的显示&#xff0c;温湿度传感器将数据穿给单片机&#xff0c;单片机又把数据送给LCD1602和蓝牙&#xff0c;让温度和湿度可以再LCD1602显示屏和手机上显示&#xff0c;它的执行逻辑和C51那里基本一样&#xff0c;就是要修改程序&#xff0c;在程序上…

【Linux20.04-qt5.12.4软件安装与初步使用-qt在Linux使用-记录-笔记】

【Linux-qt软件安装与初步使用-qt在Linux使用-记录-笔记】 1、概述2、环境说明3、步骤总结1、了解并选择自己想要安装的版本2、访问 Qt 官方网站3、在 Qt 网站上找到下载部分&#xff08;自己想下载&#xff09;4、下载完成后&#xff0c;给安装程序文件赋予执行权限。5、自动配…

单显卡插槽安装英伟达Tesla P4 AI加速卡

Tesla P4是专业AI显卡&#xff0c;只有70瓦功耗&#xff0c;可以作为AI入门使用。 安装时碰到的几个问题&#xff1a; 首先因为单显卡插槽&#xff0c;就需要先安装好机器&#xff0c;然后ssh登录进行相关配置。安装的时候来回插拔了好多次&#xff01; 其次就是安装驱动时&a…

微信聊天记录年度报告

记忆恢复 若运行代码&#xff0c;执行下列命令安装 git clone https://github.com/LC044/WeChatMsg cd WeChatMsg pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple一、登录微信 切记需要先登录要提取的微信号的微信。 手机端使用聊天记录迁移功…

【电路笔记】-电阻器额定功率

电阻器额定功率 文章目录 电阻器额定功率1、概述2、电阻功率&#xff08;P&#xff09;3、功率电阻器4、电阻器额定功率示例15、电阻器额定功率示例2 电能被电阻吸收&#xff0c;因为它是电压和电流的乘积&#xff0c;一些电阻将这种电能转化为热量。 1、概述 当电流由于电阻器…

基础堆溢出原理与DWORD SHOOT实现

堆介绍 堆的数据结构与管理策略 程序员在使用堆时只需要做三件事情&#xff1a;申请一定大小的内存&#xff0c;使用内存&#xff0c;释放内存。 对于堆管理系统来说&#xff0c;响应程序的内存使用申请就意味着要在"杂乱"的堆区中"辨别"出哪些内存是正在…

实用篇 | 利用Flask+Postman为深度学习模型进行快速测试(超详细)

利用FlaskPostman为深度学习模型进行快速测试&#xff0c;以及算法中的一些实例&#xff0c;以后会更新一些新的模板~~ #本文环境&#xff1a;服务器Ubuntu20.04(docker) 目录 1.下载postrman 2.编写flas的app文件 3.在postrman发送请求 4.实例 在服务器创建app.py文件 …

12月2号作业

#include <iostream>using namespace std; class Sofa{ private:string setting;string *lying new string;public:Sofa(){cout << "Sofa::无参构造函数" << endl;}Sofa(string setting,string lying):setting(setting),lying(new string (lying)…

【shell】

shell 一、shell简介二、shell脚本的执行方式三、shell变量3.1 shell变量介绍3.2 shell变量的定义3.1.1 基本语法3.2.2 定义变量的规则3.2.3 将命令的返回值赋予变量 四、环境变量的设置4.1 基本语法&#xff1a; 五、位置参数变量5.1 基本介绍5.2 基本语法 六、预定义变量6.1 …

金蝶云星空表单插件单据体批量删除,序号自增

文章目录 金蝶云星空表单插件单据体批量删除&#xff0c;序号自增字段标识说明表单插件获取单据体数据包移除物料为空的行其他移除物料为空的行的方式&#xff0c;但是测试不通过&#xff0c;不建议使用序号重新生成测试 金蝶云星空表单插件单据体批量删除&#xff0c;序号自增…

新的 BLUFFS 攻击导致蓝牙连接不再私密

蓝牙是一种连接我们设备的低功耗无线技术&#xff0c;有一个新的漏洞需要解决。 中间的攻击者可以使用新的 BLUFFS 攻击轻松窥探您的通信。 法国研究中心 EURECOM 的研究员 Daniele Antonioli 演示了六种新颖的攻击&#xff0c;这些攻击被定义为 BLUFFS&#xff08;蓝牙转发和…