mirror of
https://github.com/americanexpress/baton.git
synced 2025-11-24 06:26:04 +00:00
Added runConfiguration + fixed typo
This commit is contained in:
parent
778d6dc328
commit
c037624bd0
183
baton.go
183
baton.go
@ -2,6 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"github.com/valyala/fasthttp"
|
"github.com/valyala/fasthttp"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@ -23,34 +24,30 @@ var (
|
|||||||
wait = flag.Int("w", 0, "Number of seconds to wait before running test")
|
wait = flag.Int("w", 0, "Number of seconds to wait before running test")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Configuration represents the Baton configuration
|
|
||||||
type Configuration struct {
|
|
||||||
body string
|
|
||||||
concurrency int
|
|
||||||
dataFilePath string
|
|
||||||
duration int
|
|
||||||
ignoreTLS bool
|
|
||||||
method string
|
|
||||||
numberOfRequests int
|
|
||||||
requestsFromFile string
|
|
||||||
suppressOutput bool
|
|
||||||
url string
|
|
||||||
wait int
|
|
||||||
}
|
|
||||||
|
|
||||||
// Baton implements the load tester
|
// Baton implements the load tester
|
||||||
type Baton struct {
|
type Baton struct {
|
||||||
configuration Configuration
|
configuration Configuration
|
||||||
result Result
|
result Result
|
||||||
}
|
}
|
||||||
|
|
||||||
type preloadedRequest struct {
|
type preLoadedRequest struct {
|
||||||
method string // The HTTP method used to send the request
|
method string // The HTTP method used to send the request
|
||||||
url string // The URL to send the request at
|
url string // The URL to send the request at
|
||||||
body string // The body of the request (if appropriate method is selected)
|
body string // The body of the request (if appropriate method is selected)
|
||||||
headers [][]string // Array of two-element key/value pairs of header and value
|
headers [][]string // Array of two-element key/value pairs of header and value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type runConfiguration struct {
|
||||||
|
preLoadedRequestsMode bool
|
||||||
|
timedMode bool
|
||||||
|
preLoadedRequests []preLoadedRequest
|
||||||
|
client *fasthttp.Client
|
||||||
|
requests chan bool
|
||||||
|
results chan HTTPResult
|
||||||
|
done chan bool
|
||||||
|
body string
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
@ -76,93 +73,45 @@ func main() {
|
|||||||
|
|
||||||
func (baton *Baton) run() {
|
func (baton *Baton) run() {
|
||||||
|
|
||||||
logWriter := &logWriter{true}
|
configureLogging(baton.configuration.suppressOutput)
|
||||||
|
|
||||||
if baton.configuration.suppressOutput {
|
err := baton.configuration.validate()
|
||||||
logWriter.Disable()
|
|
||||||
}
|
|
||||||
|
|
||||||
log.SetFlags(0)
|
|
||||||
log.SetOutput(logWriter)
|
|
||||||
|
|
||||||
preloadedRequestsMode := false
|
|
||||||
timedMode := false
|
|
||||||
var preloadedRequests []preloadedRequest
|
|
||||||
var err error
|
|
||||||
|
|
||||||
if baton.configuration.requestsFromFile != "" {
|
|
||||||
preloadedRequests, err = preloadRequestsFromFile(baton.configuration.requestsFromFile)
|
|
||||||
preloadedRequestsMode = true
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Failed to parse requests from file: " + baton.configuration.requestsFromFile)
|
log.Fatalf("Invalid configuration: %v", err)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if baton.configuration.duration != 0 {
|
preparedRunConfiguration, err := prepareRun(baton.configuration)
|
||||||
timedMode = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if baton.configuration.concurrency < 1 || baton.configuration.numberOfRequests == 0 {
|
|
||||||
log.Fatal("Invalid concurrency level or number of requests")
|
|
||||||
}
|
|
||||||
|
|
||||||
client := &fasthttp.Client{}
|
|
||||||
if baton.configuration.ignoreTLS {
|
|
||||||
tlsConfig := &tls.Config{InsecureSkipVerify: true}
|
|
||||||
client = &fasthttp.Client{TLSConfig: tlsConfig}
|
|
||||||
}
|
|
||||||
|
|
||||||
if baton.configuration.dataFilePath != "" {
|
|
||||||
data, err := ioutil.ReadFile(baton.configuration.dataFilePath)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err.Error())
|
log.Fatalf("Error during run preparation: %v", err)
|
||||||
}
|
|
||||||
baton.configuration.body = string(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
if preloadedRequestsMode {
|
|
||||||
log.Printf("Configuring to send requests from file. (Read %d requests)\n", len(preloadedRequests))
|
|
||||||
} else {
|
|
||||||
log.Printf("Configuring to send %s requests to: %s\n", baton.configuration.method, baton.configuration.url)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if baton.configuration.wait > 0 {
|
if baton.configuration.wait > 0 {
|
||||||
time.Sleep(time.Duration(baton.configuration.wait) * time.Second)
|
time.Sleep(time.Duration(baton.configuration.wait) * time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
requests := make(chan bool, baton.configuration.numberOfRequests)
|
|
||||||
results := make(chan HTTPResult, baton.configuration.concurrency)
|
|
||||||
done := make(chan bool, baton.configuration.concurrency)
|
|
||||||
|
|
||||||
log.Println("Generating the requests...")
|
|
||||||
for r := 1; r <= baton.configuration.numberOfRequests; r++ {
|
|
||||||
requests <- true
|
|
||||||
}
|
|
||||||
close(requests)
|
|
||||||
log.Println("Finished generating the requests")
|
|
||||||
log.Println("Sending the requests to the server...")
|
log.Println("Sending the requests to the server...")
|
||||||
|
|
||||||
// Start the timer and kick off the workers
|
// Start the timer and kick off the workers
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
for w := 1; w <= baton.configuration.concurrency; w++ {
|
for w := 1; w <= baton.configuration.concurrency; w++ {
|
||||||
var worker workable
|
var worker workable
|
||||||
if timedMode {
|
if preparedRunConfiguration.timedMode {
|
||||||
worker = newTimedWorker(requests, results, done, float64(baton.configuration.duration))
|
worker = newTimedWorker(preparedRunConfiguration.requests, preparedRunConfiguration.results, preparedRunConfiguration.done, float64(baton.configuration.duration))
|
||||||
} else {
|
} else {
|
||||||
worker = newCountWorker(requests, results, done)
|
worker = newCountWorker(preparedRunConfiguration.requests, preparedRunConfiguration.results, preparedRunConfiguration.done)
|
||||||
}
|
}
|
||||||
worker.setCustomClient(client)
|
worker.setCustomClient(preparedRunConfiguration.client)
|
||||||
if preloadedRequestsMode {
|
if preparedRunConfiguration.preLoadedRequestsMode {
|
||||||
go worker.sendRequests(preloadedRequests)
|
go worker.sendRequests(preparedRunConfiguration.preLoadedRequests)
|
||||||
} else {
|
} else {
|
||||||
request := preloadedRequest{baton.configuration.method, baton.configuration.url, baton.configuration.body, [][]string{}}
|
request := preLoadedRequest{baton.configuration.method, baton.configuration.url, preparedRunConfiguration.body, [][]string{}}
|
||||||
go worker.sendRequest(request)
|
go worker.sendRequest(request)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for all the workers to finish and then stop the timer
|
// Wait for all the workers to finish and then stop the timer
|
||||||
for a := 1; a <= baton.configuration.concurrency; a++ {
|
for a := 1; a <= baton.configuration.concurrency; a++ {
|
||||||
<-done
|
<-preparedRunConfiguration.done
|
||||||
}
|
}
|
||||||
baton.result.timeTaken = time.Since(start)
|
baton.result.timeTaken = time.Since(start)
|
||||||
|
|
||||||
@ -170,7 +119,7 @@ func (baton *Baton) run() {
|
|||||||
log.Println("Processing the results...")
|
log.Println("Processing the results...")
|
||||||
|
|
||||||
for a := 1; a <= baton.configuration.concurrency; a++ {
|
for a := 1; a <= baton.configuration.concurrency; a++ {
|
||||||
result := <-results
|
result := <-preparedRunConfiguration.results
|
||||||
baton.result.httpResult.connectionErrorCount += result.connectionErrorCount
|
baton.result.httpResult.connectionErrorCount += result.connectionErrorCount
|
||||||
baton.result.httpResult.status1xxCount += result.status1xxCount
|
baton.result.httpResult.status1xxCount += result.status1xxCount
|
||||||
baton.result.httpResult.status2xxCount += result.status2xxCount
|
baton.result.httpResult.status2xxCount += result.status2xxCount
|
||||||
@ -183,3 +132,81 @@ func (baton *Baton) run() {
|
|||||||
|
|
||||||
baton.result.requestsPerSecond = int(float64(baton.result.totalRequests)/baton.result.timeTaken.Seconds() + 0.5)
|
baton.result.requestsPerSecond = int(float64(baton.result.totalRequests)/baton.result.timeTaken.Seconds() + 0.5)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func configureLogging(suppressOutput bool) {
|
||||||
|
|
||||||
|
logWriter := &logWriter{true}
|
||||||
|
|
||||||
|
if suppressOutput {
|
||||||
|
logWriter.Disable()
|
||||||
|
}
|
||||||
|
|
||||||
|
log.SetFlags(0)
|
||||||
|
log.SetOutput(logWriter)
|
||||||
|
}
|
||||||
|
|
||||||
|
func prepareRun(configuration Configuration) (runConfiguration, error) {
|
||||||
|
|
||||||
|
preLoadedRequestsMode := false
|
||||||
|
timedMode := false
|
||||||
|
|
||||||
|
var preLoadedRequests []preLoadedRequest
|
||||||
|
|
||||||
|
if configuration.requestsFromFile != "" {
|
||||||
|
var err error
|
||||||
|
preLoadedRequests, err = preLoadRequestsFromFile(configuration.requestsFromFile)
|
||||||
|
preLoadedRequestsMode = true
|
||||||
|
if err != nil {
|
||||||
|
return runConfiguration{}, errors.New("failed to parse requests from file: " + configuration.requestsFromFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if configuration.duration != 0 {
|
||||||
|
timedMode = true
|
||||||
|
}
|
||||||
|
|
||||||
|
client := &fasthttp.Client{}
|
||||||
|
if configuration.ignoreTLS {
|
||||||
|
tlsConfig := &tls.Config{InsecureSkipVerify: true}
|
||||||
|
client = &fasthttp.Client{TLSConfig: tlsConfig}
|
||||||
|
}
|
||||||
|
|
||||||
|
body := configuration.body
|
||||||
|
if configuration.dataFilePath != "" {
|
||||||
|
data, err := ioutil.ReadFile(configuration.dataFilePath)
|
||||||
|
if err != nil {
|
||||||
|
return runConfiguration{}, err
|
||||||
|
}
|
||||||
|
body = string(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
if preLoadedRequestsMode {
|
||||||
|
log.Printf("Configuring to send requests from file. (Read %d requests)\n", len(preLoadedRequests))
|
||||||
|
} else {
|
||||||
|
log.Printf("Configuring to send %s requests to: %s\n", configuration.method, configuration.url)
|
||||||
|
}
|
||||||
|
|
||||||
|
requests := make(chan bool, configuration.numberOfRequests)
|
||||||
|
results := make(chan HTTPResult, configuration.concurrency)
|
||||||
|
done := make(chan bool, configuration.concurrency)
|
||||||
|
|
||||||
|
log.Println("Generating the requests...")
|
||||||
|
for r := 1; r <= configuration.numberOfRequests; r++ {
|
||||||
|
requests <- true
|
||||||
|
}
|
||||||
|
close(requests)
|
||||||
|
log.Println("Finished generating the requests")
|
||||||
|
|
||||||
|
preparedRunConfiguration := runConfiguration{
|
||||||
|
preLoadedRequestsMode,
|
||||||
|
timedMode,
|
||||||
|
preLoadedRequests,
|
||||||
|
client,
|
||||||
|
requests,
|
||||||
|
results,
|
||||||
|
done,
|
||||||
|
body,
|
||||||
|
}
|
||||||
|
|
||||||
|
return preparedRunConfiguration, nil
|
||||||
|
}
|
||||||
|
|||||||
29
configuration.go
Normal file
29
configuration.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Configuration represents the Baton configuration
|
||||||
|
type Configuration struct {
|
||||||
|
body string
|
||||||
|
concurrency int
|
||||||
|
dataFilePath string
|
||||||
|
duration int
|
||||||
|
ignoreTLS bool
|
||||||
|
method string
|
||||||
|
numberOfRequests int
|
||||||
|
requestsFromFile string
|
||||||
|
suppressOutput bool
|
||||||
|
url string
|
||||||
|
wait int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (configuration *Configuration) validate() error {
|
||||||
|
|
||||||
|
if configuration.concurrency < 1 || configuration.numberOfRequests == 0 {
|
||||||
|
return errors.New("invalid concurrency level or number of requests")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@ -14,7 +14,7 @@ func newCountWorker(requests <-chan bool, results chan<- HTTPResult, done chan<-
|
|||||||
return &countWorker{worker}
|
return &countWorker{worker}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (worker *countWorker) sendRequest(request preloadedRequest) {
|
func (worker *countWorker) sendRequest(request preLoadedRequest) {
|
||||||
req := fasthttp.AcquireRequest()
|
req := fasthttp.AcquireRequest()
|
||||||
req.SetRequestURI(request.url)
|
req.SetRequestURI(request.url)
|
||||||
req.Header.SetMethod(request.method)
|
req.Header.SetMethod(request.method)
|
||||||
@ -27,7 +27,7 @@ func (worker *countWorker) sendRequest(request preloadedRequest) {
|
|||||||
|
|
||||||
worker.finish()
|
worker.finish()
|
||||||
}
|
}
|
||||||
func (worker *countWorker) sendRequests(requests []preloadedRequest) {
|
func (worker *countWorker) sendRequests(requests []preLoadedRequest) {
|
||||||
totalPremadeRequests := len(requests)
|
totalPremadeRequests := len(requests)
|
||||||
|
|
||||||
for range worker.requests {
|
for range worker.requests {
|
||||||
|
|||||||
@ -17,7 +17,7 @@ func extractHeaders(rawHeaders string) []string {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func preloadRequestsFromFile(filename string) ([]preloadedRequest, error) {
|
func preLoadRequestsFromFile(filename string) ([]preLoadedRequest, error) {
|
||||||
file, err := os.Open(filename)
|
file, err := os.Open(filename)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -25,7 +25,7 @@ func preloadRequestsFromFile(filename string) ([]preloadedRequest, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
reader := csv.NewReader(bufio.NewReader(file))
|
reader := csv.NewReader(bufio.NewReader(file))
|
||||||
var requests []preloadedRequest
|
var requests []preLoadedRequest
|
||||||
|
|
||||||
for {
|
for {
|
||||||
record, err := reader.Read()
|
record, err := reader.Read()
|
||||||
@ -64,7 +64,7 @@ func preloadRequestsFromFile(filename string) ([]preloadedRequest, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
requests = append(requests, preloadedRequest{method, url, body, headers})
|
requests = append(requests, preLoadedRequest{method, url, body, headers})
|
||||||
}
|
}
|
||||||
|
|
||||||
return requests, nil
|
return requests, nil
|
||||||
|
|||||||
@ -16,7 +16,7 @@ func newTimedWorker(requests <-chan bool, results chan<- HTTPResult, done chan<-
|
|||||||
return &timedWorker{worker, durationToRun}
|
return &timedWorker{worker, durationToRun}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (worker timedWorker) sendRequest(request preloadedRequest) {
|
func (worker timedWorker) sendRequest(request preLoadedRequest) {
|
||||||
req := fasthttp.AcquireRequest()
|
req := fasthttp.AcquireRequest()
|
||||||
req.SetRequestURI(request.url)
|
req.SetRequestURI(request.url)
|
||||||
req.Header.SetMethod(request.method)
|
req.Header.SetMethod(request.method)
|
||||||
@ -35,7 +35,7 @@ func (worker timedWorker) sendRequest(request preloadedRequest) {
|
|||||||
worker.finish()
|
worker.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (worker timedWorker) sendRequests(requests []preloadedRequest) {
|
func (worker timedWorker) sendRequests(requests []preLoadedRequest) {
|
||||||
totalPremadeRequests := len(requests)
|
totalPremadeRequests := len(requests)
|
||||||
startTime := time.Now()
|
startTime := time.Now()
|
||||||
|
|
||||||
|
|||||||
@ -14,8 +14,8 @@ type worker struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type workable interface {
|
type workable interface {
|
||||||
sendRequests(requests []preloadedRequest)
|
sendRequests(requests []preLoadedRequest)
|
||||||
sendRequest(request preloadedRequest)
|
sendRequest(request preLoadedRequest)
|
||||||
setCustomClient(client *fasthttp.Client)
|
setCustomClient(client *fasthttp.Client)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,8 +50,8 @@ func (worker *worker) performRequest(req *fasthttp.Request, resp *fasthttp.Respo
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildRequest(requests []preloadedRequest, totalPremadeRequests int) (*fasthttp.Request, *fasthttp.Response) {
|
func buildRequest(requests []preLoadedRequest, totalPremadeRequests int) (*fasthttp.Request, *fasthttp.Response) {
|
||||||
var currentReq preloadedRequest
|
var currentReq preLoadedRequest
|
||||||
|
|
||||||
currentReq = requests[rand.Intn(totalPremadeRequests)]
|
currentReq = requests[rand.Intn(totalPremadeRequests)]
|
||||||
req := fasthttp.AcquireRequest()
|
req := fasthttp.AcquireRequest()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user