【黑马点评】使用RabbitMQ实现消息队列——3.使用Jmeter压力测试,导入批量token,测试异步秒杀下单

3 批量获取用户token,使用jmeter压力测试

  • [3 批量获取用户token,使用jmeter压力测试](#3 批量获取用户token,使用jmeter压力测试)
    • [3.1 需求](#3.1 需求)
    • [3.2 实现](#3.2 实现)
      • [3.2.1 环境配置](#3.2.1 环境配置)
      • [3.2.2 修改登录接口UserController和实现类](#3.2.2 修改登录接口UserController和实现类)
      • [3.2.3 测试类](#3.2.3 测试类)
    • [3.3 使用jmeter进行测试](#3.3 使用jmeter进行测试)
    • [3.4 测试结果](#3.4 测试结果)
    • [3.5 将用户登录逻辑修改回去](#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包

xml 复制代码
       <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路径中),如果根据手机号登录,需要将验证验证码的代码注释掉(即注释掉验证逻辑),以便直接根据手机号登录而无需验证。

java 复制代码
/**
 * 登录功能
 * @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);
    }

修改实现类中的检验方法

主要注释掉校验验证码这部分的逻辑

java 复制代码
        // @TODO 先不校验验证码
/*        if(cacheCode == null || !cacheCode.equals(code)){
            //3.不一致,报错
            return Result.fail("验证码错误");
        }*/
        //注释掉以上部分

impl类中方法如下

java 复制代码
    @Override
    public 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.返回token
        return 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文件中。

编写测试类(即脚本):

测试类内容如下:

java 复制代码
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 {

    @Autowired
    private IUserService userService;

    @Test
    public void function(){
        String loginUrl = "http://localhost:8080/api/user/login"; // 替换为实际的登录URL
        String 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);

                // 处理登录响应,获取token
                if (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字符串转换为JSONObject
            JSONObject 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 将用户登录逻辑修改回去

取消验证码检测的注释。

相关推荐
千层冷面30 分钟前
RabbitMQ 发送者确认机制详解
分布式·rabbitmq·ruby
ChinaRainbowSea31 分钟前
3. RabbitMQ 的(Hello World) 和 RabbitMQ 的(Work Queues)工作队列
java·分布式·后端·rabbitmq·ruby·java-rabbitmq
hycccccch11 小时前
Canal+RabbitMQ实现MySQL数据增量同步
java·数据库·后端·rabbitmq
海姐软件测试13 小时前
Postman参数化设置如何设置?
开发语言·jmeter
陈平安Java and C17 小时前
RabbitMQ简单介绍和安装
rabbitmq
陈平安Java and C17 小时前
RabbitMQ应用2
rabbitmq
RainbowSea18 小时前
4. RabbitMQ 发布确认的配置详细说明
java·消息队列·rabbitmq
lifewange1 天前
Jmeter面试题
jmeter
一棵树长得超出它自己1 天前
jmeter if控制器在loop控制器执行结束后执行
前端·jmeter
雨会停rain1 天前
如何提高rabbitmq消费效率
分布式·rabbitmq