优雅的并发测试

来张图:
在这里插入图片描述
图上:预期中的多线程
图下:实际上遇到的多线程

做为测试人员,遇到潜在的并发问题时,需要通过技术或工具手段去模拟并发场景,避免产生上图中的第二种情况。

电商APP常见的秒抢活动,1000张券,1秒瞬间被抢完,根据这个例子,讲下一般的设计方式和如何测试。秒抢的关键词:固定数量、同一时间请求流量大、一人一张,内部实现方式常见为:

  • 入口限流,因为只有1000张券,接收到人数到达1000后,后面的人直接拦截
  • 异步处理,A系统负责验证记录用户身份,人员满额后,停止接待。发消息给券系统,进行发券
  • 高并发,支持多线程抢券,保证count=1000的线程安全

秒抢的处理方式,类似于mq的使用场景,限流、削峰、解耦。

并发案例的测试,相对来说不复杂,在Jmeter里,通过设置线程数、csv存取用户数据、添加并发集合点,执行完毕后,验证结果(券被消耗的数量、用户实际领到的数量,过程持续的时间)。

用Java在本地模拟高并发的调用,这里贴下code:

public class ThreadCollect implements Runnable {
    private HttpClient httpClient;
    private CyclicBarrier cyclicBarrier;

    public ThreadCollect(CyclicBarrier cyclicBarrier) {
        this.cyclicBarrier = cyclicBarrier;     /**  循环屏障,集合点,设置用户数 */
    }

    public void addThread(HttpClient httpClient){
        this.httpClient = httpClient;
    }

    @Override
    public void run() {
        try {
            Message.outPut(String.format("线程:%s 正在集结", Thread.currentThread().getName()));
            cyclicBarrier.await();  /**  线程数全部到达后,才会执行后面的代码,否则继续在此排队 */

            httpClient.send();  /** 所有的API请求,同时运行*/
            Message.outPut(String.format("线程:%s started", Thread.currentThread().getName()));
            Message.outPut(httpClient.getResponse());

        } catch (Exception var) {
            System.out.println(var.getMessage());
        }
    }
}

循环添加用户的请求,在达到并发数之前,做等待,本地httpClient请求里包装了每个用户请求的具体参数。

public class ThreadRunner {
    public void execute(List<HttpClient> httpClients, int count) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(count);
        ExecutorService executorService = Executors.newFixedThreadPool(count);

        //  api请求去屏障处集结
        for (int i = 0; i < count; i++) {
            ThreadCollect collect = new ThreadCollect(cyclicBarrier);
            collect.addThread(httpClients.get(i));
            executorService.execute(collect);
        }
        executorService.shutdown(); // 关闭线程池
    }
}

并发测试的入参为,httpClients{api具体参数的list对象},count{并发数量}

mock代码:

    public static void main(String[] args) {
        List<HttpClient> httpClients = new ArrayList<>();
        ThreadRunner runner = new ThreadRunner();
        for (int i = 0; i < 10; i++) {
            HttpClient httpClient = new HttpClient()
                    .addEndpoint("https://********/bg/v1/information/user")
                    .addMethod(HttpMethod.POST)
                    .addHeader("Content-Type", "application/json")
                    .addHeader("Authorization", "bearer 87ea05cdf70146b8ab8c361c39832452d99b72891c194a9086e030bf3678c2c1")
                    .addBodyParam("email", StringUtil.getEmail(9, 18))
                    .addBodyParam("callingPrefix", "+86")
                    .addBodyParam("phoneNumber", StringUtil.getPhone())
                    .bodyToJson()
                    .build();
            httpClients.add(httpClient);
        }

        runner.execute(httpClients, 3);
    }

输出:

线程:pool-1-thread-3 正在集结
线程:pool-1-thread-1 正在集结
线程:pool-1-thread-5 正在集结
线程:pool-1-thread-9 正在集结
线程:pool-1-thread-6 正在集结
线程:pool-1-thread-4 正在集结
线程:pool-1-thread-8 正在集结
线程:pool-1-thread-10 正在集结
线程:pool-1-thread-2 正在集结
线程:pool-1-thread-7 正在集结
线程:pool-1-thread-8 started
- - - - - - - - - -http request details- - - - - - - - - -
| Endpoint: https://**********/bg/v1/information/user
| Method: POST
| Headers:
|   - { Authorization : bearer 87ea05cdf70146b8ab8c361c39832452d99b72891c194a9086e030bf3678c2c1 }
|   - { Content-Type : application/json }
| Body: (Json || Form || Form-Data)
{"phoneNumber":"15604213233","callingPrefix":"+86","email":"vau007aqo57fpb@hotmail.com"}
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - http response details - - - - - - - - -
| Response Code: 201
| Response Time: 2.54s
| Cookie: {Set-Cookie=}
| Response:
|   - {"clientId":"8522009271012242069","code":"adfa009be9bb46029642179c62ef9a31"}
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
线程:pool-1-thread-4 started
............................................
.............................
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值