GithubHelp home page GithubHelp logo

eriklupander / gotling Goto Github PK

View Code? Open in Web Editor NEW
86.0 10.0 29.0 1.88 MB

Simple golang-based load test application using YAML documents as specification

License: MIT License

Go 75.03% HTML 23.83% Makefile 1.14%

gotling's Introduction

gotling

CircleCI

Simple golang-based load test application using YAML documents as specification.

For a more full-blown explanation of what Gotling is about, see my blog post here: http://callistaenterprise.se/blogg/teknik/2015/11/22/gotling/

Recent updates

  • UPDATE 2020-05-11: The sleep actions now also accepts golang time.Duration strings, i.e. 500ms 2s or 3m etc. Any pure numbers specified are still treated as whole seconds.
  • UPDATE 2020-04-22: Leon Stigter added support for adding arbitrary HTTP headers
  • UPDATE 2020-04-08: Leon Stigter fixed the JSONPath problem
  • UPDATE 2019-04-04: I've updated gotling to use Go modules and changed the project structure to follow https://github.com/golang-standards/project-layout better**

Disclaimer

Please note that this was my very first golang program back in 2015 and it's still (2020) probably full of anti-patterns and other nasty stuff. I'm not actively developing Gotling but I'll happily accept sensible PRs!

What it does

  • Provides high-throughput load testing of HTTP services
    • Supports GET, POST, PUT and DELETE
    • Live metrics using HTML5 canvas from canvasjs
    • Request URLs and bodies can contain ${paramName} parameters
    • ${paramName} values can be extracted from HTTP response bodies and bound to a User context
    • Capturing Set-Cookie response headers
    • POST data can be inlined or read from template files
  • TCP/UDP sockets
    • Can send line-terminated text strings
    • Possible to use ${varname} variables in payload
    • Does not currently support body parsing or variable extraction from data incoming on the TCP/UDP socket.

Building

Requires Go 1.11 or later to be installed on your computer.

NEW: Build using Go Modules

As per 2019-04-03 gotling shall be built using Go modules and there's a Makefile to help out.

1. Clone the source from github

git clone https://github.com/eriklupander/gotling.git

2. Build using Make

cd gotling
make build

3. Building for other OS's

make release

Check the dist/ folder for binaries for OS X, Linux and Window (AMD64)

Note! You still need the samples, data and log folders in the same root directory as your gotling binary when running.

4. Running

If you built gotling using make build, you'll find your binary in the bin/ folder. Try running the sample "xmldemo":

> ./bin/gotling samples/xmldemo.yml

Usage

Define your test setup in a .yml file

---
iterations: 10          # Number of iterations per user
users: 10               # Number of users
rampup: 20              # seconds
actions:                # List of actions. Currently supports http, tcp, udp, sleep
  - http:
      method: GET                                  # GET, POST, PUT, DELETE
      url: http://localhost:8183/courses           # URL. Can include ${paramName} parameters
      accept: json                                 # Only 'json' is currently supported
      response:                                    # Defines response handling
        jsonpath: $[*].id+                         # JsonPath expression to capture data from JSON response
        variable: courseId                         # Parameter name for captured value
        index: first                               # If > 1 results found - which result to store in 'variable': 
                                                   # first, random, last
  - sleep:
      duration: 3                                  # Sleep duration in seconds. Will block current user
  - http:
      method: GET
      url: http://localhost:8183/courses/${courseId}
      accept: json
      response:
        jsonpath: $.author+
        variable: author
        index: first
  - sleep:
        duration: 300ms                             # Note sleep using time.Duration format.
  - tcp:
        address: 127.0.0.1:8081                     # TCP socket connection
        payload: |TYPE|1|${UID}|${email}            # Sample data using parameter substitution

HTTP headers

Gotling supports adding HTTP headers in the YAML specification:

- http:
    title: Some title
    method: POST
    headers:
        foo: bar
        hello: world
    url: http://localhost:9183/mypath
    body: '{"id":100,"name":"Some name","author":"${author}-${someId}","created":"2015-10-23T21:33:38.254+02:00","baseLatitude":45.634353,"baseLongitude":11.3424324}'
    accept: application/json

To give this a try, create a RequestBin URL will collect requests made to it and let you inspect them in a human-friendly way and paste that URL in the demoheaders.yml on line 9.

HTTP POST bodies

Gotling supports POST/PUT bodies either directly inlined in the YAML specification, or read from a template file:

Inlined

