boomer: a Go load generator that plugs into a Locust master
A better load generator for locust, written in golang.
At a glance
- What is it?
- boomer is an MIT licensed Go library that lets Locust drive load through thousands of goroutines, reporting results back to the Locust master's web UI over ZeroMQ. It is meant to be used as a library inside your own program, and its version tags track Locust's own releases.
- Who is it for?
- boomer is a good pick for a team whose Locust scenarios need to exercise a Go client, a binary protocol or thousands of concurrent goroutines, because the reporting path is unchanged so the existing Locust web UI still works, and the profiler support is the piece you will want when the load generator itself becomes the bottleneck; it is the wrong pick if you want a general purpose benchmarking tool or if your team is not already on Locust, since boomer deliberately refuses to be a standalone runner, and before you pin a version you should check the Locust release it corresponds to, because the project now follows Locust's versioning and the master branch tracks Locust's master branch.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 106 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
It is a library, and it says so
Boomer is described as a better load generator for Locust, written in Go, and the sentence that sets the scope is the fourth one in the description: use it as a library, not a general-purpose benchmarking tool.
That is worth dwelling on, because the name suggests a tool and the reality is a library. What boomer gives you is a Go program that you write, which contains your load test logic as ordinary Go functions, and which then reports its results into a Locust master. The Locust web UI is unchanged, and your test shows up in the same charts as a Python Locust test would.
The mechanism is that boomer spawns thousands of goroutines to run your code concurrently, and it will listen and report to the Locust master automatically.
The reporting API is two functions. RecordSuccess reports a successful request with a type, a name, a response time and a response length, and RecordFailure does the same with an exception message. The example in the README shows both, and the comments in the code make the correspondence to Locust explicit, showing what the equivalent Python event would look like:
boomer.RecordSuccess("http", "foo", elapsed.Nanoseconds()/int64(time.Millisecond), int64(10))
boomer.RecordFailure("udp", "bar", elapsed.Nanoseconds()/int64(time.Millisecond), "udp error")The unit for the response time is milliseconds, computed by dividing the elapsed nanoseconds, which is a small detail that will save you an off-by-a-thousand if you copy the example.
There is a second entry point for the case where the master is not available. boomer.Run takes your tasks, and for debugging you can run the tasks directly without connecting to the master.
Tasks, weights, and how goroutines get distributed
The task model is the second half of the API, and it is where the shape of your load test is decided.
A Task has a name, a weight, and a function. The weight is used to distribute goroutines over multiple tasks, which is how you model a workload where most traffic hits one endpoint and a little hits another.
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)So with a weight of 10 and 20, roughly a third of the goroutines run foo and two thirds run bar. That is the same weighted-distribution idea Locust itself uses, which means a Locust user will recognise it immediately.
The repository layout shows the internal structure, and the file names map onto the concepts. task.go and taskset.go handle the task model, with taskset_test.go covering the set behaviour. runner.go is the execution engine. ratelimiter.go handles the request rate control, with its own test file. stats.go and output.go deal with the results and their presentation, events.go with the reporting, client.go with the connection to the master, and message.go with the wire format.
There is also a legacy.go with legacy_test.go, which is a hint about the project's history: the legacy file is almost certainly the compatibility layer for the older Locust protocol, kept so that a boomer built against a recent version can still talk to an older master.
The core of the library is boomer.go, and the test files sit next to their subjects throughout, including a boomer_suite_test.go at the root, which is the Ginkgo suite bootstrap.
Rate limiting and the ramp-up control
The flags that control how much load is applied are the ones that decide whether a test is meaningful.
--max-rps means the max count that all the Task.Fn can be called in one second. So the ceiling is on the total call rate across every task function, not per task:
$ go build -o a.out main.go
$ ./a.out --max-rps 10000The README carries a warning attached to this flag that is easy to skim past. The result may be misleading if you call boomer.RecordSuccess() more than once in Task.Fn. In other words, if your task function reports several successes per invocation, the recorded request rate and the actual invocation rate diverge, and the chart in the Locust UI will not match what the limiter thinks it is doing. It is a real measurement trap rather than a documentation nitpick.
The ramp-up is a separate flag. If you want the RPS to increase from zero to max-rps or infinity, --request-increase-rate takes a number and an optional interval:
$ go build -o a.out main.go
# The default interval is 1 second
$ ./a.out --request-increase-rate 10
# Change the interval to 1 minute
# Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h"
$ ./a.out --request-increase-rate 10/1mThe interval syntax with a unit suffix and a slash is a pleasant detail, and the list of valid units includes the microsecond spelled both ways.
Together these two flags are what turn a script into a load test with a shape: a ceiling and a ramp. A test with only one of them is either a spike or a plateau, and neither is usually what a capacity question needs.
ZeroMQ, and the pure Go implementation you can swap out
The transport between the boomer process and the Locust master is ZeroMQ, and the implementation is a build choice you have to make.
Boomer uses gomq by default, which is a pure Go implementation of the ZeroMQ protocol. Because of the instability of gomq, you can switch to goczmq, the C binding:
# use gomq
$ go build -o a.out main.go
# use goczmq
$ go build -tags 'goczmq' -o a.out main.goAnd if you fail to compile boomer with gomq, the advice is to update gomq first:
$ go get -u github.com/myzhan/gomqThe repository confirms the split at the source level. There is client_gomq.go with client_gomq_test.go for the default path and client_czmq.go for the C binding, both implementing the same client interface, and the go.mod carries both github.com/myzhan/gomq with its zmtp subpackage and github.com/zeromq/goczmq.
The gomq implementation is also a separate project by the same author, myzhan/gomq, and the zmtp subpackage is pinned to a specific commit. The advice to update gomq when the build breaks is a real consequence of tracking an upstream that moves quickly, and it is the kind of thing that would be a support burden if this were not documented.
Using a pure Go implementation is the right default for a load generator, because a cgo dependency complicates cross-compilation and puts a C library in the path of the thing you are trying to profile. The goczmq escape hatch exists for the cases where the pure Go implementation misbehaves, and the fact that both are supported means you are not stuck if one breaks.
Running without a master, and the Prometheus exporter
Two operational details decide how you set up a run, and both are documented with a clear rationale.
For debugging, you can run tasks without connecting to the master:
$ go build -o a.out main.go
./a.out --run-tasks foo,barOtherwise, start the master using the included dummy.py, which is what the included file is for:
$ locust --master -f dummy.pyThe README explains the requirement plainly: so far dummy.py is necessary when starting a master, because Locust needs such a file. And then it addresses the obvious confusion, saying that dummy.py has nothing to do with your test. It is a placeholder that satisfies Locust's requirement for a task file, and the reassurance is welcome because a file called dummy.py in a load testing setup invites the wrong assumption.
If you are not satisfied with the built-in web monitor in Locust, you can run prometheus_exporter.py instead of dummy.py as your master:
$ locust --master -f prometheus_exporter.pyThat is the path to Prometheus and Grafana, and the project links a ready-made dashboard for it, the Locust for Prometheus dashboard on Grafana.
So there are three levels of observability. The Locust web UI is the default, the Prometheus exporter is the intermediate step for teams already running that stack, and the third is the profiling support in boomer itself, which is the subject of the next section.
The go.mod shows the supporting dependencies for this: prometheus/client_golang for the metrics path, olekukonko/tablewriter for the console output, and shirou/gopsutil for the process and system information the exporter reports.
Profiling the load generator itself
The profiling section exists because of a specific anxiety, and the README states it in one line: you may think there are bottlenecks in your load generator, do not hesitate to do profiling.
That is a real problem in load testing. If the generator is the bottleneck, the results are wrong in a way that is hard to detect, because the numbers look plausible.
Both CPU and memory profiling are supported, and the README notes that it is not suggested to run them at the same time, which makes sense since both add overhead to the process you are trying to measure.
The CPU flow is a four-step sequence: run the Locust master, run boomer with CPU profiling for 30 seconds, start the test in the web UI, and then run pprof:
# 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.pprofThe memory flow is the same shape with a different flag and a different pprof view, and the view it uses is alloc_space:
$ go run main.go -mem-profile mem.pprof -mem-profile-duration 30s
$ go tool pprof -alloc_space mem.pprofThat choice of view is the right one for a load generator, since alloc_space shows total allocation rather than live heap, and a generator that churns allocations will show it there even when the live heap stays flat.
The go.mod also carries google/pprof as an indirect dependency, and the profile flags are duration-bounded, so you can profile a specific window of a longer test rather than the whole run.
Editorial conclusion
boomer is a good pick for a team whose Locust scenarios need to exercise a Go client, a binary protocol or thousands of concurrent goroutines, because the reporting path is unchanged so the existing Locust web UI still works, and the profiler support is the piece you will want when the load generator itself becomes the bottleneck; it is the wrong pick if you want a general purpose benchmarking tool or if your team is not already on Locust, since boomer deliberately refuses to be a standalone runner, and before you pin a version you should check the Locust release it corresponds to, because the project now follows Locust's versioning and the master branch tracks Locust's master branch.
Frequently asked questions
What is boomer and how does it relate to Locust?
Boomer is an MIT licensed Go library that acts as a load generator for Locust, spawning thousands of goroutines to run your code concurrently. It listens and reports to the Locust master automatically, so test results appear in the master's web UI. The README says to use it as a library, not a general-purpose benchmarking tool.
How do I install boomer and which version should I use?
Run go get github.com/myzhan/boomer@master for the master branch, or go get github.com/myzhan/[email protected] for a tagged version that works with Locust 1.6.0. Boomer now follows Locust's versioning, with the master branch working with Locust's master branch, and a tagged version is cut when Locust introduces breaking changes.
How do I report results from a boomer task?
Call boomer.RecordSuccess with a request type, a name, the response time in milliseconds and a response length, or boomer.RecordFailure with an exception message instead of a length. The README warns that results may be misleading if you call RecordSuccess more than once inside a single task function.
What are the main boomer command line flags?
--max-rps sets the maximum count of Task.Fn calls per second across all tasks, --request-increase-rate ramps the RPS up with an optional interval such as 10/1m, and --run-tasks runs specific tasks without connecting to a master, which is the debugging mode.
Can I profile boomer itself?
Yes, CPU and memory profiling are both supported, though not at the same time. Pass -cpu-profile and -cpu-profile-duration, or -mem-profile and -mem-profile-duration, and then inspect the result with go tool pprof, using the alloc_space view for memory.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/myzhan-boomer)Was this useful?