写点什么

基于 docker 的分布式性能测试框架功能验证(一)

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

本文是 FunTester 测试框架分布式性能测试功能拓展实践,是一种比较粗略的技术验证实践,技术方案采用分布式性能测试框架用例方案设想(一)中所设想场景。


粗实现方案分成三块:master 调度机slave 测试机server 被测服务


  • master 调度机:处理用例、分配任务

  • slave 测试机:接受任务、执行用例

  • server 被测服务:提供测试接口

docker 镜像

刚开始学,学了点皮毛,这里只分享几个简单步骤,各位要是有兴趣的话,还是需要一个完成的docker教程的。

基础镜像

这里我选择了Groovy:latest版本作为基础镜像,里面是Groovy 3.0.8,各位使用的使用请注意这个版本需要跟自己项目依赖的Groovy版本一致,不然会报错:


Caused by: groovy.lang.GroovyRuntimeException: Conflicting module versions. Module [groovy-xml is loaded in version 3.0.8 and you are trying to load version 2.5.7
复制代码

启动容器

使用命令docker run -it -u 0 --name funtester aed55a7f14d3 /bin/bash启动容器,这里参数-u 0使用root身份登录,不然会使用groovy账户登录,导致权限不足的报错。

设置网络

因为我的 master 调度机放在本机了,所以多了一个设置容器访问本地主机端口的步骤。


请参考官网文档:


主机的 IP 地址正在更改(如果没有网络访问权限,则没有 IP 地址)。我们建议您连接到特殊的 DNS 名称 host.docker.internal,该名称 解析为主机使用的内部 IP 地址。这是出于开发目的,不适用于 Docker Desktop for Windows 以外的生产环境。


这个功能在安装docker desktop的时候已经默认打开了,所以直接用域名host.docker.internal替换localhost即可访问 master 调度机服务接口。

安装 vim

安装vimapt-get update && apt-get install apt-file -y && apt-file update && apt-get install vim

更新依赖

这里需要使用docker cp命令将本机打包好的jar包,推送到容器中的Groovy lib目录中。

更新镜像

使用命令:docker commit -a "funtester" -m "update groovy" c9596359c1d1 funtester/groovy:v1

更新脚本

将写好的脚本推送到容器中,然后启动对应的脚本(下面会分享),就可以执行验证工作了。这里应当使用dockerfile,原谅我才看了两天,dockerfile还不是很熟练,我打算放在Springboot项目中编写dockerfile文件。

master 调度机

这里我只实现了一种调度功能:就是提供一个接口,该接口返回一个测试用例(尚未封装对象)。提供给 slave 测试机请求,返回给测试机测试任务(测试用例)。


听起来这是一个服务了,但是我现在还没开始写Springboot项目,只能用funtester moco server代替了这个功能。对于用例管理等其他功能还没有实现。

master 脚本

import com.alibaba.fastjson.JSONObjectimport com.funtester.base.bean.Resultimport com.funtester.httpclient.FunLibraryimport com.funtester.httpclient.FunRequestimport com.mocofun.moco.MocoServerimport com.sun.deploy.ui.FancyButtonimport org.apache.http.client.methods.HttpGet
class DcsServer extends MocoServer {
public static void main(String[] args) { def server = getServer(12345) def res = new JSONObject() res.times = 1000 res.thread = 20 res.mode = "ftt" res.desc = "FunTester分布式测试Demo" res.runup = 10 String url = "http://192.168.80.169:12345/m" def get = FunLibrary.getHttpGet(url) def request = FunRequest.initFromRequest(get) res.request = request output(res) server.get(urlStartsWith("/m")).response(obRes(Result.success(res)))
def run = run(server)
waitForKey("fun")
run.stop()
}}
复制代码


其中http://192.168.80.169:12345/m是被测服务暴露的测试接口,也是用funtester moco server做的,后面也会分享脚本内容。


这里我根据测试机中方法com.funtester.httpclient.FunRequest#initFromString和一些必要的参数创建了一个JSON格式的接口返回。其中在request赋值的时候,我采用的方式是:


        def request = FunRequest.initFromRequest(get)        res.request = request
复制代码


能省去不少麻烦,直接把request对象放到响应结果中。


下面是具体的响应结果:


~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~ JSON ~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~>  {>  ① . "code":0,>  ① . "data":{>  ② . . . "mode":"ftt",>  ② . . . "request":{>  ③ . . . . . "args":{>  ④ . . . . . . . >  ③ . . . . . },>  ③ . . . . . "headers":[>  ④ . . . . . . . >  ③ . . . . . ],>  ③ . . . . . "path":"",>  ③ . . . . . "request":{>  ④ . . . . . . . "allHeaders":[>  ⑤ . . . . . . . . . >  ④ . . . . . . . ],>  ④ . . . . . . . "method":"GET",>  ④ . . . . . . . "aborted":false,>  ④ . . . . . . . "protocolVersion":{>  ⑤ . . . . . . . . . "protocol":"HTTP",>  ⑤ . . . . . . . . . "major":1,>  ⑤ . . . . . . . . . "minor":1>  ④ . . . . . . . },>  ④ . . . . . . . "requestLine":{>  ⑤ . . . . . . . . . "method":"GET",>  ⑤ . . . . . . . . . "protocolVersion":{>  ⑥ . . . . . . . . . . . "$ref":"$.data.request.request.protocolVersion">  ⑤ . . . . . . . . . },>  ⑤ . . . . . . . . . "uri":"http://192.168.80.169:12345/m">  ④ . . . . . . . },>  ④ . . . . . . . "params":{>  ⑤ . . . . . . . . . "names":[>  ⑥ . . . . . . . . . . . >  ⑤ . . . . . . . . . ]>  ④ . . . . . . . },>  ④ . . . . . . . "uRI":"http://192.168.80.169:12345/m">  ③ . . . . . },>  ③ . . . . . "requestType":"GET",>  ③ . . . . . "response":{>  ④ . . . . . . . "code":-2,>  ④ . . . . . . . "FunTester":200,>  ④ . . . . . . . "content":"hello funtester!!!">  ③ . . . . . },>  ③ . . . . . "host":"",>  ③ . . . . . "json":{>  ④ . . . . . . . >  ③ . . . . . },>  ③ . . . . . "params":{>  ④ . . . . . . . >  ③ . . . . . },>  ③ . . . . . "uri":"http://192.168.80.169:12345/m">  ② . . . },>  ② . . . "times":1000,>  ② . . . "thread":20,>  ② . . . "runup":10,>  ② . . . "desc":"FunTester分布式测试Demo">  ① . },>  ① . "success":true>  }~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~ JSON ~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~~☢~
复制代码


可以看到在data.request节点中,很多信息都是重复的,这并不影响,因为data.request节点下的key我只会取有用的,这个在设计方案时已经说明了,request信息除了com.funtester.httpclient.FunRequest对象属性外,都是从使用fastjson提供的工具类对org.apache.http.client.methods.HttpRequestBase进行序列化得到的。

slave 测试机

这个逻辑通过简单的轮询去master调度机提供的接口获取测试任务或者测试用例。然后解析,执行测试用例。


脚本如下:



import com.funtester.config.Constantimport com.funtester.frame.execute.Concurrentimport com.funtester.frame.thread.RequestThreadTimesimport com.funtester.httpclient.FunLibraryimport com.funtester.httpclient.FunRequest
class Dcs extends FunLibrary {
public static void main(String[] args) { while (true) { String url = "http://host.docker.internal:12345/m" //请求此接口会返回一个用例,目前没有用对象封装 // String url = "http://localhost:12345/m"//本机调试用的 def get = getHttpGet(url) def response = getHttpResponse(get) if (response.getInteger("code") == 0) { def data = response.getJSONObject("data") def r = data.getString("request") /*此处以后会简化成一个对象*/ def request = FunRequest.initFromString(r).getRequest() //压测模式 def times = data.getIntValue("times") //测试终止条件 def mode = data.getString("mode") //线程数,这里默认固定线程模式 def thread = data.getIntValue("thread") //软启动时间 def runup = data.getIntValue("runup") //用例描述 def desc = data.getString("desc") if (mode == "ftt") { Constant.RUNUP_TIME = runup def task = new RequestThreadTimes(request, times) def performanceResultBean = new Concurrent(task, thread, desc).start() } } sleep(5.0) } }}
复制代码


后期应该会把每个不同的响应体中data封装成不同的对象,这样处理数据会比较简单。


关于测试mode,目前支持了四种(固定线程|固定 QPS * 限制请求次数|限制请求时间),之前都分享过了,这里就不多说。

server 被测服务

同样采用了funtester moco serverr编写,脚本如下:


import com.mocofun.moco.MocoServer
class TestDemo extends MocoServer{
static void main(String[] args) { def log = getServer(12345) log.response("hello funtester!!!")

def run = run(log) waitForKey("fan") run.stop()

}}
复制代码


这次我没有屏蔽日志,这样比较方便观察 slave 测试机是请求是否正确,单词请求的日志内容如下:



Request received:
GET /m HTTP/1.1Host: 192.168.80.169:12345Connection: Keep-AliveUser-Agent: Apache-HttpClient/4.5.6 (Java/1.8.0_282)Accept-Encoding: gzip,deflatecontent-length: 0
Response return:
HTTP/1.1 200Content-Length: 18Content-Type: text/plain; charset=utf-8
hello funtester!!!
复制代码

Have Fun ~ Tester !

FunTester,一群有趣的灵魂,腾讯云 &Boss 认证作者,GDevOps 官方合作媒体。




点击阅读阅文,查看 FunTester 历史原创集合

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

FunTester

关注

公众号:FunTester,Have Fun, Tester! 2020.10.20 加入

Have Fun,Tester!

评论

发布
暂无评论
基于docker的分布式性能测试框架功能验证(一)