- http:
    title: Some title
    method: POST
    url: http://localhost:9183/mypath
    body: '{"id":100,"name":"Some name","author":"${author}-${someId}","created":"2015-10-23T21:33:38.254+02:00","baseLatitude":45.634353,"baseLongitude":11.3424324}'
    accept: application/json

Note that we use body in the inlined example

From template

- http:
    title: Submit query
    method: POST
    url: http://localhost:8080/myxmlservice
    template: myproject/MyFancySOAPRequest.xml
    accept: application/xml

In this example, we use template instead of body. The myproject/ folder should always be placed in the /templates directory in the root of the project. Note that ${varName} variable substitution from feeders (see below) or values captured from previous responses in the action sequence can be used in template files.

Feeders and user context

Gotling currently support CSV feeds of data. First line needs to be comma-separated headers with the following lines containing data, e.g:

id,name,size
1,Bear,Large
2,Cat,Small
3,Deer,Medium

These values can be accessed through ${varname} matching the column header.

The UID

Each "user" gets a unique "UID" assigned to it, typically an integer from 10000 + random(no of users). Perhaps I can tweak this to either use UUID's or configurable intervals. Anyway, the UID can be used using ${UID} and can be useful for grouping data etc.

Capturing cookies

It's quite common that you need to load-test a secured API. Gotling provides a mechanism that allows you to capture Set-Cookie response headers which then will be automatically applied to subsequent HTTP actions for the current user.

Here is an example where a form-based login POST is used for logging in and storing the returned JSESSIONID cookie

- http:
  title: Login
  method: POST
  url: https://some.host.name/login
  body: 'put your urlencoded form data here'
  accept: '*/*'
  contentType: application/x-www-form-urlencoded
  storeCookie: JSESSIONID

Variable capture from HTTP response bodies

It's possible to use jsonpath OR xmlpath to capture variables from HTTP responses (json or xml) and use in subsequent invocations during the ongoing sequence of actions. See ${courseId} in the sample above.

A similar sample for xmlpath:

- http:
          method: GET
          url: http://www.w3schools.com/xml/cd_catalog.xml
          accept: text/xml
          response:
            xmlpath: //title
            variable: myTitleVar
            index: random
- http:
          method: GET
          url: http://some.other.service.maybe/authors/${myTitleVar}
          accept: json

Please note that a response definition only may contain either a jsonpath OR an xmlpath. You can't have both.

For more on xmlpath, see xmlpath

Important note: xmlpath for Go does not support xml namespaces!

HTTPS support

Gotling currently supports HTTPS, including hosts using self-signed certificates.

In the future, we'll probably add an option to allow/disallow unsecure https.

Realtime dashboard

Access at http://localhost:8182

Click "connect" to connect to the currently executing test.

Gotling dashboard

HTML reports

