写点什么

单链路性能测试实践

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

在经历过一些尝试之后,觉得在当下的项目中运用链路压测的能力,不等着其他人了。

关于链路

链路这个词其实不如路径通俗易懂,跟产品沟通这个比较有效率。具体的操作路径,产品会给一份出来,但是这都是基于UI和产品思维的文档,跟接口测试区别还是很大的,只能提供参考依据。


需要端上测试协作,有些业务细节还得端上测试同学帮忙补充一下。还需要运维同事帮忙理一下各个接口的请求量比例,这次的比例我是依据灵光一现写出来,然后大家一起调整的。


本次由于比较初级,所以这块文档就不写出来了,放一个图来表达一下这个链路做了些什么,PS:我现在很喜欢用图而不是文字,沟通效率太高了。推荐工具draw.io,感兴趣的可以参考文末的热文中两张架构图中的介绍。



  • 这次把登录剔除了,因为太慢了,对测试结果影响比较大。

场景思路

场景

场景就是老师登录,首先会请求一个知识点列表,然后通过知识点属性筛选推荐课程列表,在对课程列表中的数据进行收藏和取消收藏,在获取自己当前知识点下的课程列表(包含原创和收藏)。

思路

本次依然采取固定线程的压测模型,本人预估线程 200 左右,测试用户 600 备用,列表页保证 2 页数据。


每个线程绑定一个用户,然后用户开始循环链路执行步骤,执行一次当做一次Q。单次Q包含 9 HTTP接口请求(放弃了Socket接口,以后有需求再添加Socket接口到链路中),其中 3 次修改操作,6 次查询操作。


具体的逻辑通过内部静态类实现,然后多一个K类,用来存储每次获取的知识点属性,方便调用。由于接口请求方法都是用基础数据类型和String作为参数,所以调用时候会显得有点啰嗦。但无伤大雅,脚本写出来,本来就是用完就扔到仓库里面,改天再用再优化。

Demo 实现

package com.okayqa.composer.performance.resource1_4
import com.alibaba.fastjson.JSONimport com.alibaba.fastjson.JSONObjectimport com.funtester.base.bean.AbstractBeanimport com.funtester.base.constaint.ThreadLimitTimesCountimport com.funtester.frame.execute.Concurrentimport com.funtester.httpclient.ClientManageimport com.funtester.utils.ArgsUtilimport com.okayqa.composer.base.OkayBaseimport com.okayqa.composer.function.Mirroimport com.okayqa.composer.function.OKClass
class Login_collect_uncollect extends OkayBase {
public static void main(String[] args) { ClientManage.init(10, 5, 0, "", 0) def util = new ArgsUtil(args) def thread = util.getIntOrdefault(0, 30) def times = util.getIntOrdefault(1, 40)
def tasks = []
thread.times { tasks << new FunTester(it, times) }
new Concurrent(tasks, "资源库1.4登录>查询>收藏>取消收藏链路压测").start()
allOver() }
private static class FunTester extends ThreadLimitTimesCount<Integer> {
OkayBase base
def mirro
def clazz
FunTester(Integer integer, int times) { super(integer, times, null) }
@Override void before() { super.before() base = getBase(t) mirro = new Mirro(base) clazz = new OKClass(base) }
@Override protected void doing() throws Exception {
def klist = mirro.getKList() def karray = klist.getJSONArray("data") K ks karray.each { JSONObject parse = JSON.parse(JSON.toJSONString(it)) if (ks == null) { def level = parse.getIntValue("node_level") def type = parse.getIntValue("ktype") def id = parse.getIntValue("id") ks = new K(id, type, level) } } JSONObject response = clazz.recommend(ks.id, ks.type, ks.level) def minis = [] int i = 0 response.getJSONArray("data").each { if (i++ < 2) { JSONObject parse = JSON.parse(JSON.toJSONString(it)) int value = parse.getIntValue("minicourse_id") minis << value } } clazz.unCollect(random(minis))
mirro.getMiniCourseListV3(ks.id, ks.type, 0, ks.level) } }
private static class K extends AbstractBean {
int id
int type
int level
K(int id, int type, int level) { this.id = id this.type = type this.level = level } }
}
复制代码


其中AbstractBean类是一个抽象类,用于一些bean的方法封装,就是为了省事儿。


package com.funtester.base.bean
import com.alibaba.fastjson.JSONimport com.alibaba.fastjson.JSONObjectimport com.funtester.frame.Saveimport com.funtester.frame.SourceCodeimport org.slf4j.Loggerimport org.slf4j.LoggerFactoryimport org.springframework.beans.BeanUtils
/** * bean的基类 */abstract class AbstractBean {
static final Logger logger = LoggerFactory.getLogger(AbstractBean.class)
/** * 将bean转化为json,为了进行数据处理和打印 * * @return */ JSONObject toJson() { JSONObject.parseObject(JSONObject.toJSONString(this)) }
/** * 文本形式保存 */ def save() { Save.saveJson(this.toJson(), this.getClass().toString() + SourceCode.getMark()); }
/** * 控制台打印,使用WARN记录,以便查看 */ def print() { logger.warn(this.getClass().toString() + ":" + this.toString()); }
def initFrom(String str) { JSONObject.parseObject(str, this.getClass()) }
def initFrom(Object str) { initFrom(JSON.toJSONString(str)) }
def copyFrom(AbstractBean source) { BeanUtils.copyProperties(source, this) }
def copyTo(AbstractBean target) { BeanUtils.copyProperties(this, target) }
/** * 这里bean的属性必需是可以访问的,不然会返回空json串 * @return */ @Override String toString() { JSONObject.toJSONString(this) }
@Override protected Object clone() { initFrom(this) }
}
复制代码

控制台输出

~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~ JSON ~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~>  {>  ① . "rt":1665,>  ① . "total":1188,>  ① . "qps":18.018,>  ① . "failRate":0.0,>  ① . "threads":30,>  ① . "startTime":"2021-02-24 16:57:23",>  ① . "endTime":"2021-02-24 16:58:34",>  ① . "errorRate":1.01,>  ① . "executeTotal":1188,>  ① . "mark":"资源库1.4登录>查询>收藏>取消收藏链路压测241657",>  ① . "table":"eJzj5VLAD15sbXm2a8LTXZMN9Uyez9z9dO9Uu2fzl75Yv8ju2ZRtL6b32z3tn/ZsWweE83Lyvhfb1z/t6362tZvT2EChJKMoNaWYgA0KvFy8+F0RlFpckJ9XnKoQkpmbaqVQoVucWpSZmKOQV5qro1Cpm5uakpmYR8gOQq5QyM3MU4AYZWVhYqmQW6yTm1hhZWxoaQxkE9RNjA2UgEfTOoBo1JZRW2hmRSsQ0ccmsBW0tgnVQzS1DatVtLMRn3W0sPXRtCYgAlLtQITXWura/mhaMxCRYC+VXUGyv2nhmkfTGoGI0rCgrsseTWsBImLSIZ1dCA8sOjmMXNfCU9ZAupNYV8MdC4106qdA2vkAniAGq6OJ8AlVi6GB99FgSvTU8BU8egaBg6jsO3iWHwSuoZUPB4EzRn046sNRHw68M0Z9OOrDEe5DABkr1eo=">  }~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~ JSON ~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~
复制代码


  • 果然就狗了,QPS 慢得一塌糊涂,错误率还挺高,还敢加线程。




FunTester腾讯云社区钦定年度作者,非著名测试开发 er,欢迎关注。


发布于: 6 小时前阅读数: 5
用户头像

FunTester

关注

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

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

评论

发布
暂无评论
单链路性能测试实践