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脚本,我们既可以使加锁和释放锁为原子操作
这里使用方案一
- 导入依赖
<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
- 解压后点击jmeter脚本运行,配置基本配置
添加http请求
添加返回结果报告
可以反击结果报告,来清空报告的历史