自动化完成1000个用户的登录并获取token并生成tokens.txt文件

news2024/9/25 12:20:19

自动化完成1000个用户的登录并获取token并生成tokens.txt文件

  • 写作背景

    在我学习使用redis实现秒杀功能的过程中,在编写完秒杀代码后,需要使用Jmeter实际测试1000个用户进行秒杀,由于秒杀功能需要在用户登录完成后才能实现,用户是否登录是由登录拦截器实现的,用户在登录成功后,后台会生成一个token然后发送给浏览器,每次发送秒杀请求时,登录拦截器都会判断浏览器中是否有token,没有token就说明用户未登录,不能进行秒杀,只有含有token的秒杀请求才会被执行,具体流程图如下:
    在这里插入图片描述现在存在的问题是:我们想要发送/voucher-order/seckill/id请求,就先需要实现/user/code/user/login请求,而这两个请求实现很简单,但是需要实现1000次,同时获取这1000给token用于Jmeter压力测试。这总不可能手动测试把,所以就需要通过代码实现了(详情见P69)

  • 代码实现

    核心实现思路:通过编写一个测试类,然后使用MockMvc 发送请求,最终将获取到的token写入tokens.txt文件中(如果您有更好的方法,请留言告知在下,在下不胜感激)

    package com.hmdp.shop;
    
    import cn.hutool.core.lang.Assert;
    import cn.hutool.core.thread.ThreadUtil;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.hmdp.dto.LoginFormDTO;
    import com.hmdp.dto.Result;
    import com.hmdp.entity.User;
    import com.hmdp.service.IUserService;
    import lombok.SneakyThrows;
    import lombok.extern.slf4j.Slf4j;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.http.MediaType;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    import org.springframework.test.web.servlet.MockMvc;
    import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
    import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
    
    import javax.annotation.Resource;
    import java.io.BufferedWriter;
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.OutputStreamWriter;
    import java.nio.charset.StandardCharsets;
    import java.util.List;
    import java.util.concurrent.CopyOnWriteArrayList;
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.ExecutorService;
    import java.util.stream.Collectors;
    
    /**
     * @author ghp
     * @date 2023/2/7
     * @title
     * @description
     */
    @SpringBootTest
    @RunWith(SpringJUnit4ClassRunner.class)
    @AutoConfigureMockMvc
    @Slf4j
    public class GenerateToken {
    
        @Resource
        private MockMvc mockMvc;
    
        @Resource
        private IUserService userService;
    
        @Resource
        private ObjectMapper mapper;
    
        @Test
        // 忽视异常
        @SneakyThrows
        public void login() {
            // 查询数据库得到1000个号码
            List<String> phoneList = userService.lambdaQuery()
                    .select(User::getPhone)
                    .last("limit 1000")
                    .list().stream().map(User::getPhone).collect(Collectors.toList());
            // 使用线程池,线程池总放入1000个号码,提高效率
            ExecutorService executorService = ThreadUtil.newExecutor(phoneList.size());
            // 创建List集合,存储生成的token。多线程下使用CopyOnWriteArrayList,实现读写分离,保障线程安全(ArrayList不能保障线程安全)
            List<String> tokenList = new CopyOnWriteArrayList<>();
            // 创建CountDownLatch(线程计数器)对象,用于协调线程间的同步
            CountDownLatch countDownLatch = new CountDownLatch(phoneList.size());
            // 遍历phoneList,发送请求,然后将获取的token写入tokenList中
            phoneList.forEach(phone -> {
                executorService.execute(() -> {
                    try {
                        // 发送获取验证码的请求,获取验证码
                        String codeJson = mockMvc.perform(MockMvcRequestBuilders
                                        .post("/user/code")
                                        .queryParam("phone", phone))
                                .andExpect(MockMvcResultMatchers.status().isOk())
                                .andReturn().getResponse().getContentAsString();
                        // 将返回的JSON字符串反序列化为Result对象
                        Result result = mapper.readerFor(Result.class).readValue(codeJson);
                        Assert.isTrue(result.getSuccess(), String.format("获取“%s”手机号的验证码失败", phone));
                        String code = result.getData().toString();
    
                        // 创建一个登录表单
                        // 使用建造者模式构建 登录信息对象,我这里就没有使用了,我是直接使用new(效率较低不推荐使用)
    //                    LoginFormDTO formDTO = LoginFormDTO.builder().code(code).phone(phone).build();
                        LoginFormDTO formDTO = new LoginFormDTO();
                        formDTO.setCode(code);
                        formDTO.setPhone(phone);
                        // 将LoginFormDTO对象序列化为JSON
                        String json = mapper.writeValueAsString(formDTO);
    
                        // 发送登录请求,获取token
                        // 发送登录请求,获取返回信息(JSON字符串,其中包含token)
                        String tokenJson = mockMvc.perform(MockMvcRequestBuilders
                                        .post("/user/login").content(json).contentType(MediaType.APPLICATION_JSON))
                                .andExpect(MockMvcResultMatchers.status().isOk())
                                .andReturn().getResponse().getContentAsString();
                        // 将JSON字符串反序列化为Result对象
                        result = mapper.readerFor(Result.class).readValue(tokenJson);
                        Assert.isTrue(result.getSuccess(), String.format("获取“%s”手机号的token失败,json为“%s”", phone, json));
                        String token = result.getData().toString();
                        tokenList.add(token);
                        // 线程计数器减一
                        countDownLatch.countDown();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                });
            });
            // 线程计数器为0时,表示所有线程执行完毕,此时唤醒主线程
            countDownLatch.await();
            // 关闭线程池
            executorService.shutdown();
            Assert.isTrue(tokenList.size() == phoneList.size());
            // 所有线程都获取了token,此时将所有的token写入tokens.txt文件中
            writeToTxt(tokenList, "\\tokens.txt");
            log.info("程序执行完毕!");
        }
    
        /**
         * 生成tokens.txt文件
         * @param list
         * @param suffixPath
         * @throws Exception
         */
        private static void writeToTxt(List<String> list, String suffixPath) throws Exception {
            // 1. 创建文件
            File file = new File(System.getProperty("user.dir") + "\\src\\main\\resources" + suffixPath);
            if (!file.exists()) {
                file.createNewFile();
            }
            // 2. 输出
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8));
            for (String content : list) {
                bw.write(content);
                bw.newLine();
            }
            bw.close();
            log.info("tokens.txt文件生成完毕!");
        }
    }
    

    测试结果
    在这里插入图片描述
    在这里插入图片描述
    注意点:

    1. 需要使用前后端协议,将后端返回数据统一封装到Result类中
    2. 在获取验证码时,一定要记得将验证码封装到Result中,即:Result.ok(code),但在实际业务中是Result.ok(),因为生成的验证码不可能直接返回给浏览器吧,所以在测试完成后,一定要记得改回来

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

