Skip to content

insolar/loaderbot

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

52 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Loaderbot

Minimalistic load tool

How to use

Implement attacker, example with one client sharing connections

package attackers

import (
	"context"
	"net/http"
    "io"
    "io/ioutil"

	"github.com/insolar/loaderbot"
)

func init() {
	loaderbot.RegisterAttacker("HTTPAttackerExample", &AttackerExample{})
}

type AttackerExample struct {
	*loaderbot.Runner
	client *http.Client
}

func (a *AttackerExample) Clone(r *loaderbot.Runner) loaderbot.Attack {
	return &AttackerExample{Runner: r}
}

func (a *AttackerExample) Setup(c loaderbot.RunnerConfig) error {
	a.client = loaderbot.NewLoggingHTTPClient(c.DumpTransport, 10)
	return nil
}

func (a *AttackerExample) Do(ctx context.Context) loaderbot.DoResult {
	req, _ := http.NewRequestWithContext(ctx, "GET", a.Cfg.TargetUrl, nil)
    	res, err := a.HTTPClient.Do(req)
    	if res != nil {
    		if _, err = io.Copy(ioutil.Discard, res.Body); err != nil {
    			return loaderbot.DoResult{
    				RequestLabel: a.Name,
    				Error:        err.Error(),
    			}
    		}
    		defer res.Body.Close()
    	}
    	if err != nil {
    		return loaderbot.DoResult{
    			RequestLabel: a.Name,
    			Error:        err.Error(),
    		}
    	}
    	return loaderbot.DoResult{RequestLabel: a.Name}
}

func (a *AttackerExample) Teardown() error {
	return nil
}

Alternatively Fasthttp can be used in Do():

func (a *FastHTTPAttackerExample) Do(_ context.Context) DoResult {
	req := fasthttp.AcquireRequest()
	req.SetRequestURI(a.Cfg.TargetUrl)
	resp := fasthttp.AcquireResponse()
	defer fasthttp.ReleaseRequest(req)
	defer fasthttp.ReleaseResponse(resp)
	err := a.FastHTTPClient.Do(req, resp)
	if resp.StatusCode() >= 400 {
		return DoResult{
			Error: "request failed",
		}
	}
	if err != nil {
		return DoResult{
			Error: err.Error(),
		}
	}
	return DoResult{RequestLabel: a.Name}
}

Run test with constant amount of attackers (clients) using BoundRPS mode

cfg := &loaderbot.RunnerConfig{
		TargetUrl:       "https://clients5.google.com/pagead/drt/dn/",
		Name:            "abc",
		SystemMode:      loaderbot.BoundRPS,
		Attackers:       100,
		AttackerTimeout: 5,
		StartRPS:        100,
		StepDurationSec: 10,
		StepRPS:         300,
		TestTimeSec:     200,
	}
lt := loaderbot.NewRunner(cfg, &loaderbot.HTTPAttackerExample{}, nil)
maxRPS, _ := lt.Run()

Another option is to use BoundRPSAutoscale to add attackers if target rps isn't met during the test, which is more realistic in "open world" systems like search engines

cfg := &loaderbot.RunnerConfig{
		TargetUrl:       "https://clients5.google.com/pagead/drt/dn/",
		Name:            "abc",
		SystemMode:      loaderbot.BoundRPSAutoscale,
		Attackers:       100,
		AttackerTimeout: 5,
		StartRPS:        100,
		StepDurationSec: 10,
		StepRPS:         300,
		TestTimeSec:     200,
	}
lt := loaderbot.NewRunner(cfg, &loaderbot.HTTPAttackerExample{}, nil)
maxRPS, _ := lt.Run()

Or use UnboundRPS to check system scalability

r := NewRunner(&loaderbot.RunnerConfig{
		TargetUrl:       "http://127.0.0.1:9031/json_body",
		Name:            "nginx_test",
		SystemMode:      loaderbot.UnboundRPS,
		Attackers:       20,
		AttackerTimeout: 25,
		TestTimeSec:     30,
		SuccessRatio:    0.95,
		Prometheus:      &Prometheus{Enable: true},
	}, &HTTPAttackerExample{}, nil)
_, _ = r.Run(context.TODO())

see more examples

Config options

// RunnerConfig runner configuration
type RunnerConfig struct {
	// TargetUrl target base url
	TargetUrl string
	// Name of a runner instance
	Name string
	// InstanceType attacker type instance, used only in cluster mode
	InstanceType string
	// SystemMode BoundRPS
	// BoundRPS:
	// if application under test is a private system sync runner attackers will wait for response
	// in case your system is private and you know how many sync clients can act
	// BoundRPSAutoscale:
	// try to scale attackers when all attackers are blocked
	// UnboundRPS:
	// attack as fast as we can with N attackers
	SystemMode SystemMode
	// Attackers constant amount of attackers,
	Attackers int
	// AttackersScaleFactor how much attackers to add when rps is not met, default is 100
	AttackersScaleAmount int
	// AttackersScaleThreshold scale if current rate is less than target rate * threshold,
	// interval of values = [0, 1], default is 0.90
	AttackersScaleThreshold float64
	// AttackerTimeout timeout of attacker
	AttackerTimeout int
	// StartRPS initial requests per seconds rate
	StartRPS int
	// StepDurationSec duration of step in which rps is increased by StepRPS
	StepDurationSec int
	// StepRPS amount of requests per second which will be added in next step,
	// if StepRPS = 0 rate is constant, default StepDurationSec is 30 sec is applied,
	// just to keep 30s aggregation metrics
	StepRPS int
	// TestTimeSec test timeout
	TestTimeSec int
	// WaitBeforeSec time to wait before start in case we didn't know start criteria
	WaitBeforeSec int
	// Dumptransport dumps http requests to stdout
	DumpTransport bool
	// GoroutinesDump dumps goroutines stack for debug purposes
	GoroutinesDump bool
	// SuccessRatio to fail when below
	SuccessRatio float64
	// Metadata all other data required for test setup
	Metadata map[string]interface{}
	// LogLevel debug|info, etc.
	LogLevel string
	// LogEncoding json|console
	LogEncoding string
	// Reporting options, csv/png/stream
	ReportOptions *ReportOptions
	// ClusterOptions
	ClusterOptions *ClusterOptions
	// Prometheus config
	Prometheus *Prometheus
}

Development

make test
make lint

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages