Documentation
¶
Overview ¶
Package blaster provides the back-end for blast - a tool for load testing and sending api requests in bulk.
Blast
=====
* Blast makes API requests at a fixed rate.
* The number of concurrent workers is configurable.
* The rate may be changed interactively during execution.
* Blast is protocol agnostic, and adding a new worker type is trivial.
* For load testing: random data can be added to API requests.
* For batch jobs: CSV data can be loaded from local file or GCS bucket, and successful items from previous runs are skipped.
Installation
============
## Mac
```
brew tap dave/blast
brew install blast
```
## Linux
See the [releases page](https://github.com/dave/blast/releases)
## From source
```
go get -u github.com/dave/blast
```
Examples
========
Using the dummy worker to send at 20,000 requests per second (the dummy worker returns after a random wait, and occasionally returns errors):
```
blast --rate=20000 --workers=1000 --worker-type="dummy" --worker-template='{"min":25,"max":50}'
```
Using the http worker to request Google's homepage at one request per second (warning: this is making real http requests - don't turn the rate up!):
```
blast --rate=1 --worker-type="http" --payload-template='{"method":"GET","url":"http://www.google.com/"}'
```
Status
======
Blast prints a summary every ten seconds. While blast is running, you can hit enter for an updated
summary, or enter a number to change the sending rate. Each time you change the rate a new column
of metrics is created. If the worker returns a field named `status` in it's response, the values
are summarised as rows.
Here's an example of the output:
```
Metrics
=======
Concurrency: 1999 / 2000 workers in use
Desired rate: (all) 10000 1000 100
Actual rate: 2112 5354 989 100
Avg concurrency: 1733 1976 367 37
Duration: 00:40 00:12 00:14 00:12
Total
-----
Started: 84525 69004 14249 1272
Finished: 82525 67004 14249 1272
Mean: 376.0 ms 374.8 ms 379.3 ms 377.9 ms
95th: 491.1 ms 488.1 ms 488.2 ms 489.6 ms
200
---
Count: 79208 (96%) 64320 (96%) 13663 (96%) 1225 (96%)
Mean: 376.2 ms 381.9 ms 374.7 ms 378.1 ms
95th: 487.6 ms 489.0 ms 487.2 ms 490.5 ms
404
---
Count: 2467 (3%) 2002 (3%) 430 (3%) 35 (3%)
Mean: 371.4 ms 371.0 ms 377.2 ms 358.9 ms
95th: 487.1 ms 487.1 ms 486.0 ms 480.4 ms
500
---
Count: 853 (1%) 685 (1%) 156 (1%) 12 (1%)
Mean: 371.2 ms 370.4 ms 374.5 ms 374.3 ms
95th: 487.6 ms 487.1 ms 488.2 ms 466.3 ms
Current rate is 10000 requests / second. Enter a new rate or press enter to view status.
Rate?
```
Config
======
Blast is configured by config file, command line flags or environment variables. The `--config` flag specifies the config file to load, and can be `json`, `yaml`, `toml` or anything else that [viper](https://github.com/spf13/viper) can read. If the config flag is omitted, blast searches for `blast-config.xxx` in the current directory, `$HOME/.config/blast/` and `/etc/blast/`.
Environment variables and command line flags override config file options. Environment variables are upper case and prefixed with "BLAST" e.g. `BLAST_PAYLOAD_TEMPLATE`.
Templates
=========
The `payload-template` and `worker-template` options accept values that are rendered using the Go text/template system. Variables of the form `{{ .name }}` or `{{ "name" }}` are replaced with data.
Additionally, several simple functions are available to inject random data which is useful in load testing scenarios:
* `{{ rand_int -5 5 }}` - a random integer between -5 and 5.
* `{{ rand_float -5 5 }}` - a random float between -5 and 5.
* `{{ rand_string 10 }}` - a random string, length 10.
Index ¶
- type Blaster
- func (b *Blaster) ChangeRate(rate float64)
- func (b *Blaster) Command(ctx context.Context) error
- func (b *Blaster) Exit()
- func (b *Blaster) Initialise(ctx context.Context, c Config) error
- func (b *Blaster) LoadConfig() (Config, error)
- func (b *Blaster) LoadLogs(r io.Reader) error
- func (b *Blaster) PrintStatus(writer io.Writer)
- func (b *Blaster) ReadHeaders() error
- func (b *Blaster) RegisterWorkerType(key string, workerFunc func() Worker)
- func (b *Blaster) SetData(r io.Reader)
- func (b *Blaster) SetInput(r io.Reader)
- func (b *Blaster) SetLog(w io.Writer)
- func (b *Blaster) SetOutput(w io.Writer)
- func (b *Blaster) SetPayloadTemplate(t map[string]interface{}) error
- func (b *Blaster) SetTimeout(timeout time.Duration)
- func (b *Blaster) SetWorker(wf func() Worker)
- func (b *Blaster) SetWorkerTemplate(t map[string]interface{}) error
- func (b *Blaster) Start(ctx context.Context) (Stats, error)
- func (b *Blaster) Stats() Stats
- func (b *Blaster) WriteLogHeaders() error
- type Config
- type ExampleWorker
- type Segment
- type Starter
- type Stats
- type Status
- type Stopper
- type Total
- type Worker
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Blaster ¶
type Blaster struct {
// Quiet disables the status output.
Quiet bool
// Resume sets the resume option. See Config.Resume for more details.
Resume bool
// Rate sets the initial sending rate. Do not change this during a run - use the ChangeRate method instead. See Config.Resume for more details.
Rate float64
// Workers sets the number of workers. See Config.Workers for more details.
Workers int
// LogData sets the data fields to be logged. See Config.LogData for more details.
LogData []string
// LogOutput sets the output fields to be logged. See Config.LogOutput for more details.
LogOutput []string
// Headers sets the data headers. See Config.Headers for more details.
Headers []string
// PayloadVariants sets the payload variants. See Config.PayloadVariants for more details.
PayloadVariants []map[string]string
// WorkerVariants sets the worker variants. See Config.WorkerVariants for more details.
WorkerVariants []map[string]string
// contains filtered or unexported fields
}
Blaster provides the back-end blast: a simple tool for API load testing and batch jobs. Use the New function to create a Blaster with default values.
func New ¶
func New(ctx context.Context, cancel context.CancelFunc) *Blaster
New creates a new Blaster with defaults.
func (*Blaster) ChangeRate ¶
ChangeRate changes the sending rate during execution.
func (*Blaster) Command ¶
Command processes command line flags, loads the config and starts the blast run.
func (*Blaster) Exit ¶
func (b *Blaster) Exit()
Exit cancels any goroutines that are still processing, and closes all files.
func (*Blaster) Initialise ¶
Initialise configures the Blaster with config options in a provided Config
func (*Blaster) LoadConfig ¶
LoadConfig parses command line flags and loads a config file from disk. A Config is returned which may be used with the Initialise method to complete configuration.
func (*Blaster) LoadLogs ¶
LoadLogs loads the logs from a previous run, and stores successfully completed items so they can be skipped in the current run.
func (*Blaster) PrintStatus ¶
PrintStatus prints the status message to the output writer
func (*Blaster) ReadHeaders ¶
ReadHeaders reads one row from the data source and stores that in Headers
func (*Blaster) RegisterWorkerType ¶
RegisterWorkerType registers a new worker function that can be referenced in config file by the worker-type string field.
func (*Blaster) SetData ¶
SetData sets the CSV data source. If the provided io.Reader also satisfies io.Closer it will be closed on exit.
func (*Blaster) SetInput ¶
SetInput sets the rate adjustment reader, and allows testing rate adjustments. The Command method sets this to os.Stdin for interactive command line usage.
func (*Blaster) SetLog ¶
SetLog sets the log output. If the provided writer also satisfies io.Closer, it will be closed on exit.
func (*Blaster) SetOutput ¶
SetOutput sets the summary output writer, and allows the output to be redirected. The Command method sets this to os.Stdout for command line usage.
func (*Blaster) SetPayloadTemplate ¶
SetPayloadTemplate sets the payload template. See Config.PayloadTemplate for more details.
func (*Blaster) SetTimeout ¶
SetTimeout sets the timeout. See Config.Timeout for more details.
func (*Blaster) SetWorker ¶
SetWorker sets the worker creation function. See httpworker for a simple example.
func (*Blaster) SetWorkerTemplate ¶
SetWorkerTemplate sets the worker template. See Config.WorkerTemplate for more details.
func (*Blaster) Start ¶
Start starts the blast run without processing any config.
Example (BatchJob) ¶
package main
import (
"context"
"fmt"
"strings"
"github.com/dave/blast/blaster"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
b := blaster.New(ctx, cancel)
defer b.Exit()
b.SetWorker(func() blaster.Worker {
return &blaster.ExampleWorker{
SendFunc: func(ctx context.Context, self *blaster.ExampleWorker, in map[string]interface{}) (map[string]interface{}, error) {
return map[string]interface{}{"status": 200}, nil
},
}
})
b.Headers = []string{"header"}
b.SetData(strings.NewReader("foo\nbar"))
stats, err := b.Start(ctx)
if err != nil {
fmt.Println(err.Error())
return
}
fmt.Printf("Success == 2: %v\n", stats.All.Summary.Success == 2)
fmt.Printf("Fail == 0: %v", stats.All.Summary.Fail == 0)
}
Output: Success == 2: true Fail == 0: true
Example (LoadTest) ¶
package main
import (
"context"
"fmt"
"time"
"sync"
"github.com/dave/blast/blaster"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
b := blaster.New(ctx, cancel)
defer b.Exit()
b.SetWorker(func() blaster.Worker {
return &blaster.ExampleWorker{
SendFunc: func(ctx context.Context, self *blaster.ExampleWorker, in map[string]interface{}) (map[string]interface{}, error) {
return map[string]interface{}{"status": 200}, nil
},
}
})
b.Rate = 1000
wg := &sync.WaitGroup{}
wg.Add(1)
go func() {
stats, err := b.Start(ctx)
if err != nil {
fmt.Println(err.Error())
return
}
fmt.Printf("Success > 10: %v\n", stats.All.Summary.Success > 10)
fmt.Printf("Fail == 0: %v", stats.All.Summary.Fail == 0)
wg.Done()
}()
<-time.After(time.Millisecond * 100)
b.Exit()
wg.Wait()
}
Output: Success > 10: true Fail == 0: true
func (*Blaster) Stats ¶
Stats returns a snapshot of the metrics (as is printed during interactive execution).
func (*Blaster) WriteLogHeaders ¶
WriteLogHeaders writes the log headers to the log writer.
type Config ¶
type Config struct {
// Data sets the the data file to load. If none is specified, the worker will be called repeatedly until interrupted (useful for load testing). Load a local file or stream directly from a GCS bucket with `gs://{bucket}/{filename}.csv`. Data should be in csv format, and if `headers` is not specified the first record will be used as the headers. If a newline character is found, this string is read as the data.
Data string `mapstructure:"data" json:"data"`
// Log sets the filename of the log file to create / append to.
Log string `mapstructure:"log" json:"log"`
// Resume instructs the tool to load the log file and skip previously successful items. Failed items will be retried.
Resume bool `mapstructure:"resume" json:"resume"`
// Rate sets the initial rate in requests per second. Simply enter a new rate during execution to adjust this. (Default: 10 requests / second).
Rate float64 `mapstructure:"rate" json:"rate"`
// Workers sets the number of concurrent workers. (Default: 10 workers).
Workers int `mapstructure:"workers" json:"workers"`
// WorkerType sets the selected worker type. Register new worker types with the `RegisterWorkerType` method.
WorkerType string `mapstructure:"worker-type" json:"worker-type"`
// PayloadTemplate sets the template that is rendered and passed to the worker `Send` method. When setting this by command line flag or environment variable, use a json encoded string.
PayloadTemplate map[string]interface{} `mapstructure:"payload-template" json:"payload-template"`
// Timeout sets the deadline in the context passed to the worker. Workers must respect this the context cancellation. We exit with an error if any worker is processing for timeout + 1 second. (Default: 1 second).
Timeout int `mapstructure:"timeout" json:"timeout"`
// LogData sets an array of data fields to include in the output log. When setting this by command line flag or environment variable, use a json encoded string.
LogData []string `mapstructure:"log-data" json:"log-data"`
// LogOutput sets an array of worker response fields to include in the output log. When setting this by command line flag or environment variable, use a json encoded string.
LogOutput []string `mapstructure:"log-output" json:"log-output"`
// PayloadVariants sets an array of maps that will cause each data item to be repeated with the provided data. When setting this by command line flag or environment variable, use a json encoded string.
PayloadVariants []map[string]string `mapstructure:"payload-variants" json:"payload-variants"`
// WorkerVariants sets an array of maps that will cause each worker to be initialised with different data. When setting this by command line flag or environment variable, use a json encoded string.
WorkerVariants []map[string]string `mapstructure:"worker-variants" json:"worker-variants"`
// WorkerTemplate sets a template to render and pass to the worker `Start` or `Stop` methods if the worker satisfies the `Starter` or `Stopper` interfaces. Use with `worker-variants` to configure several workers differently to spread load. When setting this by command line flag or environment variable, use a json encoded string.
WorkerTemplate map[string]interface{} `mapstructure:"worker-template" json:"worker-template"`
// Headers sets the data file headers. If omitted, the first record of the csv data source is used. When setting this by command line flag or environment variable, use a json encoded string.
Headers []string `mapstructure:"headers" json:"headers"`
// Quiet instructs the tool to prevent interactive features. No summary is printed during operation and the rate cannot be changed interactively.
Quiet bool `mapstructure:"quiet" json:"quiet"`
}
Config provides all the standard config options. Use the Initialise method to configure with a provided Config.
type ExampleWorker ¶
type ExampleWorker struct {
SendFunc func(ctx context.Context, self *ExampleWorker, in map[string]interface{}) (map[string]interface{}, error)
StartFunc func(ctx context.Context, self *ExampleWorker, payload map[string]interface{}) error
StopFunc func(ctx context.Context, self *ExampleWorker, payload map[string]interface{}) error
Local map[string]interface{}
}
ExampleWorker facilitates code examples by satisfying the Worker, Starter and Stopper interfaces with provided functions.
func (*ExampleWorker) Send ¶
func (e *ExampleWorker) Send(ctx context.Context, in map[string]interface{}) (map[string]interface{}, error)
Send satisfies the Worker interface.
type Segment ¶
type Segment struct {
DesiredRate float64
ActualRate float64
AverageConcurrency float64
Duration time.Duration
Summary *Total
Status []*Status
}
Segment is a rate segment - a new segment is created each time the rate is changed.
type Starter ¶
Starter and Stopper are interfaces a worker can optionally satisfy to provide initialization or finalization logic. See `httpworker` and `dummyworker` for simple examples.
type Stats ¶
type Stats struct {
ConcurrencyCurrent int
ConcurrencyMaximum int
Skipped int64
All *Segment
Segments []*Segment
}
Stats is a snapshot of the metrics (as is printed during interactive execution).
type Status ¶
type Status struct {
Status string
Count int64
Fraction float64
Mean time.Duration
NinetyFifth time.Duration
}
Status is a summary of all requests that returned a specific status
type Stopper ¶
Stopper is an interface a worker can optionally satisfy to provide finalization logic.
type Total ¶
type Total struct {
Started int64
Finished int64
Success int64
Fail int64
Mean time.Duration
NinetyFifth time.Duration
}
Total is the summary of all requests in this segment
type Worker ¶
type Worker interface {
Send(ctx context.Context, payload map[string]interface{}) (response map[string]interface{}, err error)
}
Worker is an interface that allows blast to easily be extended to support any protocol. See `main.go` for an example of how to build a command with your custom worker type.