前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >链路压测中如何记录每一个耗时的请求

链路压测中如何记录每一个耗时的请求

作者头像
FunTester
发布2020-12-24 14:54:26
7960
发布2020-12-24 14:54:26
举报
文章被收录于专栏:FunTesterFunTester

前文回顾:性能测试中记录每一个耗时请求,做完了单接口耗时请求的记录功能,近期又迎来了一批多接口链路压测的需求。刚好趁着这个机会,多实现一些不同场景的链路压测需求,锻炼一波,也能提高自己写的「FunTester」测试框架的兼容性,可谓一石多鸟,何乐而不为。

非技术内容的分享简略一些。

业务需求

老师在首页看到资源列表后,对相应的列表进行收藏和取消收藏操作。

接口参数

  • 收藏
代码语言:javascript
复制
    /**
     *收藏OK智课
     * @param minicourse_id
     * @param ktype 0-机构,1-老师
     * @return
     */
    public JSONObject collect(int minicourse_id = 43089, int ktype = 0, int grade_id = 12) {
        String url = OKClassApi.COLLECT
        def params = getParams()
        params.put("org_id", 80);
        params.put("org_type", 1);
        params.put("minicourse_id", minicourse_id);
        params.put("kid_route", [82]);
        params.put("ktype", ktype);
        params.put("grade_id", grade_id);
        params.put("link_source", 1);//0-教师空间,1-教师机
        def response = getPostResponse(url, params)
        output(response)
        response
    }
  • 取消收藏
代码语言:javascript
复制
    /**
     * 取消收藏
     * @param minicourse_id
     * @param ktype
     * @param grade_id
     * @return
     */
    public JSONObject unCollect(int minicourse_id = 43089, int ktype = 0) {
        String url = OKClassApi.UNCOLLECT
        def params = getParams()
        params.put("minicourse_id", minicourse_id);
        params.put("kid_route", [82]);
        params.put("ktype", ktype);
        def response = getPostResponse(url, params)
        output(response)
        response
    }

测试方案

通过创建不用的用户对象,一个线程绑定一个用户对象,使用该对象进行「收藏」「取消收藏」操作。把一次循环当做一个request进行数据的统计,计算QPSRT等数据,生成测试结果图像。此处参考:性能测试中图形化输出测试数据

测试脚本

测试脚本使用Groovy,方便在服务器上执行,基本跟Java没有差别。

我用一个AtomicInteger对象来控制每一个线程创建的用户对象不同,具体方法是OkayBase okayBase = getBase(u.getAndIncrement())。通过获取每个对象最后一次发出请求的HttpRequestBase请求,获取请求的Mark对象值requestid,拼接到线程标记对象threadmark中,这样就可以获取到耗时的请求了。具体的数据格式如下:1218_Fdev160809808115759_Fdev160809808182457

代码语言:javascript
复制
package com.okayqa.composer.performance.master1_0

import com.fun.base.constaint.ThreadLimitTimesCount
import com.fun.frame.execute.Concurrent
import com.fun.frame.httpclient.ClientManage
import com.fun.utils.ArgsUtil
import com.okayqa.common.Common
import com.okayqa.composer.base.OkayBase
import com.okayqa.composer.function.OKClass

import java.util.concurrent.atomic.AtomicInteger

class BothCollect extends OkayBase {

    static AtomicInteger u = new AtomicInteger(0)

    static int times

    static int thread

    public static void main(String[] args) {
        ClientManage.init(5, 1, 0, "", 0)
        def util = new ArgsUtil(args)
        thread = util.getIntOrdefault(0, 200)
        times = util.getIntOrdefault(1, 100)
        def funs = []
        thread.times {
            funs << new Fun()
        }
        new Concurrent(funs, "收藏和取消收藏").start()
        allOver()
    }

    static int getTimes() {
        return times
    }

    static class Fun extends ThreadLimitTimesCount {

        OkayBase okayBase = getBase(u.getAndIncrement())

        OKClass driver = new OKClass(okayBase)

        public Fun() {
            super(null, getTimes(), null)
        }


        @Override
        protected void doing() throws Exception {
            def collect = driver.collect()
            def last = okayBase.getLast()
            def value1 = okayBase.getLastRequestId() + CONNECTOR
            this.threadmark += value
            if (collect.getJSONObject("meta").getIntValue("ecode") != 0) fail(value + "请求出错!")
            def collect1 = driver.unCollect()
            def value1 = okayBase.getLastRequestId()
            this.threadmark += value1
            if (collect1.getJSONObject("meta").getIntValue("ecode") != 0) fail(value1 + "请求出错!")
        }
    }

}

记录方案实现

首先对Base类进行改造,增加private HttpRequestBase last;属性,然后在public JSONObject getResponse(HttpRequestBase httpRequestBase)方法中增加复制操作,如下:

代码语言:javascript
复制
    @Override
    public JSONObject getResponse(HttpRequestBase httpRequestBase) {
        setHeaders(httpRequestBase);
        recordRequest(httpRequestBase);
        JSONObject response = FanLibrary.getHttpResponse(httpRequestBase);
        handleResponseHeader(response);
        return response;
    }

然后增加last相关操作:

代码语言:javascript
复制
    @Override
    public void recordRequest(HttpRequestBase base) {
        this.last = base;
    }
    
    /**
     * 获取最后一个请求
     *
     * @return
     */
    @Override
    public HttpRequestBase getRequest() {
        return last == null ? FanLibrary.getLastRequest() : last;
    }

    /**
     * 获取最后一个请求的requestid
     *
     * @return
     */
    public String getLastRequestId() {
        HttpRequestBase httpRequestBase getLast();
        return httpRequestBase.getFirstHeader(Common.REQUEST_ID.getName()).getValue();
    }

这里用了一个非空判断,主要是为了防止空指针异常,有些Base对象初始化并不是通过接口请求实现的。在进行批量Base对象创建和初始化的时候用的是单线程,代码如下:

代码语言:javascript
复制
        def funs = []
        thread.times {
            funs << new Fun()
        }

所以不存在线程安全的问题,故而采取了这种方案。

测试框架相关使用情况可以参考之前的视频讲解:

接口测试视频
Git仓库
  • Gitee地址https://gitee.com/fanapi/tester
  • GitHub地址https://github.com/JunManYuanLong/FunTester
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-12-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 FunTester 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 业务需求
  • 接口参数
  • 测试方案
  • 测试脚本
  • 记录方案实现
    • 接口测试视频
      • Git仓库
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档