import "github.com/bojand/ghz/runner"
calldata.go config.go counter.go data.go logger.go options.go reason.go reporter.go requester.go run.go stats_handler.go worker.go
const ( // ReasonNormalEnd indicates a normal end to the run ReasonNormalEnd = StopReason("normal") // ReasonCancel indicates end due to cancellation ReasonCancel = StopReason("cancel") // ReasonTimeout indicates run ended due to Z parameter timeout ReasonTimeout = StopReason("timeout") )
const ScheduleConst = "const"
ScheduleConst is a constant load schedule
const ScheduleLine = "line"
ScheduleLine is the line load schedule
const ScheduleStep = "step"
ScheduleStep is the step load schedule
ErrEndStream is a signal from message providers that worker should close the stream It should not be used for erronous states
ErrLastMessage is a signal from message providers that the returned payload is the last one of the stream This is optional but encouraged for optimized performance Message payload returned along with this error must be valid and may not be nil
LoadConfig loads the config from a file
type BinaryDataFunc func(mtd *desc.MethodDescriptor, callData *CallData) []byte
BinaryDataFunc is a function that can be used for provide binary data for request programatically. MethodDescriptor of the call is passed to the data function. CallData for the request is passed and can be used to access worker id, request number, etc...
type Bucket struct { // The Mark for histogram bucket in seconds Mark float64 `json:"mark"` // The count in the bucket Count int `json:"count"` // The frequency of results in the bucket as a decimal percentage Frequency float64 `json:"frequency"` }
Bucket holds histogram data
type CallData struct { WorkerID string // unique worker ID RequestNumber int64 // unique incremented request number for each request FullyQualifiedName string // fully-qualified name of the method call MethodName string // shorter call method name ServiceName string // the service name InputName string // name of the input message type OutputName string // name of the output message type IsClientStreaming bool // whether this call is client streaming IsServerStreaming bool // whether this call is server streaming Timestamp string // timestamp of the call in RFC3339 format TimestampUnix int64 // timestamp of the call as unix time in seconds TimestampUnixMilli int64 // timestamp of the call as unix time in milliseconds TimestampUnixNano int64 // timestamp of the call as unix time in nanoseconds UUID string // generated UUIDv4 for each call // contains filtered or unexported fields }
CallData represents contextualized data available for templating
ExecuteData applies the call data's parsed template and data string and returns the resulting buffer
Regenerate generates a new instance of call data from this parent instance The dynamic data like timestamps and UUIDs are re-filled
type Config struct { Proto string `json:"proto" toml:"proto" yaml:"proto"` Protoset string `json:"protoset" toml:"protoset" yaml:"protoset"` Call string `json:"call" toml:"call" yaml:"call"` RootCert string `json:"cacert" toml:"cacert" yaml:"cacert"` Cert string `json:"cert" toml:"cert" yaml:"cert"` Key string `json:"key" toml:"key" yaml:"key"` CountErrors bool `json:"count-errors" toml:"count-errors" yaml:"count-errors"` SkipTLSVerify bool `json:"skipTLS" toml:"skipTLS" yaml:"skipTLS"` SkipFirst uint `json:"skipFirst" toml:"skipFirst" yaml:"skipFirst"` CName string `json:"cname" toml:"cname" yaml:"cname"` Authority string `json:"authority" toml:"authority" yaml:"authority"` Insecure bool `json:"insecure,omitempty" toml:"insecure,omitempty" yaml:"insecure,omitempty"` N uint `json:"total" toml:"total" yaml:"total" default:"200"` Async bool `json:"async,omitempty" toml:"async,omitempty" yaml:"async,omitempty"` C uint `json:"concurrency" toml:"concurrency" yaml:"concurrency" default:"50"` CSchedule string `json:"concurrency-schedule" toml:"concurrency-schedule" yaml:"concurrency-schedule" default:"const"` CStart uint `json:"concurrency-start" toml:"concurrency-start" yaml:"concurrency-start" default:"1"` CEnd uint `json:"concurrency-end" toml:"concurrency-end" yaml:"concurrency-end" default:"0"` CStep int `json:"concurrency-step" toml:"concurrency-step" yaml:"concurrency-step" default:"0"` CStepDuration Duration `json:"concurrency-step-duration" toml:"concurrency-step-duration" yaml:"concurrency-step-duration" default:"0"` CMaxDuration Duration `json:"concurrency-max-duration" toml:"concurrency-max-duration" yaml:"concurrency-max-duration" default:"0"` Connections uint `json:"connections" toml:"connections" yaml:"connections" default:"1"` RPS uint `json:"rps" toml:"rps" yaml:"rps"` Z Duration `json:"duration" toml:"duration" yaml:"duration"` ZStop string `json:"duration-stop" toml:"duration-stop" yaml:"duration-stop" default:"close"` X Duration `json:"max-duration" toml:"max-duration" yaml:"max-duration"` Timeout Duration `json:"timeout" toml:"timeout" yaml:"timeout" default:"20s"` Data interface{} `json:"data,omitempty" toml:"data,omitempty" yaml:"data,omitempty"` DataPath string `json:"data-file" toml:"data-file" yaml:"data-file"` BinData []byte `json:"-" toml:"-" yaml:"-"` BinDataPath string `json:"binary-file" toml:"binary-file" yaml:"binary-file"` Metadata map[string]string `json:"metadata,omitempty" toml:"metadata,omitempty" yaml:"metadata,omitempty"` MetadataPath string `json:"metadata-file" toml:"metadata-file" yaml:"metadata-file"` SI Duration `json:"stream-interval" toml:"stream-interval" yaml:"stream-interval"` StreamCallDuration Duration `json:"stream-call-duration" toml:"stream-call-duration" yaml:"stream-call-duration"` StreamCallCount uint `json:"stream-call-count" toml:"stream-call-count" yaml:"stream-call-count"` StreamDynamicMessages bool `json:"stream-dynamic-messages" toml:"stream-dynamic-messages" yaml:"stream-dynamic-messages"` Output string `json:"output" toml:"output" yaml:"output"` Format string `json:"format" toml:"format" yaml:"format" default:"summary"` DialTimeout Duration `json:"connect-timeout" toml:"connect-timeout" yaml:"connect-timeout" default:"10s"` KeepaliveTime Duration `json:"keepalive" toml:"keepalive" yaml:"keepalive"` CPUs uint `json:"cpus" toml:"cpus" yaml:"cpus"` ImportPaths []string `json:"import-paths,omitempty" toml:"import-paths,omitempty" yaml:"import-paths,omitempty"` Name string `json:"name,omitempty" toml:"name,omitempty" yaml:"name,omitempty"` Tags map[string]string `json:"tags,omitempty" toml:"tags,omitempty" yaml:"tags,omitempty"` ReflectMetadata map[string]string `json:"reflect-metadata,omitempty" toml:"reflect-metadata,omitempty" yaml:"reflect-metadata,omitempty"` Debug string `json:"debug,omitempty" toml:"debug,omitempty" yaml:"debug,omitempty"` Host string `json:"host" toml:"host" yaml:"host"` EnableCompression bool `json:"enable-compression,omitempty" toml:"enable-compression,omitempty" yaml:"enable-compression,omitempty"` LoadSchedule string `json:"load-schedule" toml:"load-schedule" yaml:"load-schedule" default:"const"` LoadStart uint `json:"load-start" toml:"load-start" yaml:"load-start"` LoadEnd uint `json:"load-end" toml:"load-end" yaml:"load-end"` LoadStep int `json:"load-step" toml:"load-step" yaml:"load-step"` LoadStepDuration Duration `json:"load-step-duration" toml:"load-step-duration" yaml:"load-step-duration"` LoadMaxDuration Duration `json:"load-max-duration" toml:"load-max-duration" yaml:"load-max-duration"` }
Config for the run. TODO fix casing and consistency.
type Counter struct {
// contains filtered or unexported fields
}
Counter is an implementation of the request counter
Get retrieves the current count
Inc increases the current count
DataProviderFunc is the interface for providing data for calls For unary and server streaming calls it should return an array with a single element For client and bidi streaming calls it should return an array of messages to be used
Duration is our duration with TOML support
MarshalJSON implements encoding JSONMarshaler
MarshalText implements encoding.TextMarshaler
UnmarshalJSON is our custom unmarshaller with JSON support
UnmarshalText is our custom unmarshaller with TOML support
type LatencyDistribution struct { Percentage int `json:"percentage"` Latency time.Duration `json:"latency"` }
LatencyDistribution holds latency distribution data
type Logger interface { Debug(args ...interface{}) Debugf(template string, args ...interface{}) Debugw(msg string, keysAndValues ...interface{}) Error(args ...interface{}) Errorf(template string, args ...interface{}) Errorw(msg string, keysAndValues ...interface{}) }
Logger interface is the common logger interface for all of web
MetadataProviderFunc is the interface for providing metadadata for calls
Option controls some aspect of run
WithAsync specifies the async option
WithAuthority specifies the value to be used as the :authority pseudo-header. This only works with WithInsecure option.
WithBinaryData specifies the binary data
msg := &helloworld.HelloRequest{} msg.Name = "bob" binData, _ := proto.Marshal(msg) WithBinaryData(binData)
WithBinaryDataFromFile specifies the binary data
WithBinaryDataFromFile("request_data.bin")
WithBinaryDataFunc specifies the binary data func which will be called on each request
WithBinaryDataFunc(changeFunc)
WithCPUs specifies the number of CPU's to be used
WithCPUs(4)
WithCertificate specifies the certificate options for the run
WithCertificate("client.crt", "client.key")
WithClientLoadBalancing specifies the LB strategy to use The strategies has to be self written and pre defined
WithConcurrency specifies the C (number of concurrent requests) option
WithConcurrency(20)
WithConcurrencyDuration specifies the total concurrency adjustment duration
WithConcurrencyEnd specifies the concurrency end value for line or step schedule
WithConcurrencyEnd(25)
WithConcurrencySchedule specifies the concurrency adjustment schedule
WithConcurrencySchedule("const")
WithConcurrencyStart specifies the concurrency start for line or step schedule
WithConcurrencyStart(5)
WithConcurrencyStep specifies the concurrency step value or slope
WithConcurrencyStep(5)
WithConcurrencyStepDuration specifies the concurrency step duration for step schedule
WithConfig uses the configuration to populate the RunConfig See also: WithConfigFromFile, WithConfigFromReader
WithConfigFromFile uses a configuration JSON file to populate the RunConfig
WithConfigFromFile("config.json")
WithConfigFromReader uses a reader containing JSON data to populate the RunConfig See also: WithConfigFromFile
WithConnections specifies the number of gRPC connections to use
WithConnections(5)
WithCountErrors is the count errors option
WithData specifies data as generic data that can be serailized to JSON
WithDataFromFile loads JSON data from file
WithDataFromFile("data.json")
WithDataFromJSON loads JSON data from string
WithDataFromJSON(`{"name":"bob"}`)
WithDataFromReader loads JSON data from reader
file, _ := os.Open("data.json") WithDataFromReader(file)
func WithDataProvider(fn DataProviderFunc) Option
WithDataProvider provides custom data provider
WithDataProvider(func(*CallData) ([]*dynamic.Message, error) { protoMsg := &helloworld.HelloRequest{Name: "Bob"} dynamicMsg, err := dynamic.AsDynamicMessage(protoMsg) if err != nil { return nil, err } return []*dynamic.Message{dynamicMsg}, nil }),
WithDialTimeout specifies the initial connection dial timeout
WithDialTimeout(time.Duration(20*time.Second))
WithDurationStopAction specifies how run duration (Z) timeout is handled Possible options are "close", "ignore", and "wait"
WithDurationStopAction("ignore")
WithEnableCompression specifies that requests should be done using gzip Compressor
WithEnableCompression(true)
WithInsecure specifies that this run should be done using insecure mode
WithInsecure(true)
WithKeepalive specifies the keepalive timeout
WithKeepalive(time.Duration(1*time.Minute))
WithLoadDuration specifies the load duration
WithLoadEnd specifies the load end
WithLoadEnd(25)
WithLoadSchedule specifies the load schedule
WithLoadSchedule("const")
WithLoadStart specifies the load start
WithLoadStart(5)
WithLoadStep specifies the load step
WithLoadStep(5)
WithLoadStepDuration specifies the load step duration for step schedule
WithLogger specifies the logging option
WithMetadata specifies the metadata to be used as a map
md := make(map[string]string) md["token"] = "foobar" md["request-id"] = "123" WithMetadata(&md)
WithMetadataFromFile loads JSON metadata from file
WithMetadataFromJSON("metadata.json")
WithMetadataFromJSON specifies the metadata to be read from JSON string
WithMetadataFromJSON(`{"request-id":"123"}`)
func WithMetadataProvider(fn MetadataProviderFunc) Option
WithMetadataProvider provides custom metadata provider
WithMetadataProvider(ctd *CallData) (*metadata.MD, error) { return &metadata.MD{"token": []string{"secret"}}, nil }),
WithName sets the name of the test run
WithName("greeter service test")
WithPacer specified the custom pacer to use
WithProtoFile specified proto file path and optionally import paths We will automatically add the proto file path's directory and the current directory
WithProtoFile("greeter.proto", []string{"/home/protos"})
WithProtoset specified protoset file path
WithProtoset("bundle.protoset")
WithRPS specifies the RPS (requests per second) limit option
WithRPS(10)
WithReflectionMetadata specifies the metadata to be used as a map
md := make(map[string]string) md["token"] = "foobar" md["request-id"] = "123" WithReflectionMetadata(&md)
WithRootCertificate specifies the root certificate options for the run
WithRootCertificate("ca.crt")
WithRunDuration specifies the Z (total test duration) option
WithRunDuration(time.Duration(2*time.Minute))
WithServerNameOverride specifies the certificate options for the run
WithSkipFirst is the skipFirst option
WithSkipTLSVerify skip client side TLS verification of server certificate
WithStreamCallCount sets the stream close count
WithStreamCallDuration sets the maximum stream call duration at which point the client will close the stream
WithStreamDynamicMessages sets the stream dynamic message generation
WithStreamInterval sets the stream interval
func WithStreamMessageProvider(fn StreamMessageProviderFunc) Option
WithStreamMessageProvider sets custom stream message provider
WithStreamMessageProvider(func(cd *CallData) (*dynamic.Message, error) { protoMsg := &helloworld.HelloRequest{Name: cd.WorkerID + ": " + strconv.FormatInt(cd.RequestNumber, 10)} dynamicMsg, err := dynamic.AsDynamicMessage(protoMsg) if err != nil { return nil, err } callCounter++ if callCounter == 5 { err = ErrLastMessage } return dynamicMsg, err }),
func WithStreamRecvMsgIntercept(fn StreamRecvMsgInterceptFunc) Option
WithStreamRecvMsgIntercept specified the stream receive intercept function
WithStreamRecvMsgIntercept(func(msg *dynamic.Message, err error) error { if err == nil && msg != nil { reply := &helloworld.HelloReply{} convertErr := msg.ConvertTo(reply) if convertErr == nil { if reply.GetMessage() == "Hello bar" { return ErrEndStream } } } return nil })
WithTags specifies the user defined tags as a map
tags := make(map[string]string) tags["env"] = "staging" tags["created by"] = "joe developer" WithTags(&tags)
WithTemplateFuncs adds additional template functions
WithTimeout specifies the timeout for each request
WithTimeout(time.Duration(20*time.Second))
WithTotalRequests specifies the N (number of total requests) setting
WithTotalRequests(1000)
func WithWorkerTicker(ticker load.WorkerTicker) Option
WithWorkerTicker specified the custom worker ticker to use
type Options struct { Call string `json:"call,omitempty"` Host string `json:"host,omitempty"` Proto string `json:"proto,omitempty"` Protoset string `json:"protoset,omitempty"` ImportPaths []string `json:"import-paths,omitempty"` EnableCompression bool `json:"enable-compression,omitempty"` CACert string `json:"cacert,omitempty"` Cert string `json:"cert,omitempty"` Key string `json:"key,omitempty"` CName string `json:"cname,omitempty"` SkipTLS bool `json:"skipTLS,omitempty"` Insecure bool `json:"insecure"` Authority string `json:"authority,omitempty"` RPS uint `json:"rps,omitempty"` LoadSchedule string `json:"load-schedule"` LoadStart uint `json:"load-start"` LoadEnd uint `json:"load-end"` LoadStep int `json:"load-step"` LoadStepDuration time.Duration `json:"load-step-duration"` LoadMaxDuration time.Duration `json:"load-max-duration"` Concurrency uint `json:"concurrency,omitempty"` CSchedule string `json:"concurrency-schedule"` CStart uint `json:"concurrency-start"` CEnd uint `json:"concurrency-end"` CStep int `json:"concurrency-step"` CStepDuration time.Duration `json:"concurrency-step-duration"` CMaxDuration time.Duration `json:"concurrency-max-duration"` Total uint `json:"total,omitempty"` Async bool `json:"async,omitempty"` Connections uint `json:"connections,omitempty"` Duration time.Duration `json:"duration,omitempty"` Timeout time.Duration `json:"timeout,omitempty"` DialTimeout time.Duration `json:"dial-timeout,omitempty"` KeepaliveTime time.Duration `json:"keepalive,omitempty"` Data interface{} `json:"data,omitempty"` Binary bool `json:"binary"` Metadata *map[string]string `json:"metadata,omitempty"` CPUs int `json:"CPUs"` Name string `json:"name,omitempty"` SkipFirst uint `json:"skipFirst,omitempty"` CountErrors bool `json:"count-errors,omitempty"` }
Options represents the request options TODO fix casing and consistency
type Report struct { Name string `json:"name,omitempty"` EndReason StopReason `json:"endReason,omitempty"` Options Options `json:"options,omitempty"` Date time.Time `json:"date"` Count uint64 `json:"count"` Total time.Duration `json:"total"` Average time.Duration `json:"average"` Fastest time.Duration `json:"fastest"` Slowest time.Duration `json:"slowest"` Rps float64 `json:"rps"` ErrorDist map[string]int `json:"errorDistribution"` StatusCodeDist map[string]int `json:"statusCodeDistribution"` LatencyDistribution []LatencyDistribution `json:"latencyDistribution"` Histogram []Bucket `json:"histogram"` Details []ResultDetail `json:"details"` Tags map[string]string `json:"tags,omitempty"` }
Report holds the data for the full test
Run executes the test
report, err := runner.Run( "helloworld.Greeter.SayHello", "localhost:50051", WithProtoFile("greeter.proto", []string{}), WithDataFromFile("data.json"), WithInsecure(true), )
ExampleRun demonstrates how to use runner package to perform a gRPC load test programmatically. We use the printer package to print the report in pretty JSON format.
Code:
report, err := runner.Run( "helloworld.Greeter.SayHello", "localhost:50051", runner.WithProtoFile("greeter.proto", []string{}), runner.WithDataFromFile("data.json"), runner.WithInsecure(true), ) if err != nil { fmt.Println(err.Error()) os.Exit(1) } printer := printer.ReportPrinter{ Out: os.Stdout, Report: report, } printer.Print("pretty")
MarshalJSON is custom marshal for report to properly format the date
type Reporter struct {
// contains filtered or unexported fields
}
Reporter gathers all the results
Finalize all the gathered data into a final report
Run runs the reporter
RequestCounter gets the request count
type Requester struct {
// contains filtered or unexported fields
}
Requester is used for doing the requests
NewRequester creates a new requestor from the passed RunConfig
Finish finishes the test run
Run makes all the requests and returns a report of results It blocks until all work is done.
func (b *Requester) Stop(reason StopReason)
Stop stops the test
type ResultDetail struct { Timestamp time.Time `json:"timestamp"` Latency time.Duration `json:"latency"` Error string `json:"error"` Status string `json:"status"` }
ResultDetail data for each result
type RunConfig struct {
// contains filtered or unexported fields
}
RunConfig represents the request Configs
NewConfig creates a new RunConfig from the options passed
StopReason is a reason why the run ended
func ReasonFromString(str string) StopReason
ReasonFromString creates a Status from a string
func (s StopReason) MarshalJSON() ([]byte, error)
MarshalJSON formats a Threshold value into a JSON string
func (s StopReason) String() string
String() is the string representation of threshold
func (s *StopReason) UnmarshalJSON(b []byte) error
UnmarshalJSON prases a Threshold value from JSON string
StreamMessageProviderFunc is the interface for providing a message for every message send in the course of a streaming call
StreamRecvMsgInterceptFunc is an interface for function invoked when we receive a stream message Clients can return ErrEndStream to end the call early
type TickValue struct {
// contains filtered or unexported fields
}
TickValue is the tick value
type Worker struct {
// contains filtered or unexported fields
}
Worker is used for doing a single stream of requests in parallel
Stop stops the worker. It has to be started with Run() again.
Package runner imports 47 packages (graph) and is imported by 2 packages. Updated 2021-01-11. Refresh now. Tools for package owners.