redis分布式锁--Jmeter测试

Jmeter测试redis秒杀

安装jmeter

由于截图较多,见底部

秒杀测试

demo01

# 测试条件
	设置票数count为100
	这里直接单机运行,使用jmeter测试      
	jmeter访问四次  每次发送400个请求
---
# 测试结果
	一共卖出16件商品,并且卖出的商品都多次重复卖出
	此时同一商品多次卖出 qps无参考价值
    @RequestMapping("/")
    public String hello(){
        int count = Integer.parseInt(redisTemplate.opsForValue().get("count"));
        if(count>0){
            int cur=count-1;
            redisTemplate.opsForValue().set("count",String.valueOf(cur));
            System.out.println( "购买成功"+cur);
        }else{
            System.out.println("购买失败");
        }
        return "";
    }

demo02

给方法加上synchronized关键字,商品全部卖出

# 测试条件不变
---
# 测试结果
	商品全部卖出
	qps为466

但是上述代码只能在单机运行,无法保证分布式的秒杀合理性

安装nginx
接下来我们使用nginx轮训两个相同项目(安装步骤见下方)

配置nginx

	# 新增配置
	upstream testMinxu{
       #设置分权,权重越高优先访问 
        server 192.168.50.1:8080 weight=1;
        server 192.168.50.1:8081 weight=1;
    }
    server {
    # 配置服务器(虚拟机)的ip和端口号
        listen       8080;
        server_name  192.168.50.2;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
            root   html;
            index  index.html index.htm;
            # 如果访问到本机   则定向到upstream testMinxu中
            proxy_pass http://testMinxu;
        }

nginx操作说明
https://www.cnblogs.com/wangcp-2014/p/9922845.html

低配版分布式秒杀(存在问题)

使用nginx之后 再次测试 我们发现在单机可以保证秒杀的synchronized 在分布式的情况下并不能保证分布式的互斥性

如下图可知, 商品98和商品97分别被卖了两次

测试截图:
在这里插入图片描述
在这里插入图片描述

使用setnx实现分布式锁
说明:本代码基本实现分布式锁,但是当lockKey超时的话 有可能出现锁完全失效的情况
比如:当a请求拿到锁之后超时了,这时b加锁进来了,然后b还没执行完毕,a请求就将b加的锁释放掉,故后续的又可以进来 依次类推(解决方案是给每个请求设置不同的lockKey对应的唯一value,可以使用uuid即可)这样至少不会出现锁完全失效的情况,只会在每个超时时多卖出一件商品罢了

# 测试数据
	10张票   400*4次请求
# 测试结果
	qps 657
@RequestMapping("/")
    public String hello(){
        String lockKey="mx_lock";
        Boolean isLock = redisTemplate.opsForValue().setIfAbsent(lockKey, "true",10, TimeUnit.SECONDS);
        if(!isLock){
            //System.out.println("没拿到锁");
            return "";
        }
        try {
            int count = Integer.parseInt(redisTemplate.opsForValue().get("count"));
            if(count>0){
                int cur=count-1;
                redisTemplate.opsForValue().set("count",String.valueOf(cur));
                System.out.println( "购买成功"+cur);
            }else {
                //System.out.println("购买失败");
            }
        }catch (Exception e){
            return "error";
        }finally {
            redisTemplate.delete(lockKey);
        }

        return "";
    }

优化版秒杀(存在问题)

优化点:主要就是设置了锁的value,单个线程加的锁,只能由自己释放或者超时释放,防止超时后,自己会将下一个获得锁的线程的锁给释放掉

    @RequestMapping("/")
    public String hello(){
        String lockKey="mx_lock";
        String uuid=UUID.randomUUID().toString();
        Boolean isLock = redisTemplate.opsForValue().setIfAbsent(lockKey, uuid,5, TimeUnit.SECONDS);
        if(!isLock){
            //System.out.println("没拿到锁");
            return "";
        }
        try {
            int count = Integer.parseInt(redisTemplate.opsForValue().get("count"));
            if(count>0){
                int cur=count-1;
                redisTemplate.opsForValue().set("count",String.valueOf(cur));
                System.out.println( "购买成功"+cur);
            }else {
                //System.out.println("购买失败");
            }
        }catch (Exception e){
            return "error";
        }finally {
            if(uuid.equals(redisTemplate.opsForValue().get("count"))){
                redisTemplate.delete(lockKey);
            }
        }
        return "";
    }

在这里插入图片描述
在这里插入图片描述

秒杀最终版

上述代码存在超时问题,可能出现锁失效的情况,但是已经能满足普通的需求了。那么如何解决上述的情况呢?

# 方案一
	我们可以在setnx并设置超时时间为10s的时候,开启一个线程,
	每5s检查主线程是否还活着,如果或者则重置锁的时间为10s,
	这样就给锁续命了,而redisson框架已经实现了这个功能,
	我们可以集成redisson框架(底层也是使用lua脚本实现的)
# 方案二
	使用lua脚本,我们既可以使加锁和释放锁为原子操作

这里使用方案一

  1. 导入依赖
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.10.6</version>
</dependency>
<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
</dependency>
	@Bean
    public Redisson getRedisson() {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://192.168.50.2:6379").setDatabase(0);
        return (Redisson) Redisson.create(config);
    }

    @Autowired
    Redisson redisson;

    @RequestMapping("/1")
    public String hello1() {
        String lockKey = "mx_lock";
        String uuid = UUID.randomUUID().toString();
        RLock lock = redisson.getLock(lockKey);

        try {
            lock.lock();
            int count = Integer.parseInt(redisTemplate.opsForValue().get("count"));
            if (count > 0) {
                int cur = count - 1;
                redisTemplate.opsForValue().set("count", String.valueOf(cur));
                System.out.println("购买成功" + cur);
            } else {
                //System.out.println("购买失败");
            }
        }catch (Exception e){

        }
        finally {
            lock.unlock();
        }
        return "";
    }
# 测试结果
	qps为200
# 测试输出
	购买成功9
	购买成功8
	购买成功5
	购买成功3
	购买成功1
	--------
	购买成功7
	购买成功6
	购买成功4
	购买成功2
	购买成功0

安装nginx

https://blog.csdn.net/K_Lily/article/details/106555140

安装jmeter

官网: https://jmeter.apache.org/

二进制的可以直接执行,源码需要自己编译
windows下载zip mac和linux下载tgz

  1. 解压后点击jmeter脚本运行,配置基本配置
    在这里插入图片描述
    添加http请求
    在这里插入图片描述
    添加返回结果报告
    在这里插入图片描述
    可以反击结果报告,来清空报告的历史
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值