Boomer Documentation¶
What is Boomer?¶
Boomer is a golang library and works with Locust.
Using goroutines to run you code concurrently will outperform the gevent implementation in Locust. That’s why I created this project.
Remember, use it as a library, not a general-purpose benchmarking tool.
Features¶
- Write user test scenarios in golang
Just put you test scenarios in a normal function, boomer will spawn goroutines to run the function for many times to produce stress.
- Build-in rate limit support
You can put rate limit on each boomer instance, which is useful when you just want to evaluate if the target is able to handle specific requests per second, instead of exhausting the target.
- Different output destination
You can write you own output implementation to collect the test result.
Installation¶
Boomer can be installed and updated with the “go get” command.
install:
$ go get github.com/myzhan/boomer
update:
$ go get -u github.com/myzhan/boomer
If you want to point to a particular revision of boomer, you should use a dependency management tool like dep or go module.
The goczmq dependency¶
Locust uses the zeromq protocol, so boomer depends on a zeromq client. Boomer uses gomq by default, which is a pure Go implementation.
Because of the instability of gomq, you can switch to goczmq.
Once install goczmq successfully, then you can build with goczmq instead of gomq.
$ go build -tags 'goczmq' your-code.go
Quickstart¶
Code¶
This is a quick example of writing test scenarios with boomer.
package main
import "time"
import "github.com/myzhan/boomer"
func foo(){
start := time.Now()
time.Sleep(100 * time.Millisecond)
elapsed := time.Since(start)
/*
Report your test result as a success
*/
boomer.RecordSuccess("http", "foo", elapsed.Nanoseconds()/int64(time.Millisecond), int64(10))
}
func bar(){
start := time.Now()
time.Sleep(100 * time.Millisecond)
elapsed := time.Since(start)
/*
Report your test result as a failure
*/
boomer.RecordFailure("udp", "bar", elapsed.Nanoseconds()/int64(time.Millisecond), "udp error")
}
func main(){
task1 := &boomer.Task{
Name: "foo",
// The weight is used to distribute goroutines over multiple tasks.
Weight: 10,
Fn: foo,
}
task2 := &boomer.Task{
Name: "bar",
Weight: 20,
Fn: bar,
}
boomer.Run(task1, task2)
}
Here we define two tasks, task1 reports a success to boomer every 100 milliseconds, and meanwhile task2 reports a failure. The weight of task1 is 10, and the weight of task2 is 20, if the locust master asks boomer to spawn 30 users, then task1 will get 10 goroutines to run and task2 will get 20. The numbers of users can be specified in the Web UI.
Test¶
Because task1 is named foo and tasks2 is named bar, you can run them without connecting to the master.
$ go run --run-tasks foo,bar
In this case, task1 and task2 will be run for one time with no output.
You can add logs to ensure your tasks running correctly.
Build¶
$ go build -o you-code you-code.go
Run¶
- Start the locust master with the included dummy.py.
$ locust --master -f dummy.py
So far, dummy.py is necessary when starting a master, because locust needs such a file.
Don’t worry, dummy.py has nothing to do with your test.
- Start you test program.
$ chmod +x ./you-code && ./you-code
Note
To see all available options type: you-code --help
Open up Locust’s web interface¶
Once you’ve started Locust and boomer, you should open up a browser and point it to http://127.0.0.1:8089 (if you are running Locust locally).
Running Mode¶
Currently, boomer has two running mode, standalone and distributed.
Distributed¶
When running in distributed mode, boomer will connect to a locust master and running as a slave. It’s the default running mode of boomer.
Standalone¶
When running in standalone mode, boomer doesn’t need to connect to a locust master and start testing immediately.
By default, the standalone mode works with a ConsoleOutput, which will print the test result to the console, you can write you own output and add more by calling boomer.AddOutput().
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | package main
import (
"log"
"time"
"github.com/myzhan/boomer"
)
func foo() {
start := time.Now()
time.Sleep(100 * time.Millisecond)
elapsed := time.Since(start)
// Report your test result as a success, if you write it in python, it will looks like this
// events.request_success.fire(request_type="http", name="foo", response_time=100, response_length=10)
globalBoomer.RecordSuccess("http", "foo", elapsed.Nanoseconds()/int64(time.Millisecond), int64(10))
}
var globalBoomer *boomer.Boomer
func main() {
log.SetFlags(log.LstdFlags | log.Lshortfile)
task1 := &boomer.Task{
Name: "foo",
Weight: 10,
Fn: foo,
}
numClients := 10
spawnRate := 10
globalBoomer = boomer.NewStandaloneBoomer(numClients, spawnRate)
globalBoomer.Run(task1)
}
|
Custom output¶
You can write you own output to deal with the test result.
type Output interface {
OnStart()
OnEvent(data map[string]interface{})
OnStop()
}
All the OnXXX function will be call in a separated goroutine, just in case some output will block. But it will wait for all outputs return to avoid data lost.
It works like:
wg := sync.WaitGroup{}
wg.Add(len(outputs))
for _, output := range outputs {
go func(o Output) {
o.OnXXXX()
wg.Done()
}(output)
}
wg.Wait()
OnStart¶
OnStart will be call before the test starts.
OnEvent¶
By default, each output receive stats data from runner every three seconds. OnEvent is responsible for dealing with the data.
Don’t write to the origin data! Because all outputs share the same reference.
OnStop¶
OnStop will be called before the test ends. If you are writing to a disk file, it’s time to flush.
Options¶
For convenience, boomer supports several command line options.
Since it may conflict with user’s code, I’m planning to remove this feature and allow users to set options programmatically.
--master-host
¶
Host or IP address of locust master for distributed load testing.
Defaults to 127.0.0.1.
--master-port
¶
The port to connect to that is used by the locust master for distributed load testing.
Defaults to 5557.
--run-tasks
¶
Run tasks without connecting to the master, multiply tasks is separated by comma.
--max-rps
¶
Max RPS that boomer can generate, disabled by default.
–max-rps=100 means the max RPS is limit to 100.
Defaults to 0.
--request-increase-rate
¶
Request increase rate, disabled by default.
–request-increase-rate=100/1s means the threshold will ramp up.
--cpu-profile
¶
Enable CPU profiling and specify a file path to save the result.
--cpu-profile-duration
¶
CPU profile duration.
The timer will start when the process starts.
Defaults to 30 seconds.
--mem-profile
¶
Enable memory profiling and specify a file path to save the result.
--mem-profile-duration
¶
Memory profile duration.
The timer will start when the process starts.
Defaults to 30 seconds.
Profiling¶
You may think there are bottlenecks in your load generator, don’t hesitate to do profiling.
Both CPU and memory profiling are supported.
It’s not suggested to run CPU profiling and memory profiling at the same time.
CPU Profiling¶
# 1. run locust master.
# 2. run boomer with cpu profiling for 30 seconds.
$ go run main.go -cpu-profile cpu.pprof -cpu-profile-duration 30s
# 3. start test in the WebUI.
# 4. run pprof.
$ go tool pprof cpu.pprof
Type: cpu
Time: Nov 14, 2018 at 8:04pm (CST)
Duration: 30.17s, Total samples = 12.07s (40.01%)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) web
Memory Profiling¶
# 1. run locust master.
# 2. run boomer with memory profiling for 30 seconds.
$ go run main.go -mem-profile mem.pprof -mem-profile-duration 30s
# 3. start test in the WebUI.
# 4. run pprof and try 'go tool pprof --help' to learn more.
$ go tool pprof -alloc_space mem.pprof
Type: alloc_space
Time: Nov 14, 2018 at 8:26pm (CST)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top