DFS深度优先搜索—Java版

递归三要素 递归的定义 递归的拆解 递归的出口 什么时候使用DFS&#xff1f; 深度回溯问题&#xff08;DFS与回溯区别不大&#xff09; 二叉树问题 组合、排列问题 找方案问题&#xff08;解空间是一棵树或者图&#xff0c;需要自行构造图/树&#xff09; 图的搜索问题…

Smokeping的主从模式部署

Smokeping 支持 Standalone&#xff08;单机&#xff09;模式和 Master/Slave&#xff08;主从&#xff09;模式。 之前老苏折腾过单机模式&#xff0c;这次应网友 Roxmie 的要求&#xff0c;研究了一下主从模式的部署 文章传送门&#xff1a; 网络性能监控工具Smokeping 因为…

Go基础-环境安装

文章目录1 Go?Golang?2 下载Go3 windows安装4 测试是否成功1 Go?Golang? Go也称为Golang&#xff0c;是Google开发的一个开源的编译型的静态语言。 Golang的主要关注点是高可用、高并发和高扩展性&#xff0c;Go语言定位是系统级编程语言&#xff0c;对web程序具有很好的支…

SAP数据导入工具(LSMW) 超级详细教程(批量导入内部订单)

目录 第一步&#xff1a;记录批导步骤编辑数据源对应字段 第二步&#xff1a;维护数据源 第三步&#xff1a;维护数据源对应字段&#xff08;重要&#xff09; 第四步&#xff1a;维护数据源关系。 第五步&#xff1a;维护数据源与导入字段的对应关系。 第六步&#xff0…

K_A12_006 基于STM32等单片机驱动BH1750模块 串口与OLED0.96双显示

K_A12_006 基于STM32等单片机驱动BH1750模块 串口与OLED0.96双显示一、资源说明二、基本参数参数引脚说明三、驱动说明时序对应程序:四、部分代码说明1、接线引脚定义1.1、STC89C52RCBH1750模块1.2、STM32F103C8T6BH1750模块五、基础知识学习与相关资料下载六、视频效果展示与程…

《蓝桥杯每日一题》递归·AcWing 1497. 树的遍历

1.题目描述一个二叉树&#xff0c;树中每个节点的权值互不相同。现在给出它的后序遍历和中序遍历&#xff0c;请你输出它的层序遍历。输入格式第一行包含整数 N&#xff0c;表示二叉树的节点数。第二行包含 N个整数&#xff0c;表示二叉树的后序遍历。第三行包含 N 个整数&…

设计模式之迭代器模式与命令模式详解和应用

目录1 迭代器模式1.1 目标1.2 内容定位1.3 迭代器模式1.4 迭代器模式的应用场景1.5 手写字定义的送代器1.6 迭代器模式在源码中的体现1.7 迭代器模式的优缺点2 命令模式2.1 定义2.2 命令模式的应用场景2.3 命令模式在业务场景中的应用2.4 命令模式在源码中的体现2.5 命令模式的…

UVa 211 The Domino Effect 多米诺效应 暴力搜索

