写点什么

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

用户头像
FunTester
关注
发布于: 4 小时前

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


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

业务需求

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

接口参数

  • 收藏


    /**     *收藏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    }
复制代码


  • 取消收藏


    /**     * 取消收藏     * @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


package com.okayqa.composer.performance.master1_0
import com.fun.base.constaint.ThreadLimitTimesCountimport com.fun.frame.execute.Concurrentimport com.fun.frame.httpclient.ClientManageimport com.fun.utils.ArgsUtilimport com.okayqa.common.Commonimport com.okayqa.composer.base.OkayBaseimport 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)方法中增加复制操作,如下:


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


然后增加last相关操作:


    @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对象创建和初始化的时候用的是单线程,代码如下:


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


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


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

接口测试视频

Git 仓库

  • Gitee地址 https://gitee.com/fanapi/tester

  • GitHub地址 https://github.com/JunManYuanLong/FunTester



FunTester,非著名测试开发,文章记录学习和感悟,欢迎关注,交流成长。

FunTester 热文精选

发布于: 4 小时前阅读数: 3
用户头像

FunTester

关注

公众号:FunTester,650+原创,欢迎关注 2020.10.20 加入

Have Fun,Tester! 公众号FunTester,坚持原创文章的测试人。 FunTester测试框架作者,DCS_FunTester分布式性能测试框架作者。

评论

发布
暂无评论
链路压测中如何记录每一个耗时的请求