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.