【黑马点评】使用RabbitMQ实现消息队列——3.使用Jmeter压力测试,导入批量token,测试异步秒杀下单
3 批量获取用户token,使用jmeter压力测试
- 3 批量获取用户token,使用jmeter压力测试
- 3.1 需求
- 3.2 实现
- 3.2.1 环境配置
- 3.2.2 修改登录接口UserController和实现类
- 3.2.3 测试类
- 3.3 使用jmeter进行测试
- 3.4 测试结果
- 3.5 将用户登录逻辑修改回去
3 批量获取用户token,使用jmeter压力测试
3.1 需求
优惠券秒杀的接口,需要模拟1000个不同登录用户下的秒杀场景,测试这个接口的性能。
分析
1.如何模拟这1000个用户?
我们可以使用for循环在数据库中批量添加这1000个用户,然后需要对这1000个用户进行登录以获取这1000个用户的token,以便在jmeter发起的请求头中携带这1000个token模拟1000个用户。
2.如何批量获取token?
编写脚本发起1000个登录请求,并将响应的token写入txt文件中。
3.2 实现
3.2.1 环境配置
1.修改pom.xml文件,导入apache包
<dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.5.6</version></dependency>
3.2.2 修改登录接口UserController和实现类
1.登录接口:修改UserController.java中的login方法为以下内容
本项目使用的是手机号和验证码登录方式,这两个参数携带在请求体(requestbody)中,而不是请求参数中(url路径中),如果根据手机号登录,需要将验证验证码的代码注释掉(即注释掉验证逻辑),以便直接根据手机号登录而无需验证。
/*** 登录功能* @param loginForm 登录参数,包含手机号、验证码;或者手机号、密码*/
@PostMapping("/login")
public Result login(@RequestBody LoginFormDTO loginForm, HttpSession session){String phone = loginForm.getPhone();String code = loginForm.getCode();if(phone == null){return Result.fail("手机号为空!");}// if(code == null){
// return Result.fail("验证码为空!");
// }return userService.login(loginForm, session);}
修改实现类中的检验方法
主要注释掉校验验证码这部分的逻辑
// @TODO 先不校验验证码
/* if(cacheCode == null || !cacheCode.equals(code)){//3.不一致,报错return Result.fail("验证码错误");}*///注释掉以上部分
impl类中方法如下
@Overridepublic Result login(LoginFormDTO loginForm, HttpSession session) {// 1.校验手机号String phone = loginForm.getPhone();if (RegexUtils.isPhoneInvalid(phone)) {// 2.如果不符合,返回错误信息return Result.fail("手机号格式错误!");}
// // 3.校验验证码
// Object cacheCode = session.getAttribute("code");// 3.从redis获取验证码并校验String cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + phone);String code = loginForm.getCode();// @TODO 先不校验验证码
/* if(cacheCode == null || !cacheCode.equals(code)){//3.不一致,报错return Result.fail("验证码错误");}*///注释掉以上部分//一致,根据手机号查询用户User user = query().eq("phone", phone).one();//5.判断用户是否存在if(user == null){//不存在,则创建user = createUserWithPhone(phone);}// 7.保存用户信息到 redis中// 7.1.随机生成token,作为登录令牌String token = UUID.randomUUID().toString(true);// 7.2.将User对象转为HashMap存储UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);Map<String, Object> userMap = BeanUtil.beanToMap(userDTO, new HashMap<>(), //beanToMap方法执行了对象到Map的转换CopyOptions.create().setIgnoreNullValue(true) //BeanUtil在转换过程中忽略所有null值的属性.setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString())); //对于每个字段值,它简单地调用toString()方法,将字段值转换为字符串。// 7.3.存储String tokenKey = LOGIN_USER_KEY + token;stringRedisTemplate.opsForHash().putAll(tokenKey, userMap);// 7.4.设置token有效期stringRedisTemplate.expire(tokenKey, LOGIN_USER_TTL, TimeUnit.MINUTES);// 8.返回tokenreturn Result.ok(token);}
3.2.3 测试类
2.登录流程:
用户登录成功后,服务端会将token作为data数据返回给客户端,并将token存储到Redis中。之后客户端将token添加到请求头Authorization中,每次发起请求都需要携带该请求头,后端拦截器会根据请求头进行用户身份验证。
3.响应格式:
用户登录成功服务端响应格式
{“success”:true,“data”:“301130fd-7e25-4c93-8a79-9eb7d54c6fed”}//响应体
批量获取token脚本(Java)
思路:使用userService从数据库中获取用户集合(这里使用的是Mybatis-plus),遍历集合中的每个用户,将用户的手机号添加到请求体中,使用Java的Http客户端发起请求。之后从json响应体中获取token并写入txt文件中。
编写测试类(即脚本):
测试类内容如下:
package com.hmdp;import com.hmdp.entity.User;
import com.hmdp.service.IUserService;import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClients;import org.apache.http.util.EntityUtils;
import org.json.JSONObject;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import java.io.BufferedWriter;
import java.io.FileWriter;import java.util.List;
@SpringBootTest
public class UserLoginBatch {@Autowiredprivate IUserService userService;@Testpublic void function(){String loginUrl = "http://localhost:8080/api/user/login"; // 替换为实际的登录URLString tokenFilePath = "tokens.txt"; // 存储Token的文件路径try {HttpClient httpClient = HttpClients.createDefault();BufferedWriter writer = new BufferedWriter(new FileWriter(tokenFilePath));// 从数据库中获取用户手机号List<User> users = userService.list();for(User user : users) {String phoneNumber = user.getPhone();// 构建登录请求HttpPost httpPost = new HttpPost(loginUrl);//(1.如果作为请求参数传递)//List<NameValuePair> params = new ArrayList<>();//params.add(new BasicNameValuePair("phone", phoneNumber));// 如果登录需要提供密码,也可以添加密码参数// params.add(new BasicNameValuePair("password", "user_password"));//httpPost.setEntity(new UrlEncodedFormEntity(params));// (2.如果作为请求体传递)构建请求体JSON对象JSONObject jsonRequest = new JSONObject();jsonRequest.put("phone", phoneNumber);StringEntity requestEntity = new StringEntity(jsonRequest.toString(),ContentType.APPLICATION_JSON);httpPost.setEntity(requestEntity);// 发送登录请求HttpResponse response = httpClient.execute(httpPost);// 处理登录响应,获取tokenif (response.getStatusLine().getStatusCode() == 200) {HttpEntity entity = response.getEntity();String responseString = EntityUtils.toString(entity);System.out.println(responseString);// 解析响应,获取token,这里假设响应是JSON格式的// 根据实际情况使用合适的JSON库进行解析String token = parseTokenFromJson(responseString);System.out.println("手机号 " + phoneNumber + " 登录成功,Token: " + token);// 将token写入txt文件writer.write(token);writer.newLine();} else {System.out.println("手机号 " + phoneNumber + " 登录失败");}}writer.close();} catch (Exception e) {e.printStackTrace();}}// 解析JSON响应获取token的方法,这里只是示例,具体实现需要根据实际响应格式进行解析private static String parseTokenFromJson(String json) {try {// 将JSON字符串转换为JSONObjectJSONObject jsonObject = new JSONObject(json);// 从JSONObject中获取名为"token"的字段的值String token = jsonObject.getString("data");return token;} catch (Exception e) {e.printStackTrace();return null; // 解析失败,返回null或者抛出异常,具体根据实际需求处理}}
}
先运行HmDianPingApplication,之后运行测试类可得到存储了1000个用户token的txt文件
3.3 使用jmeter进行测试
1.设置线程数为1000
2.导入tokens文件
3.设置HTTP信息头管理器
4.设置HTTP请求
注意,Path为/api/voucher-order/seckill/13
13记得修改为自己的秒杀商品id
5.运行并得到结果
运行前,13的商品库存为100
3.4 测试结果
Redis和数据库中库存减为0
订单数量为100
3.5 将用户登录逻辑修改回去
取消验证码检测的注释。
相关文章:

【黑马点评】使用RabbitMQ实现消息队列——3.使用Jmeter压力测试,导入批量token,测试异步秒杀下单
3 批量获取用户token,使用jmeter压力测试 3 批量获取用户token,使用jmeter压力测试3.1 需求3.2 实现3.2.1 环境配置3.2.2 修改登录接口UserController和实现类3.2.3 测试类 3.3 使用jmeter进行测试3.4 测试结果3.5 将用户登录逻辑修改回去 3 批量获取用户…...

第 21 章 一条记录的多幅面孔——事务的隔离级别与 MVCC
21.1 事前准备 CREATE TABLE hero ( number INT, NAME VARCHAR ( 100 ), country VARCHAR ( 100 ), PRIMARY KEY ( number ) ) ENGINE INNODB CHARSET utf8;INSERT INTO hero VALUES ( 1, 刘备, 蜀 );21.2 事务隔离级别 在保证事务隔离性的前提下,使用不同的隔…...

javaScript操作dom的事件(3个案例+代码+效果图)
目录 1.焦点事件 案例:登录表单的验证 1.代码 2.效果 3.解释 2.鼠标事件 案例:单击鼠标使小球跳跃 1.代码 2.效果 3.解释 3.键盘事件 案例:使用左右键控制小球左右移动 1.代码 2.效果 编辑 3.解释 1.焦点事件 focus 当获得焦点时出发(不会冒泡)blur 当失去焦点时出发(不会…...

国庆期间的问题,如何在老家访问杭州办公室的网络呢
背景:国庆期间的问题,如何在老家访问杭州办公室的网络呢 实现方案:异地组网 实现语言:Java 环境:三个网络,一台拥有公网IP的服务器、一台杭州本地机房内服务器、你老家所在网络中的一台电脑(…...

动态规划算法——三步问题
1.题目解析 2.算法原理 本题可以近似看做泰波那契数列,即小孩到第一个台阶需要一步,到第二个台阶则是到第一个台阶的步数加上第一阶到第二阶的步数,同理第三阶就是第二阶的步数加上第二阶到第三阶的步数,由于小孩只能走三步&#…...

【鸿蒙学习】深入解析鸿蒙应用与元服务:含义、区别、应用场景及创建方法
文章目录 鸿蒙应用(HarmonyOS App)含义用于干什么优缺点 元服务(Atomic Service)含义用于干什么优缺点 鸿蒙应用与元服务的区别创建方法鸿蒙应用的创建元服务的创建 总结 随着科技的不断进步,操作系统也在不断迭代更新…...

React学习01 jsx、组件与组件的三大属性
文章目录 jsx的介绍与语法1.真实DOM和虚拟DOM2.jsx语法 模块与模块化,组件与组件化模块与模块化组件与组件化 React组件React事件绑定函数式组件类式组件组件属性state组件属性props组件属性ref 尚硅谷react教程官方文档学习记录笔记01 jsx的介绍与语法 1.真实DOM和…...

项目——超级马里奥——Day(3)
一、游戏开发思路: 1.Frame--->BackGround--->Obstacle---->BufferedImage,人物等 2.BackGround的构造函数: 只要记住窗口里边的每一个场景,只要游戏一开始就已经出现在屏幕里边的,都是在构造函数里边 3.绘…...

测试-BUG篇
文章目录 软件测试的生命周期BUGbug的概念描述bug的要素bug级别bug的生命周期 与开发产生争执怎么办(高频考题) 软件测试的生命周期 软件测试贯穿于软件的整个生命周期 BUG bug的概念 是指计算机程序中存在的一个错误(error)、缺陷(flaw)、疏忽(mista…...

vue2中 vue-count-to组件让数字从某个数字动态的显示到某个数字(后附vue3的用法)
1、首先安装 npm install vue-count-to2、使用 2.1、先导入组件 import countTo from ‘vue-count-to’2.2、注册组件 components: { countTo },2.3、使用组件 <countTo> <template><div class"home"><countTo class"count-to&qu…...

AI模型部署初认识
AI部署这个词儿大家肯定不陌生,可能有些小伙伴还不是很清楚这个是干嘛的,但总归是耳熟能详了。 近些年来,在深度学习算法已经足够卷卷卷之后,深度学习的另一个偏向于工程的方向–部署工业落地,才开始被谈论的多了起来…...

在线生成论文的网站有哪些?分享5款AI一键原创论文免费网站
一、千笔-AIPasspaper 千笔-AIPasspaper是一款备受推荐的AI写作助手,它集成了多种功能,包括论文大纲生成、内容填充、文献引用和查重修改等。这款工具基于最新的自然语言处理技术,能够帮助用户快速生成高质量的论文内容。 AI论文࿰…...

考研论坛平台|考研论坛小程序系统|基于java和微信小程序的考研论坛平台小程序设计与实现(源码+数据库+文档)
考研论坛平台小程序 目录 基于java和微信小程序的考研论坛平台小程序设计与实现 一、前言 二、系统功能设计 三、系统实现 四、数据库设计 1、实体ER图 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取: 博主介绍:✌️大厂…...

Pandas 时间序列处理
Pandas 时间序列处理 说明: 请回答以下问题,以展示您对 pandas 中时间序列处理的全面理解。请在适用时提供代码示例。 问题 1 如何将日期字符串列表 [2023-01-01, 2023-01-02, 2023-01-03] 转换为 pandas 的 DatetimeIndex? 问题 2 给定一…...

PCL 1.8.1 + VTK 1.8.0 + QT5.14.2+ VS2017 环境搭建
先看看效果: PCL 1.8.1下载安装: Tags PointCloudLibrary/pcl GitHub 安装完成后: 如果VTK想重新编译的,可以看我的这篇博客:...

微信小程序和抖音小程序的分享和广告接入代码
开发完成小程序或者小游戏之后,我们为什么要接入分享和广告视频功能,主要原因有以下几个方面。 微信小程序和抖音小程序接入分享和广告功能主要基于以下几个原因: 用户获取与增长:分享功能可以帮助用户将小程序内容传播给更多人&…...

中断系统的原理
一、介绍 中断是为使单片机具有对外部或内部随机发生的事件实时处理而设置的。中断是指CPU在正常运行程序时,由于内部或外部事件的发生,导致CPU中断当前运行的程序,转而去执行其他程序的过程。 中断可以是硬件产生的,也可以是…...

安装Rust
Rust 是一种系统级编程语言,旨在提供高性能和内存安全,同时避免常见的编程错误。 由 Mozilla Research 推出,Rust 自推出以来因其独特的设计理念和强大的功能而在开发者社区中迅速获得了广泛的关注和采用。 curl --proto ‘https’ --tlsv1.2…...

vite学习教程05、vite+vue2构建本地 SVG 图标
文章目录 前言一、构建本地SVG图标详细步骤1、安装开发依赖2、配置vite2.1、配置vite.config.js2.2、封装vite引入插件脚本 解决报错:can not find package fast-glob imported 二、实际应用应用1:未封装,直接vue应用应用2:封装vu…...

机器学习——自监督学习与无监督学习
# 机器学习中的自监督学习与无监督学习 在机器学习的世界中,监督学习、无监督学习和自监督学习都是重要的学习方法。本文将聚焦于自监督学习与无监督学习,探讨它们的原理、应用场景以及技术细节,并通过大量代码示例来揭示这些方法的内在工作…...

2003经典绝版100%仿盛大服务端火炬引擎原版
;中国游戏制作 本版本为中国游戏制作小组推出的第一个版本,有什么不完美的地方还请大家多多指教!!! 与盛大逼真!!! 本版本M2Server采用“梦幻风”以及“冰眼”M2Server修改器修改的 2004/1/…...

银河麒麟服务器:更新软件源
银河麒麟服务器:更新软件源 1、使用场景2、操作步骤3、注意事项 💐The Begin💐点点关注,收藏不迷路💐 1、使用场景 当需要安装最新软件或修改软件源配置后,需更新软件源以获取最新软件包信息。 2、操作步…...

字节跳动收购Oladance耳机:强化音频技术,加速VR/AR生态布局
字节跳动收购Oladance耳机:加码VR/AR领域布局 近日,字节跳动宣布已完成对开放式耳机品牌Oladance的收购,实现了对该品牌的100%控股。这一收购标志着字节跳动在AI硬件领域的进一步扩展和深化,特别是对其VR/AR领域布局的重要加码。 …...

Android SystemUI组件(11)SystemUIVisibility解读
该系列文章总纲链接:专题分纲目录 Android SystemUI组件 本章关键点总结 & 说明: 说明:本章节持续迭代之前章节思维导图,主要关注左侧最上方SystemUiVisibility解读部分即可。 本章节主要讲解SystemUiVisibility的概念及其相…...

JSON 全知全解:深入探索 JSON 的奥秘
目录 一、JSON 基础认知(一)JSON 的定义与历史(二)JSON 的语法规则(三)JSON 与 JS 对象的关系 二、JSON 在不同语言中的用法(一)JavaScript 中的 JSON 操作(二࿰…...

CSS | 响应式布局之媒体查询(media-query)详解
media type(媒体类型)是CSS 2中的一个非常有用的属性,通过media type我们可以对不同的设备指定特定的样式,从而实现更丰富的界面。media query(媒体查询)是对media type的一种增强,是CSS 3的重要内容之一。随着移动互联网的发展,m…...

并查集的模拟实现
简化版本 class UnionFindSet { public:UnionFindSet(size_t n):_ufs(n, -1){}void Union(int x1, int x2){int root1 FindRoot(x1);int root2 FindRoot(x2);// 如果本身就在一个集合就没必要合并了if (root1 root2)return;// 控制数据量小的往大的集合合并if (abs(_ufs[roo…...

如何高效删除 MySQL 日志表中的历史数据?实战指南
在处理高并发的物联网平台或者其他日志密集型应用时,数据库中的日志表往往会迅速增长,数据量庞大到数百GB甚至更高,严重影响数据库性能。如何有效管理这些庞大的日志数据,特别是在不影响在线业务的情况下,成为了一项技…...

请散户股民看过来,密切关注两件大事
明天股市要开市,不仅散户股民期盼节后股市大涨,上面也同样想在节后来上一个“开门红”。 为此,上面没休假,关起门来办了两件大事,这两天发布消息已提前预热了。 两件大事如下: 一是,上交所10…...

设计模式之外观模式(Facade)
一、外观模式介绍 外观模式( Facade Pattern),也叫门面模式,是一个 “结构型” 设计模式。 外观模式的原始定义是:为子系统中的一组接口提供统一的接口。它定义了一个更高级别 的接口,使子系统更易于使用。 外观模式,是…...