原文由 bugVanisher 发表于 TesterHome 社区,点击原文链接可与作者间接交换。
前段时间,我主导推动组里实现了一套基于 Locust+boomer 的通用的压测平台,次要目标是满足咱们组内的各种压测场景,比方 grpc、websocket、webrtc、http 等协定的压测场景。正好咱们公司的技术栈以 go 为主,咱们能够轻松地应用 go 编写脚本,通过公司的部署平台编译打包后横向扩缩施压集群,能够说解决了各种压测需要。然而咱们发现,只管本人编写脚本十分自在,然而对不理解平台、不理解 Go 的同学来说,应用老本是比拟大的,尤其是首次接触,因而我开始思考如何简化脚本的编写和部署。
从 http 开始
来源于 httprunner 和公司其余 Group 的工具的灵感,我想到用 json 的形式去定义 http 压测场景,而后用 go 去解析执行,能够预感的是这种形式的压测性能不如间接写代码,然而如果能够通过可承受的性能损耗来换取更简略的接入形式,更对立的应用形式,也是极好的,毕竟咱们不缺机器。针对 http 协定,以下大略梳理了一下须要实现的能力。
简略阐明下:
-
多接口
很好了解,压测的时候须要满足多个接口按肯定的比例同时压测。在某些非凡场景下,可能还存在接口依赖的问题,这也要思考到。
-
登录态
压测的接口可能有登录校验,那么压测的时候须要带上登录态,如果能买通账号平台主动批量生成登录态就不便很多了。
-
参数化
定义的脚本须要提供参数化能力,总不能所有参数写死,比方动静生成工夫戳,ID,变长字符串等等,如果简略的参数生成无奈满足,用户本人上传也是挺好的。
-
校验
响应内容校验是接口测试很重要的局部,在压测场景也是一样的。
定义 Json 构造
接下来,定义 Json 构造,尽量去满足下面所形容的需要。http 协定,无非就是三个局部,body、header、url,因而每一个接口须要蕴含这个三个字段,当然,名字是必不可少的,还有一个十分重要的字段,就是校验字段 validator,上面就来看看这个 Json 应该是什么样子。
{
"debug": true,
"domain": "https://postman-echo.com",
"header": {},
"declare": ["{{ $sessionId := getSid}}"
],
"init_variables": {
"roomId": 1001,
"sessionId": "{{$sessionId}}",
"ids": "{{$sessionId}}"
},
"running_variables": {"tid": "{{ getRandomId 5000}}"
},
"func_set": [
{
"key": "getTest",
"method": "GET",
"url": "/get?name=gannicus&roomId={{.roomId}}&age=10&tid={{.tid}}",
"body": "{\"timeout\":10000}",
"validator": "{{and (eq .http_status_code 200) (eq .args.age (10 | toString)) }}"
},
{
"key": "postTest",
"method": "POST",
"header": {"Cookie": "{{ .tid}}",
"Content-Type": "application/json"
},
"url": "/post?name=gannicus",
"body": "{\"timeout\":{{ .tid}}, \"retry\":true}",
"validator": "{{and (eq .http_status_code 200) (eq .data.timeout (.tid | toFloat64) ) (eq .data.retry false) }}"
}
]
}
func_set 应该挺好了解的,这里解释一下 declare、init_variables、running_variables:
-
declare
这个字段是为了申明变量的,比方在 init 或 running 变量中都能够援用这个变量,申明形式如:["{{ $sessionId := getSid}}", "{{$id := 100100}}" ]
-
init_variables
初始化变量,只初始化一次,能够是常量,也能够从模板函数中获取,如:
{ "roomId": 1001, "sessionId": "{{$sessionId}}", "ids": "{{now}}" }
-
running_variables
运行时变量,每一个申请发动前都会去结构参数,因而不倡议常量定义在这里。
{"tid": "{{ getRandomId 5000}}" }
解析流程
想要利用 boomer,那就须要想方法生成 boomer.Task,它的构造如下:
type Task struct {
// The weight is used to distribute goroutines over multiple tasks.
Weight int
// Fn is called by the goroutines allocated to this task, in a loop.
Fn func()
Name string
}
外围就是失去这个执行函数 Fn,思路就是别离依据 init 和 running 变量定义,为 func_set 中申明的每个申请别离定义一个匿名函数,函数中去动静生成变量,而后发动实在申请,最初依据每个申请申明的 validator 进行断言。整个执行流程如下:
实现原理
go 的原生库中就有模板相干的库 text/template, 我间接应用模板库实现了这套解析逻辑,包含参数的生成,模板办法、断言,整个 json 脚本的语法都是基于 go 的模板库的。感兴趣的敌人能够查看:
- 官网模板库
- Go 语言规范库 text/template 包深入浅出
如何断言
因为断言这部分十分重要,所以独自讲。下面曾经说过断言是通过模板来实现的,所以要应用断言就要把握根本的模板语法。
模板库内置了比拟和逻辑办法所以能够间接应用,比方比拟 http 状态码:
"validator": "{{eq .http_status_code 200}}"
再比方多个比拟:
"validator": "{{and (eq .http_status_code 200) (eq .data.timeout (.tid | toFloat64) ) (eq .data.retry false) }}"
可能你也曾经留神到了有一个 toFloat64, 它是一个模板自定义函数,这里是为了做类型转换。
此外,你也能够看到基于 go 模板库,拜访变量变得非常简单,比方下面的.data.timeout, 它对应响应中的内容相似如下:
{
"data":{"timeout": 1000}
}
这样咱们就能够比拟响应 json 中的任何字段了。
写在最初
简略做了一下压力测试,比照不应用模板解析和应用模板解析的状况,模板解析在 CPU 密集型的场景下性能大略是间接写脚本编译的三分之一,如果不是 CPU 密集型应该能够去到二分之一,因而我临时不优化了。令人惊喜的是这个模板解析也能够扩大成为接口测试的编写形式,相似 httprunner。
目前只是做了一个 Demo,还没正真集成到咱们的压测平台,还是挺令人期待呀。
源码地址:https://github.com/bugVanisher/go-httpwrapper
原文由 bugVanisher 发表于 TesterHome 社区,点击原文链接可与作者间接交换。
今日份的常识已摄入~
想理解更多前沿测试开发技术:欢送关注「第十届 MTSC 大会·上海」>>>
1 个主会场 +12 大专场,大咖星散精英齐聚
12 个专场包含:
知乎、OpenHarmony、开源、游戏、酷家乐、音视频、客户端、服务端、数字经济、效力晋升、品质保障、智能化测试