Not functional right now :(

Uses the following libraries

  • github.com/davecheney/profile
  • gopkg.in/yaml.v2
  • gopkg.in/xmlpath.v2
  • gorilla/websocket
  • highcharts

License

Licensed under the MIT license.

See LICENSE

gotling's People

Contributors

bonedaddy avatar eriklupander avatar missedone avatar retgits avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

gotling's Issues

[Question] Open to PRs?

Hi, I found this project while looking for a Go-based load testing tool. I totally like it and would love to contribute back some changes I made. I've added back the JSONPath handling and fixed a small bug in the testdef.RANDOM case in passResultIntoSessionMap .

Add support for file-based templates

Instead of inlining POST bodies etc, add support for reading those as templates from files. Example syntax:

- http:
      method: POST
      url: http://localhost:9183/mypostablepath
      template: myproject/somepostbody.json

Where myproject is a folder in $GOTLING_HOME/templates

A sample template file could be like this, e.g. uses same ${paramName} syntax:

somepostbody.json
{"name":"${fullName}}", "age":"${age}"}

For the templates, it would be interesting to adapt StringTemplate syntax and capabilities, but that can be evaluated in the future.

update README about how to build from source code

hi @eriklupander

build the project a have my shoe wet is the way how we evaluate the tool.
so it's better to document the build steps at least in README, even better is there is a kick start script to run, for example, Makefile or so.

anyway, i'm not sure how you build this project,

here is how i did with gb

# vendoring the dependencies
# if we use gb as well, i think you can commit the gb vendor manifest file so that others can easily restore the dependencies. 
gb vendor fetch gopkg.in/yaml.v2
gb vendor fetch github.com/NodePrime/jsonpath/cli/jsonpath
gb vendor fetch github.com/gorilla/websocket
gb vendor fetch github.com/tobyhede/go-underscore

# build
gb build

# now the executable binary should be under $PROJECT/bin

at last, thanks for sharing the idea and the project.

RFC - accept response by ignore the body

hi @eriklupander

for now, gotling only support accept: json, for some case, i'd like to get the response, read the body but just simply ignore the data.
so, could you add support of this, for example, if we don't specify accept in the spec, then behavior as read body but ignore?

actually, i'd like to try to contribute PR if you think this is valid requirement.
thanks

Example for session sensitive stress test

Hi Erik,

I thanks for your wonderful contribution since have to fight with gatling session / scala for a long time, without anything I'm already familiar with go lang.

Just curious, since I have to deal with API stress test (most of them used for mobile devices), a lot of side-function have to be perform among requests. How do I do it in gotling?

For example, this is the code snippet I got previously on gatling:

...
  val scn_auto = scenario("android_auto")
      .exec(http("/api/register")
        .post("/api/register")
        .header("Accept","application/json, application/*+json")
        .header("Authorization", "%s".format(authorizationStr0))
        .header("Content-Type", "multipart/form-data;boundary=%s".format(sep0))
        .body(
          StringBody(
            session => body0
          )
        )
        .check(
          jsonPath("$.result").saveAs("result0")
        )
      )
      .exec(session => {
        result0 = session("result0").as[String]
        val decryptText: String = AES.decrypt(result0, todayPadding)
        val respJSON = JSON.parseFull(decryptText)
        client_id = respJSON
          .get
          .asInstanceOf[Map[String, Any]]("client_id")
          .asInstanceOf[String]
        client_secret_key = respJSON
          .get
          .asInstanceOf[Map[String, Any]]("client_secret_key")
          .asInstanceOf[String]
        println("client_id -> " + client_id)
        println("client_secret_key -> " + client_secret_key)
        session.set("sep1", genSep(r, 36)).set("client_id", client_id).set("client_secret_key", client_secret_key)
      })
      .exec(http("/api/login")
        .post("/api/login")
        .header("Accept","application/json, application/*+json")
        .header(
          "Authorization",
          session => {
            val theClientId = session("client_id").as[String]
            Base64.getEncoder.encodeToString(theClientId.getBytes("UTF-8"))
          })
        .header(
          "Content-Type",
          session => {
            val theSep = session("sep1").as[String]
            "multipart/form-data;boundary=%s".format(theSep)
          })
        .body(
          StringBody(
            session => {
              val theSep = session("sep1").as[String]
              val theClientSecretKey = session("client_secret_key").as[String]
              genBody(theSep, dataToCryptText(data1, theClientSecretKey))
            }
          )
        )
        .check(
          jsonPath("$.result").saveAs("result1")
        )
      )
...

AES.encrypt, AES.decrypt, genBody are my custom functions. Basic workflow is:

  1. send my device id to server with today date as AES encrypt key + base64 encoded
  2. base64 decoded + AES decrypt with today's date the response and save the private key issued
  3. send my login id and pwd with AES encrypt the private key + base64 encoded and get response

How do I do the same thing in gotling?

Getting error HTTP request failed: connect: cannot assign requested address

Great work on the repo btw, I really like the whole idea and design.

I am facing an issue

HTTP request failed: dial tcp xx.xx.xx.xx: connect: cannot assign requested address

My yaml file is like this:

iterations: 10
users: 5000
rampup: 20

with

  - sleep:
      duration: 5

I am running it on a powerful VM, so memory should not be a problem.

Have you faced something like this?

Broken builds.

Build is broken as github.com/NodePrime/ is no longer there. It is more sensible to include the vendor package inside the main code base.

The project looks awesome - Please fix the build - so that I can try and play with this.

Also, it is worthy of putting on godoc.org...

Gotling opening up new TCP connection for each request

I am monitoring open connections made by gotling on nginx, and I saw that gotling opened a new connection for each request.

So for instance if I had 100 users, each with 10 iterations, gotling made a total of 1000 new TCP connections to nginx.

I'm not sure if this was the intended behavior, but I did some research, and by moving the code below in httpreq.go to outside the function DoHttpRequest (basically making it a global variable):

var DefaultTransport http.RoundTripper = &http.Transport{
	TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}

reduced the number of new TCP connections.

My guess is that gotling started utilizing persistent connections after this change?

Love to hear your thoughts on this.

Host request-header Support

Hi, is Host http header is supported. I want to stress test linkerd service which works with Host http header.

Any help would be appreciated, thanks

1000 request/sec example

Hey Erik,
Just want to ask,
Ho do I simulate like 1000 rps initiated (not complete),
and with the yml file,
how do I setup something like 200 unique users in that 1000 requests?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.