Functional verification of docker-based distributed performance testing framework (1)

Functional verification of docker-based distributed performance testing framework (1)

This article is the practice of the distributed performance test function expansion of the FunTester test framework, which is a relatively rough technical verification practice. The technical solution adopts the scenario envisaged in the distributed performance test framework use case scenario (1) .

The rough implementation scheme is divided into three parts: master scheduling machine , slave test machine and server tested service .

  • master scheduling machine: handle use cases and assign tasks
  • Slave test machine: accept tasks and execute use cases
  • Server tested service: provide test interface

docker image

I just started to learn, I learned a little bit, here are just a few simple steps, if you are interested, you still need one to complete

docker
Tutorial.

Base mirror

Here i chose

Groovy:latest
The version is used as the base image, which is
Groovy 3.0.8
, Please note that this version needs to be dependent on your own project
Groovy
The version is the same, otherwise an error will be reported:

By Caused: groovy.lang.GroovyRuntimeException: Conflicting Module1 versions Module1 [Groovy IS-loaded in XML Version. 3.0 .8 and you are Trying to Load Version 2.5 .7 duplicated code

Start the container

Use the command

docker run -it -u 0 --name funtester aed55a7f14d3/bin/bash
Start the container, this parameter
-u 0
use
root
Login as identity, or you will use
groovy
Account login, resulting in insufficient authority error.

Set up network

Because my master scheduling machine is placed on the local machine, there is an additional step to set the container to access the local host port.

Please refer to the official website document:

The IP address of the host is changing (if there is no network access, there is no IP address). We recommend that you connect to the special DNS name host.docker.internal, which resolves to the internal IP address used by the host. This is for development purposes and does not apply to production environments other than Docker Desktop for Windows.

This feature is installed

docker desktop
Is already turned on by default, so use the domain name directly
host.docker.internal
replace
localhost
You can access the master dispatcher service interface.

Install vim

installation

vim
:
apt-get update && apt-get install apt-file -y && apt-file update && apt-get install vim

Update dependencies

Need to use here

docker cp
Command to package the machine
jar
Package, pushed to the container
Groovy lib
Directory.

Update mirror

Use the command:

docker commit -a "funtester" -m "update groovy" c9596359c1d1 funtester/groovy:v1

Update script

Push the written script to the container, and then start the corresponding script (which will be shared below), and you can perform the verification work. Should be used here

dockerfile
, Forgive me for just watching for two days,
dockerfile
I'm not very skilled yet, I plan to put
Springboot
Written in the project
dockerfile
file.

master scheduling machine

Here I only implement a scheduling function: to provide an interface that returns a test case (the object has not been encapsulated). Provide the request to the slave tester and return it to the tester for the test task (test case).

It sounds like this is a service, but I haven't started writing yet

Springboot
Item, can only be used
funtester moco server
Replace this function. Other functions such as use case management have not yet been implemented.

master script

import com.alibaba.fastjson.JSONObject import com.funtester.base.bean.Result import com.funtester.httpclient.FunLibrary import com.funtester.httpclient.FunRequest import com.mocofun.moco.MocoServer import com.sun.deploy.ui .FancyButton import 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 Distributed Test 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() } } Copy code

among them

http://192.168.80.169:12345/m
It is the test interface exposed by the service under test, and it is also used
funtester moco server
If you do, the content of the script will be shared later.

Here I am based on the method in the test machine

com.funtester.httpclient.FunRequest#initFromString
And some necessary parameters created a
JSON
The format of the interface returns. Which is in
request
When assigning values, the method I use is:

def request = FunRequest.initFromRequest(get) res.request = request Copy code

Can save a lot of trouble, just put

request
The object is placed in the response result.

The following is the specific response result:

~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~ 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 Distributed Test Demo" . }, . "Success" : true } ~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~ JSON ~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~ Copy code

Can be seen in

data.request
In the node, a lot of information is repeated, which does not affect, because
data.request
Under the node
key
I will only take the useful ones. This has been explained in the design plan.
request
Information except
com.funtester.httpclient.FunRequest
Except for object properties, they are all from
fastjson
Tools provided
org.apache.http.client.methods.HttpRequestBase
Get serialized.

slave test machine

This logic goes through simple polling

master scheduling machine
The provided interface obtains test tasks or test cases. Then parse and execute the test case.

The script is as follows:

import com.funtester.config.Constant import com.funtester.frame.execute.Concurrent import com.funtester.frame.thread.RequestThreadTimes import com.funtester.httpclient.FunLibrary import com.funtester.httpclient.FunRequest class Dcs extends FunLibrary { public static void main (String[] args) { while ( true ) { String url = "http://host.docker.internal:12345/m" //Requesting this interface will return a use case, currently there is no object encapsulation //String url = "http://localhost:12345/m"//For local debugging def get = getHttpGet(url) def response = getHttpResponse(get) if (response.getInteger( "code" ) == 0 ) { def data = response.getJSONObject( "data" ) def r = data.getString( "request" ) /*Here will be simplified into an object in the future*/ def request = FunRequest.initFromString(r).getRequest() //Pressure test mode def times = data.getIntValue( "times" ) //Test termination condition def mode = data.getString( "mode" ) //Number of threads, here the default fixed thread mode def thread = data.getIntValue( " thread" ) //Soft start time def runup = data.getIntValue( "runup" ) //Use case description 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 ) } } } Copy code

In the later stage, each different response body should be

data
Encapsulated into different objects, so that processing data will be relatively simple.

About testing

mode
, Currently supports four types (fixed thread|fixed QPS * limited number of requests|limited request time), all of which have been shared before, so I won t say more here.

server tested service

Also adopted

funtester moco serverr
Written, the script is as follows:

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() } } Copy code

This time I did not block the log, so it is more convenient to observe whether the request of the slave test machine is correct. The log content of the word request is as follows:

Request received: GET/m HTTP/1.1 Host: 192.168 .80 .169 : 12345 Connection: Keep-Alive - Agent-the User: the Apache-HttpClient/4.5 of 5 .6 (the Java/1.8 .0_282 ) Accept-Encoding: gzip,deflate content-length: 0 Response return : HTTP/1.1 200 Content-Length: 18 Content-Type: text/plain; charset=utf- 8 hello funtester!!! Copy code

FunTester , Tencent Cloud author of the year , Boss direct contract author , GDevOps official partner media , non-famous test development.