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

自动化完成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(),因为生成的验证码不可能直接返回给浏览器吧,所以在测试完成后,一定要记得改回来
    3. 一定要保障数据库中存在1000个手机号
  • 13
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

知识汲取者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值