题目链接&#xff1a;UVa 211 The Domino Effect 题目描述&#xff1a; 一张多米诺骨牌拥有两个数值&#xff0c;一共有二十八张不同的多米诺骨牌&#xff0c;这二十八张多米诺骨牌的点数如下图所示&#xff1a; 上图的BoneBoneBone代表编号&#xff0c;而PipsPipsPips代表两个…

Springboot扩展点系列之终结篇:Bean的生命周期

前言关于Springboot扩展点系列已经输出了13篇文章&#xff0c;分别梳理出了各个扩展点的功能特性、实现方式和工作原理&#xff0c;为什么要花这么多时间来梳理这些内容&#xff1f;根本原因就是这篇文章&#xff1a;Spring bean的生命周期。你了解Spring bean生命周期&#xf…

前端最全面试题整理

前端基础 一、 HTTP/HTML/浏览器 1、说一下 http 和 https https 的 SSL 加密是在传输层实现的。 (1) http 和 https 的基本概念 http: 超文本传输协议&#xff0c;是互联网上应用最为广泛的一种网络协议&#xff0c;是一个客户端和服务器端请求和应答的标准&#xff08;T…

操作SSH无密登录配置

例如小编有三台服务器需要相互访问&#xff0c;就需要配置三台&#xff0c;这三台分别是hadoop102,hadoop103 , hadoop1041.打开三个服务器&#xff0c;分别生成hadoop102&#xff0c;hadoop103 , hadoop104的公钥和私钥输入命令&#xff0c;然后一直回车&#xff0c;这时候什么…

狂神聊Redis复习笔记二

目录事务监控&#xff01; Watch &#xff08;面试常问&#xff01;&#xff09;悲观锁&#xff1a;乐观锁&#xff1a;Redis测监视测试Redis.conf详解Redis持久化RDB&#xff08;Redis DataBase&#xff09;AOF&#xff08;Append Only File&#xff09;Redis发布订阅Redis主从…

Docker资源隔离(namespace,cgroups)

一、概述 Docker容器的本质是宿主机上的一个进程。Docker通过namespace实现了资源隔离&#xff0c;通过cgroups实现了资源限制&#xff0c;通过写时复制机制&#xff08;copy-on-write&#xff09;实现了高效的文件操作。 二、Linux内核的namespace机制 namespace 机制提供一种…

jfr引起的一次jvm异常记录

业务生产启动时&#xff0c;20个节点有1-2个节点因为jvm问题出现启动失败&#xff0c;k8s自动重启后正常。在测试环境2个节点下偶现 排查思路&#xff1a; 先拿到hs_err_pid的jvm错误文件找到当前线程和内部错误信息 hs_err_pid 文件分析 当前线程&#xff1a;lettuce的线程…

使用Robot Framework实现多平台自动化测试

目录 前言 1、设计目标 2、架构设计 3、平台实现 4、平台的创新点 5、平台的实施效果 6、总结 重点&#xff1a;配套学习资料和视频教学 前言 基于Robot Framework、Jenkins、Appium、Selenium、Requests、AutoIt等开源框架和技术&#xff0c;成功打造了通用自动化测试…

各数据库数据类型的介绍和匹配

各数据库数据类型的介绍和匹配1. Oracle的数据类型2. Mysql的数据类型3. Sql server的数据类型4. 类型匹配5. Awakening1. Oracle的数据类型 数据类型介绍 VARCHAR2 :可变长度的字符串 最大长度4000 bytes 可做索引的最大长度749&#xff1b; NCHAR :根据字符集而定的固定长度字…

微服务架构的演变

文章目录1.1 系统架构的演变过程1.1.1 单体应用架构1.1.2 垂直应用架构1.1.3 分布式架构1.1.4 SOA架构1.1.5 微服务架构1.2 微服务架构设计原则1.2.1 AKF拆分原则1.2.1.1 X轴扩展&#xff08;水平复制&#xff09;1.2.1.2 Y轴扩展&#xff08;模块拆分&#xff09;1.2.1.3 Z轴扩…

【SSM】Spring对IoC的实现方式DI详讲

控制反转的一种实现方式——依赖注入一、IoC 控制反转&#xff08;Overview&#xff09;依赖注入&#xff08;DI&#xff09;- Overview利用 IoC&#xff08;控制反转&#xff09;这种思想有什么好处呢&#xff1f;二、依赖注入的方式setter 方式&#xff08;xml配置中的proper…

Java JSR规范列表

Java JSR规范列表目录概述需求&#xff1a;设计思路实现思路分析1.JSR2.JSR方法3.web service4.Webservice:5.数据处理器拓展实现参考资料和推荐阅读Survive by day and develop by night. talk for import biz , show your perfect code,full busy&#xff0c;skip hardness,m…

JavaWeb-连接数据库实现用户登录、注册、修改密码(全代码)

上一篇博客中我通过使用HttpServlet完成一个假登录&#xff0c;这篇博客我将通过JDBC连接数据库&#xff0c;使其实现简单的用户登录、注册以及更改密码一、MySQL:MySQL部分代码&#xff1a;-- ---------------------------- -- Table structure for users -- ----------------…